JS_IR: DCE
Could be enabled by toggling `-Xir-dce` Box test output in js/js.translator/testData/out-min
This commit is contained in:
+3
@@ -122,6 +122,9 @@ class K2JSCompilerArguments : CommonCompilerArguments() {
|
||||
@Argument(value = "-Xir-produce-js", description = "Generates JS file using IR backend. Also disables pre-IR backend")
|
||||
var irProduceJs: Boolean by FreezableVar(false)
|
||||
|
||||
@Argument(value = "-Xir-dce", description = "Perform experimental dead code elimination")
|
||||
var irDce: Boolean by FreezableVar(false)
|
||||
|
||||
@Argument(value = "-Xir-only", description = "Disables pre-IR backend")
|
||||
var irOnly: Boolean by FreezableVar(false)
|
||||
|
||||
|
||||
@@ -205,10 +205,13 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
phaseConfig,
|
||||
allDependencies = resolvedLibraries,
|
||||
friendDependencies = friendDependencies,
|
||||
mainArguments = mainCallArguments
|
||||
mainArguments = mainCallArguments,
|
||||
generateFullJs = !arguments.irDce,
|
||||
generateDceJs = arguments.irDce
|
||||
)
|
||||
|
||||
outputFile.writeText(compiledModule.jsCode)
|
||||
val jsCode = if (arguments.irDce) compiledModule.dceJsCode!! else compiledModule.jsCode!!
|
||||
outputFile.writeText(jsCode)
|
||||
if (arguments.generateDts) {
|
||||
val dtsFile = outputFile.withReplacedExtensionOrNull(outputFile.extension, "d.ts")!!
|
||||
dtsFile.writeText(compiledModule.tsDefinitions ?: error("No ts definitions"))
|
||||
|
||||
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js
|
||||
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.backend.js.export.isExported
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsName
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrFail
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.*
|
||||
import java.util.*
|
||||
|
||||
fun eliminateDeadDeclarations(
|
||||
module: IrModuleFragment,
|
||||
context: JsIrBackendContext,
|
||||
mainFunction: IrSimpleFunction?
|
||||
) {
|
||||
|
||||
val allRoots = buildRoots(module, context, mainFunction)
|
||||
|
||||
val usefulDeclarations = usefulDeclarations(allRoots, context)
|
||||
|
||||
removeUselessDeclarations(module, usefulDeclarations)
|
||||
}
|
||||
|
||||
private fun buildRoots(module: IrModuleFragment, context: JsIrBackendContext, mainFunction: IrSimpleFunction?): Iterable<IrDeclaration> {
|
||||
val rootDeclarations =
|
||||
(module.files + context.packageLevelJsModules + context.externalPackageFragment.values).flatMapTo(mutableListOf()) { file ->
|
||||
file.declarations.filter {
|
||||
it is IrField && it.initializer != null && it.fqNameWhenAvailable?.asString()?.startsWith("kotlin") != true
|
||||
|| it.isExported(context)
|
||||
|| it.isEffectivelyExternal()
|
||||
|| it is IrField && it.correspondingPropertySymbol?.owner?.isExported(context) == true
|
||||
|| it is IrSimpleFunction && it.correspondingPropertySymbol?.owner?.isExported(context) == true
|
||||
}
|
||||
}
|
||||
|
||||
if (context.hasTests) rootDeclarations += context.testContainer
|
||||
|
||||
if (mainFunction != null) {
|
||||
rootDeclarations += mainFunction
|
||||
if (mainFunction.isSuspend) {
|
||||
rootDeclarations += context.coroutineEmptyContinuation.owner
|
||||
}
|
||||
}
|
||||
|
||||
return rootDeclarations
|
||||
}
|
||||
|
||||
private fun removeUselessDeclarations(module: IrModuleFragment, usefulDeclarations: Set<IrDeclaration>) {
|
||||
module.files.forEach {
|
||||
it.acceptVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitFile(declaration: IrFile) {
|
||||
process(declaration)
|
||||
}
|
||||
|
||||
override fun visitClass(declaration: IrClass) {
|
||||
process(declaration)
|
||||
}
|
||||
|
||||
override fun visitConstructor(declaration: IrConstructor) {
|
||||
if (declaration !in usefulDeclarations) {
|
||||
// Keep the constructor declaration without body in order to declare the JS constructor function
|
||||
declaration.body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, emptyList())
|
||||
}
|
||||
}
|
||||
|
||||
private fun process(container: IrDeclarationContainer) {
|
||||
container.declarations.transformFlat { member ->
|
||||
if (member !in usefulDeclarations && member !is IrConstructor) {
|
||||
emptyList()
|
||||
} else {
|
||||
member.acceptVoid(this)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun Iterable<IrDeclaration>.withNested(): Iterable<IrDeclaration> {
|
||||
val result = mutableListOf<IrDeclaration>()
|
||||
|
||||
this.forEach {
|
||||
it.acceptVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitBody(body: IrBody) {
|
||||
// Skip
|
||||
}
|
||||
|
||||
override fun visitDeclaration(declaration: IrDeclaration) {
|
||||
super.visitDeclaration(declaration)
|
||||
result += declaration
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
fun usefulDeclarations(roots: Iterable<IrDeclaration>, context: JsIrBackendContext): Set<IrDeclaration> {
|
||||
val queue = ArrayDeque<IrDeclaration>()
|
||||
val result = hashSetOf<IrDeclaration>()
|
||||
val constructedClasses = hashSetOf<IrClass>()
|
||||
|
||||
fun IrDeclaration.enqueue() {
|
||||
if (this !in result) {
|
||||
result.add(this)
|
||||
queue.addLast(this)
|
||||
}
|
||||
}
|
||||
|
||||
// Add roots, including nested declarations
|
||||
roots.withNested().forEach { it.enqueue() }
|
||||
|
||||
val toStringMethod =
|
||||
context.irBuiltIns.anyClass.owner.declarations.filterIsInstance<IrFunction>().single { it.name.asString() == "toString" }
|
||||
val equalsMethod =
|
||||
context.irBuiltIns.anyClass.owner.declarations.filterIsInstance<IrFunction>().single { it.name.asString() == "equals" }
|
||||
val hashCodeMethod =
|
||||
context.irBuiltIns.anyClass.owner.declarations.filterIsInstance<IrFunction>().single { it.name.asString() == "hashCode" }
|
||||
|
||||
while (queue.isNotEmpty()) {
|
||||
while (queue.isNotEmpty()) {
|
||||
val declaration = queue.pollFirst()
|
||||
|
||||
if (declaration is IrClass) {
|
||||
declaration.superTypes.forEach {
|
||||
(it.classifierOrNull as? IrClassSymbol)?.owner?.enqueue()
|
||||
}
|
||||
|
||||
// Special hack for `IntrinsicsJs.kt` support
|
||||
if (declaration.superTypes.any { it.isSuspendFunctionTypeOrSubtype() }) {
|
||||
declaration.declarations.forEach {
|
||||
if (it is IrSimpleFunction && it.name.asString().startsWith("invoke")) {
|
||||
it.enqueue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO find out how `doResume` gets removed
|
||||
if (declaration.symbol == context.ir.symbols.coroutineImpl) {
|
||||
declaration.declarations.forEach {
|
||||
if (it is IrSimpleFunction && it.name.asString() == "doResume") {
|
||||
it.enqueue()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (declaration is IrSimpleFunction) {
|
||||
declaration.resolveFakeOverride()?.enqueue()
|
||||
}
|
||||
|
||||
// Collect instantiated classes.
|
||||
if (declaration is IrConstructor) {
|
||||
declaration.constructedClass.let {
|
||||
it.enqueue()
|
||||
constructedClasses += it
|
||||
}
|
||||
}
|
||||
|
||||
val body = when (declaration) {
|
||||
is IrFunction -> declaration.body
|
||||
is IrField -> declaration.initializer
|
||||
is IrVariable -> declaration.initializer
|
||||
else -> null
|
||||
}
|
||||
|
||||
body?.acceptVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression) {
|
||||
super.visitFunctionAccess(expression)
|
||||
|
||||
expression.symbol.owner.enqueue()
|
||||
}
|
||||
|
||||
override fun visitVariableAccess(expression: IrValueAccessExpression) {
|
||||
super.visitVariableAccess(expression)
|
||||
|
||||
expression.symbol.owner.enqueue()
|
||||
}
|
||||
|
||||
override fun visitFieldAccess(expression: IrFieldAccessExpression) {
|
||||
super.visitFieldAccess(expression)
|
||||
|
||||
expression.symbol.owner.enqueue()
|
||||
}
|
||||
|
||||
override fun visitCall(expression: IrCall) {
|
||||
super.visitCall(expression)
|
||||
|
||||
when (expression.symbol) {
|
||||
context.intrinsics.jsBoxIntrinsic -> {
|
||||
val inlineClass = expression.getTypeArgument(0)!!.getInlinedClass()!!
|
||||
val constructor = inlineClass.declarations.filterIsInstance<IrConstructor>().single { it.isPrimary }
|
||||
constructor.enqueue()
|
||||
}
|
||||
context.intrinsics.jsClass -> {
|
||||
(expression.getTypeArgument(0)!!.classifierOrFail.owner as IrDeclaration).enqueue()
|
||||
}
|
||||
context.intrinsics.jsObjectCreate.symbol -> {
|
||||
val classToCreate = expression.getTypeArgument(0)!!.classifierOrFail.owner as IrClass
|
||||
classToCreate.enqueue()
|
||||
constructedClasses += classToCreate
|
||||
}
|
||||
context.intrinsics.jsEquals -> {
|
||||
equalsMethod.enqueue()
|
||||
}
|
||||
context.intrinsics.jsToString -> {
|
||||
toStringMethod.enqueue()
|
||||
}
|
||||
context.intrinsics.jsHashCode -> {
|
||||
hashCodeMethod.enqueue()
|
||||
}
|
||||
context.intrinsics.jsPlus -> {
|
||||
if (expression.getValueArgument(0)?.type?.classOrNull == context.irBuiltIns.stringClass) {
|
||||
toStringMethod.enqueue()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitStringConcatenation(expression: IrStringConcatenation) {
|
||||
super.visitStringConcatenation(expression)
|
||||
|
||||
toStringMethod.enqueue()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun IrOverridableDeclaration<*>.overridesUsefulFunction(): Boolean {
|
||||
return this.overriddenSymbols.any {
|
||||
(it.owner as? IrOverridableDeclaration<*>)?.let {
|
||||
it in result || it.overridesUsefulFunction()
|
||||
} ?: false
|
||||
}
|
||||
}
|
||||
|
||||
for (klass in constructedClasses) {
|
||||
for (declaration in klass.declarations) {
|
||||
if (declaration in result) continue
|
||||
|
||||
if (declaration is IrOverridableDeclaration<*> && declaration.overridesUsefulFunction()) {
|
||||
declaration.enqueue()
|
||||
}
|
||||
|
||||
if (declaration is IrSimpleFunction && declaration.getJsNameOrKotlinName().asString() == "valueOf") {
|
||||
declaration.enqueue()
|
||||
}
|
||||
|
||||
// A hack to support `toJson` and other js-specific members
|
||||
if (declaration.getJsName() != null ||
|
||||
declaration is IrField && declaration.correspondingPropertySymbol?.owner?.getJsName() != null ||
|
||||
declaration is IrSimpleFunction && declaration.correspondingPropertySymbol?.owner?.getJsName() != null
|
||||
) {
|
||||
declaration.enqueue()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -9,12 +9,19 @@ import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
|
||||
import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.ir.backend.js.export.isExported
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.moveBodilessDeclarationsToSeparatePlace
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsMainFunctionDetector
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.NameTables
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.isJsExport
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
|
||||
import org.jetbrains.kotlin.ir.declarations.IrField
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
||||
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
|
||||
import org.jetbrains.kotlin.library.KotlinLibrary
|
||||
import org.jetbrains.kotlin.library.resolver.KotlinLibraryResolveResult
|
||||
@@ -32,7 +39,8 @@ fun sortDependencies(dependencies: Collection<IrModuleFragment>): Collection<IrM
|
||||
}
|
||||
|
||||
class CompilerResult(
|
||||
val jsCode: String,
|
||||
val jsCode: String?,
|
||||
val dceJsCode: String?,
|
||||
val tsDefinitions: String? = null
|
||||
)
|
||||
|
||||
@@ -44,7 +52,9 @@ fun compile(
|
||||
allDependencies: KotlinLibraryResolveResult,
|
||||
friendDependencies: List<KotlinLibrary>,
|
||||
mainArguments: List<String>?,
|
||||
exportedDeclarations: Set<FqName> = emptySet()
|
||||
exportedDeclarations: Set<FqName> = emptySet(),
|
||||
generateFullJs: Boolean = true,
|
||||
generateDceJs: Boolean = false
|
||||
): CompilerResult {
|
||||
val (moduleFragment, dependencyModules, irBuiltIns, symbolTable, deserializer) =
|
||||
loadIr(project, files, configuration, allDependencies, friendDependencies)
|
||||
@@ -74,7 +84,7 @@ fun compile(
|
||||
jsPhases.invokeToplevel(phaseConfig, context, moduleFragment)
|
||||
|
||||
val transformer = IrModuleToJsTransformer(context, mainFunction, mainArguments)
|
||||
return transformer.generateModule(moduleFragment)
|
||||
return transformer.generateModule(moduleFragment, generateFullJs, generateDceJs)
|
||||
}
|
||||
|
||||
fun generateJsCode(
|
||||
@@ -86,5 +96,5 @@ fun generateJsCode(
|
||||
jsPhases.invokeToplevel(PhaseConfig(jsPhases), context, moduleFragment)
|
||||
|
||||
val transformer = IrModuleToJsTransformer(context, null, null, true, nameTables)
|
||||
return transformer.generateModule(moduleFragment).jsCode
|
||||
return transformer.generateModule(moduleFragment).jsCode!!
|
||||
}
|
||||
|
||||
+47
-42
@@ -45,35 +45,9 @@ class ExportModelGenerator(val context: JsIrBackendContext) {
|
||||
}
|
||||
)
|
||||
|
||||
private fun getExportCandidate(declaration: IrDeclaration): IrDeclarationWithName? {
|
||||
// Only actual public declarations with name can be exported
|
||||
if (declaration !is IrDeclarationWithVisibility ||
|
||||
declaration !is IrDeclarationWithName ||
|
||||
declaration.visibility != Visibilities.PUBLIC ||
|
||||
declaration.isExpect
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Workaround to get property declarations instead of its lowered accessors.
|
||||
if (declaration is IrSimpleFunction) {
|
||||
val property = declaration.correspondingPropertySymbol?.owner
|
||||
if (property != null) {
|
||||
// Return property for getter accessors only to prevent
|
||||
// returning it twice (for getter and setter) in the same scope
|
||||
return if (property.getter == declaration)
|
||||
property
|
||||
else
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
return declaration
|
||||
}
|
||||
|
||||
private fun exportDeclaration(declaration: IrDeclaration): ExportedDeclaration? {
|
||||
val candidate = getExportCandidate(declaration) ?: return null
|
||||
if (!shouldDeclarationBeExported(candidate)) return null
|
||||
if (!shouldDeclarationBeExported(candidate, context)) return null
|
||||
|
||||
return when (candidate) {
|
||||
is IrSimpleFunction -> exportFunction(candidate)
|
||||
@@ -173,7 +147,7 @@ class ExportModelGenerator(val context: JsIrBackendContext) {
|
||||
|
||||
for (declaration in klass.declarations) {
|
||||
val candidate = getExportCandidate(declaration) ?: continue
|
||||
if (!shouldDeclarationBeExported(candidate)) continue
|
||||
if (!shouldDeclarationBeExported(candidate, context)) continue
|
||||
|
||||
when (candidate) {
|
||||
is IrSimpleFunction ->
|
||||
@@ -302,20 +276,6 @@ class ExportModelGenerator(val context: JsIrBackendContext) {
|
||||
else identifier
|
||||
}
|
||||
|
||||
private fun shouldDeclarationBeExported(declaration: IrDeclarationWithName): Boolean {
|
||||
if (declaration.fqNameWhenAvailable in context.additionalExportedDeclarations)
|
||||
return true
|
||||
|
||||
if (declaration.isJsExport())
|
||||
return true
|
||||
|
||||
return when (val parent = declaration.parent) {
|
||||
is IrDeclarationWithName -> shouldDeclarationBeExported(parent)
|
||||
is IrAnnotationContainer -> parent.isJsExport()
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun functionExportability(function: IrSimpleFunction): Exportability {
|
||||
if (function.isInline && function.typeParameters.any { it.isReified })
|
||||
return Exportability.Prohibited("Inline reified function")
|
||||
@@ -353,6 +313,51 @@ sealed class Exportability {
|
||||
private val IrClassifierSymbol.isInterface
|
||||
get() = (owner as? IrClass)?.isInterface == true
|
||||
|
||||
private fun getExportCandidate(declaration: IrDeclaration): IrDeclarationWithName? {
|
||||
// Only actual public declarations with name can be exported
|
||||
if (declaration !is IrDeclarationWithVisibility ||
|
||||
declaration !is IrDeclarationWithName ||
|
||||
declaration.visibility != Visibilities.PUBLIC ||
|
||||
declaration.isExpect
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Workaround to get property declarations instead of its lowered accessors.
|
||||
if (declaration is IrSimpleFunction) {
|
||||
val property = declaration.correspondingPropertySymbol?.owner
|
||||
if (property != null) {
|
||||
// Return property for getter accessors only to prevent
|
||||
// returning it twice (for getter and setter) in the same scope
|
||||
return if (property.getter == declaration)
|
||||
property
|
||||
else
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
return declaration
|
||||
}
|
||||
|
||||
private fun shouldDeclarationBeExported(declaration: IrDeclarationWithName, context: JsIrBackendContext): Boolean {
|
||||
if (declaration.fqNameWhenAvailable in context.additionalExportedDeclarations)
|
||||
return true
|
||||
|
||||
if (declaration.isJsExport())
|
||||
return true
|
||||
|
||||
return when (val parent = declaration.parent) {
|
||||
is IrDeclarationWithName -> shouldDeclarationBeExported(parent, context)
|
||||
is IrAnnotationContainer -> parent.isJsExport()
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
fun IrDeclaration.isExported(context: JsIrBackendContext): Boolean {
|
||||
val candidate = getExportCandidate(this) ?: return false
|
||||
return shouldDeclarationBeExported(candidate, context)
|
||||
}
|
||||
|
||||
private val reservedWords = setOf(
|
||||
"break",
|
||||
"case",
|
||||
|
||||
+1
@@ -138,6 +138,7 @@ class BridgesConstruction(val context: CommonBackendContext) : ClassLoweringPass
|
||||
valueParameters += bridge.valueParameters.map { p -> p.copyTo(this) }
|
||||
annotations += bridge.annotations
|
||||
overriddenSymbols.addAll(delegateTo.overriddenSymbols)
|
||||
overriddenSymbols.add(bridge.symbol)
|
||||
}
|
||||
|
||||
context.createIrBuilder(irFunction.symbol).irBlockBody(irFunction) {
|
||||
|
||||
+58
-44
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.ir.backend.js.lower.calls
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
@@ -172,57 +173,70 @@ class NumberOperatorCallsTransformer(context: JsIrBackendContext) : CallsTransfo
|
||||
else -> e
|
||||
}
|
||||
|
||||
private fun withLongCoercion(default: (IrFunctionAccessExpression) -> IrExpression): (IrFunctionAccessExpression) -> IrExpression = { call ->
|
||||
assert(call.valueArgumentsCount == 1)
|
||||
val arg = call.getValueArgument(0)!!
|
||||
private fun withLongCoercion(default: (IrFunctionAccessExpression) -> IrExpression): (IrFunctionAccessExpression) -> IrExpression =
|
||||
{ call ->
|
||||
assert(call.valueArgumentsCount == 1)
|
||||
val arg = call.getValueArgument(0)!!
|
||||
|
||||
if (arg.type.isLong()) {
|
||||
val receiverType = call.dispatchReceiver!!.type
|
||||
var actualCall = call
|
||||
|
||||
when {
|
||||
// Double OP Long => Double OP Long.toDouble()
|
||||
receiverType.isDouble() -> {
|
||||
call.putValueArgument(0, IrCallImpl(
|
||||
call.startOffset,
|
||||
call.endOffset,
|
||||
intrinsics.longToDouble.owner.returnType,
|
||||
intrinsics.longToDouble
|
||||
).apply {
|
||||
dispatchReceiver = arg
|
||||
})
|
||||
}
|
||||
// Float OP Long => Float OP Long.toFloat()
|
||||
receiverType.isFloat() -> {
|
||||
call.putValueArgument(0, IrCallImpl(
|
||||
call.startOffset,
|
||||
call.endOffset,
|
||||
intrinsics.longToFloat.owner.returnType,
|
||||
intrinsics.longToFloat
|
||||
).apply {
|
||||
dispatchReceiver = arg
|
||||
})
|
||||
}
|
||||
// {Byte, Short, Int} OP Long => {Byte, Sort, Int}.toLong() OP Long
|
||||
!receiverType.isLong() -> {
|
||||
call.dispatchReceiver = IrCallImpl(
|
||||
call.startOffset,
|
||||
call.endOffset,
|
||||
intrinsics.jsNumberToLong.owner.returnType,
|
||||
intrinsics.jsNumberToLong
|
||||
).apply {
|
||||
putValueArgument(0, call.dispatchReceiver)
|
||||
if (arg.type.isLong()) {
|
||||
val receiverType = call.dispatchReceiver!!.type
|
||||
|
||||
when {
|
||||
// Double OP Long => Double OP Long.toDouble()
|
||||
receiverType.isDouble() -> {
|
||||
call.putValueArgument(0, IrCallImpl(
|
||||
call.startOffset,
|
||||
call.endOffset,
|
||||
intrinsics.longToDouble.owner.returnType,
|
||||
intrinsics.longToDouble
|
||||
).apply {
|
||||
dispatchReceiver = arg
|
||||
})
|
||||
}
|
||||
// Float OP Long => Float OP Long.toFloat()
|
||||
receiverType.isFloat() -> {
|
||||
call.putValueArgument(0, IrCallImpl(
|
||||
call.startOffset,
|
||||
call.endOffset,
|
||||
intrinsics.longToFloat.owner.returnType,
|
||||
intrinsics.longToFloat
|
||||
).apply {
|
||||
dispatchReceiver = arg
|
||||
})
|
||||
}
|
||||
// {Byte, Short, Int} OP Long => {Byte, Sort, Int}.toLong() OP Long
|
||||
!receiverType.isLong() -> {
|
||||
call.dispatchReceiver = IrCallImpl(
|
||||
call.startOffset,
|
||||
call.endOffset,
|
||||
intrinsics.jsNumberToLong.owner.returnType,
|
||||
intrinsics.jsNumberToLong
|
||||
).apply {
|
||||
putValueArgument(0, call.dispatchReceiver)
|
||||
}
|
||||
|
||||
// Replace {Byte, Short, Int}.OP with corresponding Long.OP
|
||||
val declaration = call.symbol.owner as IrSimpleFunction
|
||||
val replacement = intrinsics.longClassSymbol.owner.declarations.filterIsInstance<IrSimpleFunction>()
|
||||
.single { member ->
|
||||
member.name.asString() == declaration.name.asString() &&
|
||||
member.valueParameters.size == declaration.valueParameters.size &&
|
||||
member.valueParameters.zip(declaration.valueParameters).all { (a, b) -> a.type == b.type }
|
||||
}.symbol
|
||||
|
||||
actualCall = irCall(call, replacement)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (call.dispatchReceiver!!.type.isLong()) {
|
||||
// LHS is Long => use as is
|
||||
call
|
||||
} else {
|
||||
default(call)
|
||||
if (actualCall.dispatchReceiver!!.type.isLong()) {
|
||||
actualCall
|
||||
} else {
|
||||
default(actualCall)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun IrFunctionSymbol.call(vararg arguments: IrExpression) =
|
||||
JsIrBuilder.buildCall(this, owner.returnType).apply {
|
||||
|
||||
+71
-58
@@ -8,8 +8,10 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.ir.backend.js.CompilerResult
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.eliminateDeadDeclarations
|
||||
import org.jetbrains.kotlin.ir.backend.js.export.ExportModelGenerator
|
||||
import org.jetbrains.kotlin.ir.backend.js.export.ExportModelToJsStatements
|
||||
import org.jetbrains.kotlin.ir.backend.js.export.ExportedModule
|
||||
import org.jetbrains.kotlin.ir.backend.js.export.toTypeScript
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.StaticMembersLowering
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
@@ -33,63 +35,7 @@ class IrModuleToJsTransformer(
|
||||
private val moduleKind = backendContext.configuration[JSConfigurationKeys.MODULE_KIND]!!
|
||||
private val generateRegionComments = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_REGION_COMMENTS)
|
||||
|
||||
private fun generateModuleBody(module: IrModuleFragment, context: JsGenerationContext): List<JsStatement> {
|
||||
val statements = mutableListOf<JsStatement>().also {
|
||||
if (!generateScriptModule) it += JsStringLiteral("use strict").makeStmt()
|
||||
}
|
||||
|
||||
val preDeclarationBlock = JsGlobalBlock()
|
||||
val postDeclarationBlock = JsGlobalBlock()
|
||||
|
||||
statements.addWithComment("block: pre-declaration", preDeclarationBlock)
|
||||
|
||||
val generateFilePaths = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_COMMENTS_WITH_FILE_PATH)
|
||||
val pathPrefixMap = backendContext.configuration.getMap(JSConfigurationKeys.FILE_PATHS_PREFIX_MAP)
|
||||
|
||||
module.files.forEach {
|
||||
val fileStatements = it.accept(IrFileToJsTransformer(), context).statements
|
||||
if (fileStatements.isNotEmpty()) {
|
||||
var startComment = ""
|
||||
|
||||
if (generateRegionComments) {
|
||||
startComment = "region "
|
||||
}
|
||||
|
||||
if (generateRegionComments || generateFilePaths) {
|
||||
val originalPath = it.path
|
||||
val path = pathPrefixMap.entries
|
||||
.find { (k, _) -> originalPath.startsWith(k) }
|
||||
?.let { (k, v) -> v + originalPath.substring(k.length) }
|
||||
?: originalPath
|
||||
|
||||
startComment += "file: $path"
|
||||
}
|
||||
|
||||
if (startComment.isNotEmpty()) {
|
||||
statements.add(JsSingleLineComment(startComment))
|
||||
}
|
||||
|
||||
statements.addAll(fileStatements)
|
||||
statements.endRegion()
|
||||
}
|
||||
}
|
||||
|
||||
// sort member forwarding code
|
||||
processClassModels(context.staticContext.classModels, preDeclarationBlock, postDeclarationBlock)
|
||||
|
||||
statements.addWithComment("block: post-declaration", postDeclarationBlock.statements)
|
||||
statements.addWithComment("block: init", context.staticContext.initializerBlock.statements)
|
||||
|
||||
if (backendContext.hasTests) {
|
||||
statements.startRegion("block: tests")
|
||||
statements += JsInvocation(context.getNameForStaticFunction(backendContext.testContainer).makeRef()).makeStmt()
|
||||
statements.endRegion()
|
||||
}
|
||||
|
||||
return statements
|
||||
}
|
||||
|
||||
fun generateModule(module: IrModuleFragment): CompilerResult {
|
||||
fun generateModule(module: IrModuleFragment, fullJs: Boolean = true, dceJs: Boolean = false): CompilerResult {
|
||||
val additionalPackages = with(backendContext) {
|
||||
externalPackageFragment.values + listOf(
|
||||
bodilessBuiltInsPackageFragment,
|
||||
@@ -104,6 +50,17 @@ class IrModuleToJsTransformer(
|
||||
|
||||
namer.merge(module.files, additionalPackages)
|
||||
|
||||
val jsCode = if (fullJs) generateWrappedModuleBody(module, exportedModule) else null
|
||||
|
||||
val dceJsCode = if (dceJs) {
|
||||
eliminateDeadDeclarations(module, backendContext, mainFunction)
|
||||
generateWrappedModuleBody(module, exportedModule)
|
||||
} else null
|
||||
|
||||
return CompilerResult(jsCode, dceJsCode, dts)
|
||||
}
|
||||
|
||||
private fun generateWrappedModuleBody(module: IrModuleFragment, exportedModule: ExportedModule): String {
|
||||
val program = JsProgram()
|
||||
|
||||
val nameGenerator = IrNamerImpl(
|
||||
@@ -159,7 +116,63 @@ class IrModuleToJsTransformer(
|
||||
)
|
||||
}
|
||||
|
||||
return CompilerResult(program.toString(), dts)
|
||||
return program.toString()
|
||||
}
|
||||
|
||||
private fun generateModuleBody(module: IrModuleFragment, context: JsGenerationContext): List<JsStatement> {
|
||||
val statements = mutableListOf<JsStatement>().also {
|
||||
if (!generateScriptModule) it += JsStringLiteral("use strict").makeStmt()
|
||||
}
|
||||
|
||||
val preDeclarationBlock = JsGlobalBlock()
|
||||
val postDeclarationBlock = JsGlobalBlock()
|
||||
|
||||
statements.addWithComment("block: pre-declaration", preDeclarationBlock)
|
||||
|
||||
val generateFilePaths = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_COMMENTS_WITH_FILE_PATH)
|
||||
val pathPrefixMap = backendContext.configuration.getMap(JSConfigurationKeys.FILE_PATHS_PREFIX_MAP)
|
||||
|
||||
module.files.forEach {
|
||||
val fileStatements = it.accept(IrFileToJsTransformer(), context).statements
|
||||
if (fileStatements.isNotEmpty()) {
|
||||
var startComment = ""
|
||||
|
||||
if (generateRegionComments) {
|
||||
startComment = "region "
|
||||
}
|
||||
|
||||
if (generateRegionComments || generateFilePaths) {
|
||||
val originalPath = it.path
|
||||
val path = pathPrefixMap.entries
|
||||
.find { (k, _) -> originalPath.startsWith(k) }
|
||||
?.let { (k, v) -> v + originalPath.substring(k.length) }
|
||||
?: originalPath
|
||||
|
||||
startComment += "file: $path"
|
||||
}
|
||||
|
||||
if (startComment.isNotEmpty()) {
|
||||
statements.add(JsSingleLineComment(startComment))
|
||||
}
|
||||
|
||||
statements.addAll(fileStatements)
|
||||
statements.endRegion()
|
||||
}
|
||||
}
|
||||
|
||||
// sort member forwarding code
|
||||
processClassModels(context.staticContext.classModels, preDeclarationBlock, postDeclarationBlock)
|
||||
|
||||
statements.addWithComment("block: post-declaration", postDeclarationBlock.statements)
|
||||
statements.addWithComment("block: init", context.staticContext.initializerBlock.statements)
|
||||
|
||||
if (backendContext.hasTests) {
|
||||
statements.startRegion("block: tests")
|
||||
statements += JsInvocation(context.getNameForStaticFunction(backendContext.testContainer).makeRef()).makeStmt()
|
||||
statements.endRegion()
|
||||
}
|
||||
|
||||
return statements
|
||||
}
|
||||
|
||||
private fun generateMainArguments(mainFunction: IrSimpleFunction, rootContext: JsGenerationContext): List<JsExpression> {
|
||||
|
||||
+22
-13
@@ -6,10 +6,8 @@
|
||||
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
|
||||
import org.jetbrains.kotlin.ir.backend.js.export.isExported
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
|
||||
@@ -84,22 +82,33 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
||||
)
|
||||
}
|
||||
|
||||
val getterRef = property.getter?.accessorRef()
|
||||
val setterRef = property.setter?.accessorRef()
|
||||
classBlock.statements += JsExpressionStatement(
|
||||
defineProperty(
|
||||
classPrototypeRef,
|
||||
context.getNameForProperty(property).ident,
|
||||
getter = getterRef,
|
||||
setter = setterRef
|
||||
if (irClass.isExported(context.staticContext.backendContext) ||
|
||||
property.getter?.overridesExternal() == true ||
|
||||
property.getJsName() != null
|
||||
) {
|
||||
val getterRef = property.getter?.accessorRef()
|
||||
val setterRef = property.setter?.accessorRef()
|
||||
classBlock.statements += JsExpressionStatement(
|
||||
defineProperty(
|
||||
classPrototypeRef,
|
||||
context.getNameForProperty(property).ident,
|
||||
getter = getterRef,
|
||||
setter = setterRef
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
context.staticContext.classModels[irClass.symbol] = classModel
|
||||
return classBlock
|
||||
}
|
||||
|
||||
private fun IrSimpleFunction.overridesExternal(): Boolean {
|
||||
if (this.isEffectivelyExternal()) return true
|
||||
|
||||
return this.overriddenSymbols.any { it.owner.overridesExternal() }
|
||||
}
|
||||
|
||||
private fun generateMemberFunction(declaration: IrSimpleFunction): JsStatement? {
|
||||
|
||||
val translatedFunction = declaration.run { if (isReal) accept(IrFunctionToJsTransformer(), context) else null }
|
||||
|
||||
+1
@@ -4,6 +4,7 @@ where advanced options include:
|
||||
-Xfriend-modules=<path> Paths to friend modules
|
||||
-Xfriend-modules-disabled Disable internal declaration export
|
||||
-Xgenerate-dts Generate TypeScript declarations .d.ts file alongside JS file. Available in IR backend only.
|
||||
-Xir-dce Perform experimental dead code elimination
|
||||
-Xir-only Disables pre-IR backend
|
||||
-Xir-produce-js Generates JS file using IR backend. Also disables pre-IR backend
|
||||
-Xir-produce-klib-dir Generate unpacked KLIB into parent directory of output JS file.
|
||||
|
||||
@@ -87,6 +87,7 @@ abstract class BasicBoxTest(
|
||||
|
||||
protected open val runMinifierByDefault: Boolean = false
|
||||
protected open val skipMinification = getBoolean("kotlin.js.skipMinificationTest")
|
||||
protected open val runIrDce: Boolean = false
|
||||
|
||||
protected open val incrementalCompilationChecksEnabled = true
|
||||
|
||||
@@ -103,6 +104,7 @@ abstract class BasicBoxTest(
|
||||
open fun doTest(filePath: String, expectedResult: String, mainCallParameters: MainCallParameters, coroutinesPackage: String = "") {
|
||||
val file = File(filePath)
|
||||
val outputDir = getOutputDir(file)
|
||||
val dceOutputDir = getOutputDir(file, testGroupOutputDirForMinification)
|
||||
var fileContent = KotlinTestUtils.doLoadFile(file)
|
||||
if (coroutinesPackage.isNotEmpty()) {
|
||||
fileContent = fileContent.replace("COROUTINES_PACKAGE", coroutinesPackage)
|
||||
@@ -154,10 +156,11 @@ abstract class BasicBoxTest(
|
||||
val friends = module.friends.map { modules[it]?.outputFileName(outputDir) + ".meta.js" }
|
||||
|
||||
val outputFileName = module.outputFileName(outputDir) + ".js"
|
||||
val dceOutputFileName = module.outputFileName(dceOutputDir) + ".js"
|
||||
val isMainModule = mainModuleName == module.name
|
||||
generateJavaScriptFile(
|
||||
testFactory.tmpDir,
|
||||
file.parent, module, outputFileName, dependencies, allDependencies, friends, modules.size > 1,
|
||||
file.parent, module, outputFileName, dceOutputFileName, dependencies, allDependencies, friends, modules.size > 1,
|
||||
!SKIP_SOURCEMAP_REMAPPING.matcher(fileContent).find(), outputPrefixFile, outputPostfixFile,
|
||||
actualMainCallParameters, testPackage, testFunction, needsFullIrRuntime, isMainModule
|
||||
)
|
||||
@@ -212,6 +215,9 @@ abstract class BasicBoxTest(
|
||||
val allJsFiles = additionalFiles + inputJsFiles + generatedJsFiles.map { it.first } + globalCommonFiles + localCommonFiles +
|
||||
additionalCommonFiles + additionalMainFiles
|
||||
|
||||
val dceAllJsFiles = additionalFiles + inputJsFiles + generatedJsFiles.map { it.first.replace(outputDir.absolutePath, dceOutputDir.absolutePath) } +
|
||||
globalCommonFiles + localCommonFiles + additionalCommonFiles + additionalMainFiles
|
||||
|
||||
val dontRunGeneratedCode = InTextDirectivesUtils.dontRunGeneratedCode(targetBackend, file)
|
||||
|
||||
if (!dontRunGeneratedCode && generateNodeJsRunner && !SKIP_NODE_JS.matcher(fileContent).find()) {
|
||||
@@ -223,6 +229,10 @@ abstract class BasicBoxTest(
|
||||
|
||||
if (!dontRunGeneratedCode) {
|
||||
runGeneratedCode(allJsFiles, testModuleName, testPackage, testFunction, expectedResult, withModuleSystem)
|
||||
|
||||
if (runIrDce) {
|
||||
runGeneratedCode(dceAllJsFiles, testModuleName, testPackage, testFunction, expectedResult, withModuleSystem)
|
||||
}
|
||||
}
|
||||
|
||||
performAdditionalChecks(generatedJsFiles.map { it.first }, outputPrefixFile, outputPostfixFile)
|
||||
@@ -340,6 +350,7 @@ abstract class BasicBoxTest(
|
||||
directory: String,
|
||||
module: TestModule,
|
||||
outputFileName: String,
|
||||
dceOutputFileName: String,
|
||||
dependencies: List<String>,
|
||||
allDependencies: List<String>,
|
||||
friends: List<String>,
|
||||
@@ -369,10 +380,11 @@ abstract class BasicBoxTest(
|
||||
val sourceDirs = (testFiles + additionalFiles).map { File(it).parent }.distinct()
|
||||
val config = createConfig(sourceDirs, module, dependencies, allDependencies, friends, multiModule, tmpDir, incrementalData = null)
|
||||
val outputFile = File(outputFileName)
|
||||
val dceOutputFile = File(dceOutputFileName)
|
||||
|
||||
val incrementalData = IncrementalData()
|
||||
translateFiles(
|
||||
psiFiles.map(TranslationUnit::SourceFile), outputFile, config, outputPrefixFile, outputPostfixFile,
|
||||
psiFiles.map(TranslationUnit::SourceFile), outputFile, dceOutputFile, config, outputPrefixFile, outputPostfixFile,
|
||||
mainCallParameters, incrementalData, remap, testPackage, testFunction, needsFullIrRuntime, isMainModule
|
||||
)
|
||||
|
||||
@@ -423,7 +435,7 @@ abstract class BasicBoxTest(
|
||||
val recompiledOutputFile = File(outputFile.parentFile, outputFile.nameWithoutExtension + "-recompiled.js")
|
||||
|
||||
translateFiles(
|
||||
translationUnits, recompiledOutputFile, recompiledConfig, outputPrefixFile, outputPostfixFile,
|
||||
translationUnits, recompiledOutputFile, recompiledOutputFile, recompiledConfig, outputPrefixFile, outputPostfixFile,
|
||||
mainCallParameters, incrementalData, remap, testPackage, testFunction, needsFullIrRuntime, false
|
||||
)
|
||||
|
||||
@@ -485,6 +497,7 @@ abstract class BasicBoxTest(
|
||||
protected open fun translateFiles(
|
||||
units: List<TranslationUnit>,
|
||||
outputFile: File,
|
||||
dceOutputFile: File,
|
||||
config: JsConfig,
|
||||
outputPrefixFile: File?,
|
||||
outputPostfixFile: File?,
|
||||
|
||||
@@ -46,6 +46,8 @@ abstract class BasicIrBoxTest(
|
||||
|
||||
override val skipMinification = true
|
||||
|
||||
override val runIrDce: Boolean = true
|
||||
|
||||
// TODO Design incremental compilation for IR and add test support
|
||||
override val incrementalCompilationChecksEnabled = false
|
||||
|
||||
@@ -62,6 +64,7 @@ abstract class BasicIrBoxTest(
|
||||
override fun translateFiles(
|
||||
units: List<TranslationUnit>,
|
||||
outputFile: File,
|
||||
dceOutputFile: File,
|
||||
config: JsConfig,
|
||||
outputPrefixFile: File?,
|
||||
outputPostfixFile: File?,
|
||||
@@ -118,12 +121,19 @@ abstract class BasicIrBoxTest(
|
||||
allDependencies = resolvedLibraries,
|
||||
friendDependencies = emptyList(),
|
||||
mainArguments = mainCallParameters.run { if (shouldBeGenerated()) arguments() else null },
|
||||
exportedDeclarations = setOf(FqName.fromSegments(listOfNotNull(testPackage, testFunction)))
|
||||
exportedDeclarations = setOf(FqName.fromSegments(listOfNotNull(testPackage, testFunction))),
|
||||
generateFullJs = true,
|
||||
generateDceJs = runIrDce
|
||||
)
|
||||
|
||||
val wrappedCode = wrapWithModuleEmulationMarkers(compiledModule.jsCode, moduleId = config.moduleId, moduleKind = config.moduleKind)
|
||||
val wrappedCode = wrapWithModuleEmulationMarkers(compiledModule.jsCode!!, moduleId = config.moduleId, moduleKind = config.moduleKind)
|
||||
outputFile.write(wrappedCode)
|
||||
|
||||
compiledModule.dceJsCode?.let { dceJsCode ->
|
||||
val dceWrappedCode = wrapWithModuleEmulationMarkers(dceJsCode, moduleId = config.moduleId, moduleKind = config.moduleKind)
|
||||
dceOutputFile.write(dceWrappedCode)
|
||||
}
|
||||
|
||||
if (generateDts) {
|
||||
val dtsFile = outputFile.withReplacedExtensionOrNull("_v5.js", ".d.ts")
|
||||
dtsFile?.write(compiledModule.tsDefinitions ?: error("No ts definitions"))
|
||||
|
||||
+1
-1
@@ -3,5 +3,5 @@ package kotlin
|
||||
// see StdLibTestBase.removeAdHocAssertions
|
||||
|
||||
fun fail(message: String? = null): Nothing {
|
||||
throw Exception(message)
|
||||
throw Throwable(message)
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1276
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
|
||||
@JsExport
|
||||
class A(val x: Char)
|
||||
|
||||
fun typeOf(x: dynamic): String = js("typeof x")
|
||||
|
||||
@@ -4,6 +4,7 @@ interface I {
|
||||
val a: Char
|
||||
}
|
||||
|
||||
@JsExport
|
||||
object X : I {
|
||||
override var a = '#'
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1289
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
|
||||
@JsExport
|
||||
open class A {
|
||||
val foo: Char
|
||||
get() = 'X'
|
||||
|
||||
@@ -3,6 +3,7 @@ package foo
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
@JsExport
|
||||
class A {
|
||||
@JsName("xx") val x: Int by B(23)
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1290
|
||||
|
||||
@JsExport
|
||||
object A {
|
||||
@JsName("js_method") fun f() = "method"
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1291
|
||||
|
||||
@JsExport
|
||||
object A {
|
||||
@JsName("js_f") fun f(x: Int) = "f($x)"
|
||||
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ fun box(): String {
|
||||
assertEquals(a.foo, undefined)
|
||||
assertNotEquals(a.toString, undefined)
|
||||
|
||||
val b: dynamic = object {val bar = ""}
|
||||
val b: dynamic = object {@JsName("bar") val bar = ""}
|
||||
assertEquals(b.foo, undefined)
|
||||
assertNotEquals(b.bar, undefined)
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ fun deinitialize(foo: dynamic) {
|
||||
}
|
||||
|
||||
class Foo {
|
||||
@JsName("bar")
|
||||
lateinit var bar: String
|
||||
|
||||
fun test(): String {
|
||||
|
||||
@@ -55,7 +55,7 @@ fun hashCode(obj: dynamic): Int {
|
||||
}
|
||||
}
|
||||
|
||||
private var POW_2_32 = 4294967296
|
||||
private var POW_2_32 = 4294967296.0
|
||||
private var OBJECT_HASH_CODE_PROPERTY_NAME = "kotlinHashCodeValue$"
|
||||
|
||||
fun getObjectHashCode(obj: dynamic): Int {
|
||||
@@ -83,7 +83,9 @@ fun identityHashCode(obj: Any?): Int = getObjectHashCode(obj)
|
||||
|
||||
internal fun captureStack(instance: Throwable) {
|
||||
if (js("Error").captureStackTrace != null) {
|
||||
js("Error").captureStackTrace(instance, instance::class.js)
|
||||
// TODO Why we generated get kclass for throwable in original code?
|
||||
js("Error").captureStackTrace(instance, instance.asDynamic().constructor)
|
||||
// js("Error").captureStackTrace(instance, instance::class.js)
|
||||
} else {
|
||||
instance.asDynamic().stack = js("new Error()").stack
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ internal open class BufferedOutput : BaseOutput() {
|
||||
internal class BufferedOutputToConsoleLog : BufferedOutput() {
|
||||
override fun print(message: Any?) {
|
||||
var s = String(message)
|
||||
val i = s.lastIndexOf('\n')
|
||||
val i = s.nativeLastIndexOf("\n", 0)
|
||||
if (i >= 0) {
|
||||
buffer += s.substring(0, i)
|
||||
flush()
|
||||
|
||||
Reference in New Issue
Block a user