diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt index 1969b6cee09..4afd1ca7c6e 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt @@ -10,21 +10,22 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl import org.jetbrains.kotlin.ir.backend.js.utils.createValueParameter -import org.jetbrains.kotlin.ir.backend.js.utils.getFunctions import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.util.DeclarationStubGenerator -import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.js.resolve.JsPlatform.builtIns -import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinTypeFactory import org.jetbrains.kotlin.types.Variance -class JsIntrinsics(private val module: ModuleDescriptor, private val irBuiltIns: IrBuiltIns, symbolTable: SymbolTable) { +class JsIntrinsics( + private val module: ModuleDescriptor, + private val irBuiltIns: IrBuiltIns, + context: JsIrBackendContext +) { - private val stubBuilder = DeclarationStubGenerator(symbolTable, JsLoweredDeclarationOrigin.JS_INTRINSICS_STUB) + private val stubBuilder = DeclarationStubGenerator(context.symbolTable, JsLoweredDeclarationOrigin.JS_INTRINSICS_STUB) // Equality operations: @@ -81,18 +82,45 @@ class JsIntrinsics(private val module: ModuleDescriptor, private val irBuiltIns: val jsPropertyGet = binOp("kPropertyGet") val jsPropertySet = tripleOp("kPropertySet", irBuiltIns.unit) - // Other: + + // Type checks: val jsInstanceOf = binOpBool("jsInstanceOf") + val jsTypeOf = unOp("jsTypeOf", irBuiltIns.string) - val jsObjectCreate = defineObjectCreateIntrinsic() - val jsSetJSField = defineSetJSPropertyIntrinsic() + // Other: - val jsCode = module.getFunctions(FqName("kotlin.js.js")).singleOrNull()?.let { symbolTable.referenceFunction(it) } + val jsObjectCreate = defineObjectCreateIntrinsic() // Object.create + val jsSetJSField = defineSetJSPropertyIntrinsic() // till we don't have dynamic type we use intrinsic which sets a field with any name + val jsToJsType = defineToJsType() // creates name reference to KotlinType + val jsCode = context.getInternalFunctions("js").singleOrNull()?.let { context.symbolTable.referenceFunction(it) } // js("") // Helpers: + private fun defineToJsType(): IrSimpleFunction { + val desc = SimpleFunctionDescriptorImpl.create( + module, + Annotations.EMPTY, + Name.identifier("\$toJSType\$"), + CallableMemberDescriptor.Kind.SYNTHESIZED, + SourceElement.NO_SOURCE + ).apply { + + val typeParameter = TypeParameterDescriptorImpl.createWithDefaultBound( + this, + Annotations.EMPTY, + false, + Variance.INVARIANT, + Name.identifier("T"), + 0 + ) + initialize(null, null, listOf(typeParameter), emptyList(), builtIns.anyType, Modality.FINAL, Visibilities.PUBLIC) + } + + return stubBuilder.generateFunctionStub(desc) + } + // TODO: unify how we create intrinsic symbols private fun defineObjectCreateIntrinsic(): IrSimpleFunction { @@ -121,7 +149,6 @@ class JsIntrinsics(private val module: ModuleDescriptor, private val irBuiltIns: return stubBuilder.generateFunctionStub(desc) } - private fun defineSetJSPropertyIntrinsic(): IrSimpleFunction { val returnType = irBuiltIns.unit diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt index 85862270a76..74ef23ff690 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt @@ -12,21 +12,23 @@ import org.jetbrains.kotlin.backend.common.ir.Ir import org.jetbrains.kotlin.backend.common.ir.Symbols import org.jetbrains.kotlin.backend.js.JsDescriptorsFactory import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol +import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.types.KotlinType class JsIrBackendContext( val module: ModuleDescriptor, @@ -35,20 +37,31 @@ class JsIrBackendContext( irModuleFragment: IrModuleFragment ) : CommonBackendContext { - val intrinsics = JsIntrinsics(module, irBuiltIns, symbolTable) - override val builtIns = module.builtIns + override val sharedVariablesManager = JsSharedVariablesManager(builtIns, KnownPackageFragmentDescriptor(builtIns.builtInsModule, FqName("kotlin.js.internal"))) override val descriptorsFactory = JsDescriptorsFactory() - override val reflectionTypes: ReflectionTypes by lazy(LazyThreadSafetyMode.PUBLICATION) { // TODO ReflectionTypes(module, FqName("kotlin.reflect")) } - override val ir: Ir = object : Ir(this, irModuleFragment) { - override val symbols: Symbols = object : Symbols(this@JsIrBackendContext, symbolTable) { + private val internalPackageName = FqName("kotlin.js") + private val internalPackage = module.getPackage(internalPackageName) + + val intrinsics = JsIntrinsics(module, irBuiltIns, this) + + private val operatorMap = referenceOperators() + + data class SecondaryCtorPair(val delegate: IrSimpleFunctionSymbol, val stub: IrSimpleFunctionSymbol) + + val secondaryConstructorsMap = mutableMapOf() + + fun getOperatorByName(name: Name, type: KotlinType) = operatorMap[name]?.get(type) + + override val ir = object : Ir(this, irModuleFragment) { + override val symbols = object : Symbols(this@JsIrBackendContext, symbolTable) { override fun calc(initializer: () -> IrClassSymbol): IrClassSymbol { return object : IrClassSymbol { @@ -63,13 +76,13 @@ class JsIrBackendContext( get () = TODO("not implemented") override val ThrowNullPointerException - get () = TODO("not implemented") + get () = irBuiltIns.throwNpeSymbol override val ThrowNoWhenBranchMatchedException - get () = TODO("not implemented") + get () = irBuiltIns.noWhenBranchMatchedExceptionSymbol override val ThrowTypeCastException - get () = TODO("not implemented") + get () = irBuiltIns.throwCceSymbol override val ThrowUninitializedPropertyAccessException = symbolTable.referenceSimpleFunction( irBuiltIns.defineOperator( @@ -92,29 +105,30 @@ class JsIrBackendContext( override fun shouldGenerateHandlerParameterForDefaultBodyFun() = true } - data class SecondaryCtorPair(val delegate: IrSimpleFunctionSymbol, val stub: IrSimpleFunctionSymbol) + private fun referenceOperators() = OperatorNames.ALL.map { name -> + name to irBuiltIns.primitiveTypes.fold(mutableMapOf()) { m, t -> + val function = t.memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND).singleOrNull() + function?.let { m.put(t, symbolTable.referenceSimpleFunction(it)) } + m + } + }.toMap() - val secondaryConstructorsMap = mutableMapOf() + private fun findClass(memberScope: MemberScope, className: String) = findClass(memberScope, Name.identifier(className)) - private fun find(memberScope: MemberScope, className: String): ClassDescriptor { - return find(memberScope, Name.identifier(className)) - } + private fun findClass(memberScope: MemberScope, name: Name) = + memberScope.getContributedClassifier(name, NoLookupLocation.FROM_BACKEND) as ClassDescriptor - private fun find(memberScope: MemberScope, name: Name): ClassDescriptor { - return memberScope.getContributedClassifier(name, NoLookupLocation.FROM_BACKEND) as ClassDescriptor - } + private fun findFunctions(memberScope: MemberScope, className: String) = + findFunctions(memberScope, Name.identifier(className)) - override fun getInternalClass(name: String): ClassDescriptor { - return find(module.getPackage(FqName("kotlin.js")).memberScope, name) - } + private fun findFunctions(memberScope: MemberScope, name: Name) = + memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND).toList() - override fun getClass(fqName: FqName): ClassDescriptor { - return find(module.getPackage(fqName.parent()).memberScope, fqName.shortName()) - } + override fun getInternalClass(name: String) = findClass(internalPackage.memberScope, name) - override fun getInternalFunctions(name: String): List { - TODO("not implemented") - } + override fun getClass(fqName: FqName) = findClass(module.getPackage(fqName.parent()).memberScope, fqName.shortName()) + + override fun getInternalFunctions(name: String) = findFunctions(internalPackage.memberScope, name) override fun log(message: () -> String) { /*TODO*/ diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt index a37ef7fbc21..19031cc9b36 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt @@ -9,10 +9,7 @@ import com.intellij.openapi.project.Project import org.jetbrains.kotlin.backend.common.lower.* import org.jetbrains.kotlin.backend.common.runOnFilePostfix import org.jetbrains.kotlin.config.CompilerConfiguration -import org.jetbrains.kotlin.ir.backend.js.lower.BlockDecomposerLowering -import org.jetbrains.kotlin.ir.backend.js.lower.CallableReferenceLowering -import org.jetbrains.kotlin.ir.backend.js.lower.IntrinsicifyCallsLowering -import org.jetbrains.kotlin.ir.backend.js.lower.SecondaryCtorLowering +import org.jetbrains.kotlin.ir.backend.js.lower.* import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator @@ -65,6 +62,7 @@ fun JsIrBackendContext.lower(file: IrFile) { InnerClassConstructorCallsLowering(this).runOnFilePostfix(file) PropertiesLowering().lower(file) InitializersLowering(this, JsLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, false).runOnFilePostfix(file) + TypeOperatorLowering(this).lower(file) BlockDecomposerLowering(this).runOnFilePostfix(file) SecondaryCtorLowering(this).runOnFilePostfix(file) CallableReferenceLowering(this).lower(file) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrArithBuilder.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrArithBuilder.kt new file mode 100644 index 00000000000..a153ab10c66 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrArithBuilder.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. 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.ir + +import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext +import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.name.Name + +class JsIrArithBuilder(val context: JsIrBackendContext) { + + val symbols = context.ir.symbols + + private fun buildBinaryOperator(name: Name, l: IrExpression, r: IrExpression): IrExpression { + val symbol = context.getOperatorByName(name, l.type)!! + return JsIrBuilder.buildCall(symbol).apply { + dispatchReceiver = l + putValueArgument(0, r) + } + } + + private fun buildUnaryOperator(name: Name, v: IrExpression): IrExpression { + val symbol = context.getOperatorByName(name, v.type)!! + return JsIrBuilder.buildCall(symbol).apply { dispatchReceiver = v } + } + + fun add(l: IrExpression, r: IrExpression) = buildBinaryOperator(OperatorNames.ADD, l, r) + fun sub(l: IrExpression, r: IrExpression) = buildBinaryOperator(OperatorNames.SUB, l, r) + fun mul(l: IrExpression, r: IrExpression) = buildBinaryOperator(OperatorNames.MUL, l, r) + fun div(l: IrExpression, r: IrExpression) = buildBinaryOperator(OperatorNames.DIV, l, r) + fun mod(l: IrExpression, r: IrExpression) = buildBinaryOperator(OperatorNames.MOD, l, r) + fun and(l: IrExpression, r: IrExpression) = buildBinaryOperator(OperatorNames.AND, l, r) + fun or(l: IrExpression, r: IrExpression) = buildBinaryOperator(OperatorNames.OR, l, r) + fun shl(l: IrExpression, r: IrExpression) = buildBinaryOperator(OperatorNames.SHL, l, r) + fun shr(l: IrExpression, r: IrExpression) = buildBinaryOperator(OperatorNames.SHR, l, r) + fun shru(l: IrExpression, r: IrExpression) = buildBinaryOperator(OperatorNames.SHRU, l, r) + fun not(v: IrExpression): IrExpression = buildUnaryOperator(OperatorNames.NOT, v) + fun inv(v: IrExpression): IrExpression = buildUnaryOperator(OperatorNames.INV, v) + + fun andand(l: IrExpression, r: IrExpression) = // if (l) r else false + JsIrBuilder.buildIfElse(context.builtIns.booleanType, l, r, JsIrBuilder.buildBoolean(context.builtIns.booleanType, false)) + fun oror(l: IrExpression, r: IrExpression) = // if (l) true else r + JsIrBuilder.buildIfElse(context.builtIns.booleanType, l, JsIrBuilder.buildBoolean(context.builtIns.booleanType, true), r) +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt index 98b8af13dae..69690087b06 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt @@ -78,5 +78,6 @@ object JsIrBuilder { fun buildNull(type: KotlinType) = IrConstImpl.constNull(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type) fun buildBoolean(type: KotlinType, v: Boolean) = IrConstImpl.boolean(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, v) + fun buildInt(type: KotlinType, v: Int) = IrConstImpl.int(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, v) fun buildString(type: KotlinType, s: String) = IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, s) } \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CallableReferenceLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CallableReferenceLowering.kt index 134893de7b3..93e0d5a9659 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CallableReferenceLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CallableReferenceLowering.kt @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.ir.symbols.IrValueSymbol import org.jetbrains.kotlin.ir.visitors.* import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +// TODO: generate $metadata$ property and fill it with corresponding KFunction/KProperty interface class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringPass { private data class CallableReferenceKey( diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/IntrinsicifyCallsLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/IntrinsicifyCallsLowering.kt index a867e0adba9..313ec78f37b 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/IntrinsicifyCallsLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/IntrinsicifyCallsLowering.kt @@ -7,25 +7,23 @@ package org.jetbrains.kotlin.ir.backend.js.lower import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET 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.Namer +import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.expressions.IrCall -import org.jetbrains.kotlin.ir.expressions.IrConstKind import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.copyTypeArgumentsFrom import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.SimpleType -import org.jetbrains.kotlin.util.OperatorNameConventions class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileLoweringPass { @@ -48,36 +46,36 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL memberToIrFunction.run { for (type in primitiveNumbers) { - op(type, OperatorNameConventions.UNARY_PLUS, context.intrinsics.jsUnaryPlus) - op(type, OperatorNameConventions.UNARY_MINUS, context.intrinsics.jsUnaryMinus) + op(type, OperatorNames.UNARY_PLUS, context.intrinsics.jsUnaryPlus) + op(type, OperatorNames.UNARY_MINUS, context.intrinsics.jsUnaryMinus) - op(type, OperatorNameConventions.PLUS, context.intrinsics.jsPlus) - op(type, OperatorNameConventions.MINUS, context.intrinsics.jsMinus) - op(type, OperatorNameConventions.TIMES, context.intrinsics.jsMult) - op(type, OperatorNameConventions.DIV, context.intrinsics.jsDiv) - op(type, OperatorNameConventions.MOD, context.intrinsics.jsMod) - op(type, OperatorNameConventions.REM, context.intrinsics.jsMod) + op(type, OperatorNames.ADD, context.intrinsics.jsPlus) + op(type, OperatorNames.SUB, context.intrinsics.jsMinus) + op(type, OperatorNames.MUL, context.intrinsics.jsMult) + op(type, OperatorNames.DIV, context.intrinsics.jsDiv) + op(type, OperatorNames.MOD, context.intrinsics.jsMod) + op(type, OperatorNames.REM, context.intrinsics.jsMod) } context.irBuiltIns.string.let { - op(it, OperatorNameConventions.PLUS, context.intrinsics.jsPlus) + op(it, OperatorNames.ADD, context.intrinsics.jsPlus) } context.irBuiltIns.int.let { - op(it, "shl", context.intrinsics.jsBitShiftL) - op(it, "shr", context.intrinsics.jsBitShiftR) - op(it, "ushr", context.intrinsics.jsBitShiftRU) - op(it, "and", context.intrinsics.jsBitAnd) - op(it, "or", context.intrinsics.jsBitOr) - op(it, "xor", context.intrinsics.jsBitXor) - op(it, "inv", context.intrinsics.jsBitNot) + op(it, OperatorNames.SHL, context.intrinsics.jsBitShiftL) + op(it, OperatorNames.SHR, context.intrinsics.jsBitShiftR) + op(it, OperatorNames.SHRU, context.intrinsics.jsBitShiftRU) + op(it, OperatorNames.AND, context.intrinsics.jsBitAnd) + op(it, OperatorNames.OR, context.intrinsics.jsBitOr) + op(it, OperatorNames.XOR, context.intrinsics.jsBitXor) + op(it, OperatorNames.INV, context.intrinsics.jsBitNot) } context.irBuiltIns.bool.let { - op(it, OperatorNameConventions.AND, context.intrinsics.jsAnd) - op(it, OperatorNameConventions.OR, context.intrinsics.jsOr) - op(it, OperatorNameConventions.NOT, context.intrinsics.jsNot) - op(it, "xor", context.intrinsics.jsBitXor) + op(it, OperatorNames.AND, context.intrinsics.jsAnd) + op(it, OperatorNames.OR, context.intrinsics.jsOr) + op(it, OperatorNames.NOT, context.intrinsics.jsNot) + op(it, OperatorNames.XOR, context.intrinsics.jsBitXor) } } @@ -99,14 +97,14 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL memberToTransformer.run { for (type in primitiveNumbers) { // TODO: use increment and decrement when it's possible - op(type, OperatorNameConventions.INC) { + op(type, OperatorNames.INC) { irCall(it, context.intrinsics.jsPlus.symbol, dispatchReceiverAsFirstArgument = true).apply { - putValueArgument(1, IrConstImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.int, IrConstKind.Int, 1)) + putValueArgument(1, JsIrBuilder.buildInt(context.irBuiltIns.int, 1)) } } - op(type, OperatorNameConventions.DEC) { + op(type, OperatorNames.DEC) { irCall(it, context.intrinsics.jsMinus.symbol, dispatchReceiverAsFirstArgument = true).apply { - putValueArgument(1, IrConstImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.int, IrConstKind.Int, 1)) + putValueArgument(1, JsIrBuilder.buildInt(context.irBuiltIns.int, 1)) } } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt new file mode 100644 index 00000000000..c128f178f75 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt @@ -0,0 +1,299 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. 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.lower + +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.descriptors.isFunctionOrKFunctionType +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext +import org.jetbrains.kotlin.ir.backend.js.ir.JsIrArithBuilder +import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder +import org.jetbrains.kotlin.ir.backend.js.symbols.JsSymbolBuilder +import org.jetbrains.kotlin.ir.declarations.IrDeclaration +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrTypeOperator +import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall +import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl +import org.jetbrains.kotlin.ir.visitors.IrElementTransformer +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.isNullable +import org.jetbrains.kotlin.types.typeUtil.* + +class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass { + private val unit = context.builtIns.unit + private val unitValue = JsIrBuilder.buildGetObjectValue(unit.defaultType, context.symbolTable.referenceClass(unit)) + + private val lit24 = JsIrBuilder.buildInt(context.builtIns.intType, 24) + private val lit16 = JsIrBuilder.buildInt(context.builtIns.intType, 16) + + private val byteMask = JsIrBuilder.buildInt(context.builtIns.intType, 0xFF) + private val shortMask = JsIrBuilder.buildInt(context.builtIns.intType, 0xFFFF) + + private val calculator = JsIrArithBuilder(context) + + //NOTE: Should we define JS-own functions similar to current implementation? + private val throwCCE = context.irBuiltIns.throwCceSymbol + private val throwNPE = context.irBuiltIns.throwNpeSymbol + + private val eqeq = context.irBuiltIns.eqeqSymbol + + private val isInterfaceSymbol = getInternalFunction("isInterface") + private val isArraySymbol = getInternalFunction("isArray") + private val isCharSymbol = getInternalFunction("isChar") + private val isObjectSymbol = getInternalFunction("isObject") + + private val instanceOfIntrinsicSymbol = context.intrinsics.jsInstanceOf.symbol + private val typeOfIntrinsicSymbol = context.intrinsics.jsTypeOf.symbol + private val toJSTypeIntrinsicSymbol = context.intrinsics.jsToJsType.symbol + + private val stringMarker = JsIrBuilder.buildString(context.irBuiltIns.string, "string") + private val booleanMarker = JsIrBuilder.buildString(context.irBuiltIns.string, "boolean") + private val functionMarker = JsIrBuilder.buildString(context.irBuiltIns.string, "function") + private val numberMarker = JsIrBuilder.buildString(context.irBuiltIns.string, "number") + + private val litTrue: IrExpression = JsIrBuilder.buildBoolean(context.irBuiltIns.bool, true) + private val litNull: IrExpression = JsIrBuilder.buildNull(context.builtIns.nullableNothingType) + + private fun getInternalFunction(name: String) = context.symbolTable.referenceSimpleFunction(context.getInternalFunctions(name).single()) + + override fun lower(irFile: IrFile) { + // TODO: get rid of descriptors + irFile.transformChildren(object : IrElementTransformer { + override fun visitDeclaration(declaration: IrDeclaration, data: DeclarationDescriptor) = + super.visitDeclaration(declaration, declaration.descriptor) + + override fun visitTypeOperator(expression: IrTypeOperatorCall, data: DeclarationDescriptor): IrExpression { + super.visitTypeOperator(expression, data) + + return when (expression.operator) { + IrTypeOperator.IMPLICIT_CAST -> lowerImplicitCast(expression) + IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> lowerCoercionToUnit(expression) + IrTypeOperator.IMPLICIT_INTEGER_COERCION -> lowerIntegerCoercion(expression, data) + IrTypeOperator.IMPLICIT_NOTNULL -> lowerImplicitNotNull(expression, data) + IrTypeOperator.INSTANCEOF -> lowerInstanceOf(expression, data, false) + IrTypeOperator.NOT_INSTANCEOF -> lowerInstanceOf(expression, data, true) + IrTypeOperator.CAST -> lowerCast(expression, data, false) + IrTypeOperator.SAFE_CAST -> lowerCast(expression, data, true) + } + } + + private fun lowerImplicitNotNull(expression: IrTypeOperatorCall, containingDeclaration: DeclarationDescriptor): IrExpression { + assert(expression.operator == IrTypeOperator.IMPLICIT_NOTNULL) + assert(expression.typeOperand.isNullable() xor expression.argument.type.isNullable()) + + val newStatements = mutableListOf() + + val argument = cacheValue(expression.argument, newStatements, containingDeclaration) + val irNullCheck = nullCheck(argument) + + newStatements += JsIrBuilder.buildIfElse(expression.typeOperand, irNullCheck, JsIrBuilder.buildCall(throwNPE), argument) + + return expression.run { IrCompositeImpl(startOffset, endOffset, typeOperand, null, newStatements) } + } + + private fun lowerCast( + expression: IrTypeOperatorCall, + containingDeclaration: DeclarationDescriptor, + isSafe: Boolean + ): IrExpression { + assert(expression.operator == IrTypeOperator.CAST || expression.operator == IrTypeOperator.SAFE_CAST) + assert((expression.operator == IrTypeOperator.SAFE_CAST) == isSafe) + + val toType = expression.typeOperand + val failResult = if (isSafe) litNull else JsIrBuilder.buildCall(throwCCE) + + val newStatements = mutableListOf() + + val argument = cacheValue(expression.argument, newStatements, containingDeclaration) + + val check = generateTypeCheck(argument, toType) + + newStatements += JsIrBuilder.buildIfElse(toType, check, argument, failResult) + + return expression.run { IrCompositeImpl(startOffset, endOffset, toType, null, newStatements) } + } + + private fun lowerImplicitCast(expression: IrTypeOperatorCall) = expression.run { + assert(operator == IrTypeOperator.IMPLICIT_CAST) + IrCompositeImpl(startOffset, endOffset, typeOperand, null, listOf(argument)) + } + + // Note: native `instanceOf` is not used which is important because of null-behaviour + private fun advancedCheckRequired(type: KotlinType) = type.isInterface() || + KotlinBuiltIns.isArray(type) || + KotlinBuiltIns.isPrimitiveArray(type) || + isTypeOfCheckingType(type) + + private fun isTypeOfCheckingType(type: KotlinType) = + ((type.isPrimitiveNumberType() || KotlinBuiltIns.isNumber(type)) && !type.isLong() && !type.isChar()) || + type.isBoolean() || + type.isFunctionOrKFunctionType || + KotlinBuiltIns.isString(type) + + fun lowerInstanceOf( + expression: IrTypeOperatorCall, + containingDeclaration: DeclarationDescriptor, + inverted: Boolean + ): IrExpression { + assert(expression.operator == IrTypeOperator.INSTANCEOF || expression.operator == IrTypeOperator.NOT_INSTANCEOF) + assert((expression.operator == IrTypeOperator.NOT_INSTANCEOF) == inverted) + + val toType = expression.typeOperand + val isCopyRequired = expression.argument.type.isNullable() && advancedCheckRequired(toType.makeNotNullable()) + val newStatements = mutableListOf() + + val argument = + if (isCopyRequired) cacheValue(expression.argument, newStatements, containingDeclaration) else expression.argument + val check = generateTypeCheck(argument, toType) + val result = if (inverted) calculator.not(check) else check + + return if (isCopyRequired) { + newStatements += result + IrCompositeImpl(expression.startOffset, expression.endOffset, toType, null, newStatements) + } else result + } + + private fun nullCheck(value: IrExpression) = JsIrBuilder.buildCall(eqeq).apply { + putValueArgument(0, value) + putValueArgument(1, litNull) + } + + private fun cacheValue(value: IrExpression, newStatements: MutableList, cd: DeclarationDescriptor): IrExpression { + val varSymbol = JsSymbolBuilder.buildTempVar(cd, value.type, mutable = false) + newStatements += JsIrBuilder.buildVar(varSymbol, value) + return JsIrBuilder.buildGetValue(varSymbol) + } + + private fun generateTypeCheck(argument: IrExpression, toType: KotlinType): IrExpression { + val toNotNullable = toType.makeNotNullable() + val instanceCheck = generateTypeCheckNonNull(argument, toNotNullable) + val isFromNullable = argument.type.isNullable() + val isToNullable = toType.isNullable() + val isNativeCheck = !advancedCheckRequired(toNotNullable) + + return when { + !isFromNullable -> instanceCheck // ! -> * + isToNullable -> calculator.run { oror(nullCheck(argument), instanceCheck) } // * -> ? + else -> if (isNativeCheck) instanceCheck else calculator.run { + andand( + not(nullCheck(argument)), + instanceCheck + ) + } // ? -> ! + } + } + + private fun generateTypeCheckNonNull(argument: IrExpression, toType: KotlinType): IrExpression { + assert(!toType.isMarkedNullable) + return when { + KotlinBuiltIns.isAny(toType) -> generateIsObjectCheck(argument) + isTypeOfCheckingType(toType) -> generateTypeOfCheck(argument, toType) + toType.isChar() -> generateCheckForChar(argument) + KotlinBuiltIns.isArray(toType) -> generateGenericArrayCheck(argument) + KotlinBuiltIns.isPrimitiveArray(toType) -> generatePrimitiveArrayTypeCheck(argument, toType) + toType.isTypeParameter() -> generateTypeCheckWithTypeParameter(argument, toType) + toType.isInterface() -> generateInterfaceCheck(argument, toType) + else -> generateNativeInstanceOf(argument, toType) + } + } + + private fun generateIsObjectCheck(argument: IrExpression) = JsIrBuilder.buildCall(isObjectSymbol).apply { + putValueArgument(0, argument) + } + + private fun generateTypeCheckWithTypeParameter(argument: IrExpression, toType: KotlinType): IrExpression { + val typeParameterDescriptor = toType.constructor.declarationDescriptor as TypeParameterDescriptor + assert(!typeParameterDescriptor.isReified) { "reified parameters have to be lowered before" } + + return typeParameterDescriptor.upperBounds.fold(litTrue) { r, t -> + val check = generateTypeCheckNonNull(argument, t.makeNotNullable()) + calculator.and(r, check) + } + } + + private fun generateCheckForChar(argument: IrExpression) = + JsIrBuilder.buildCall(isCharSymbol).apply { dispatchReceiver = argument } + + private fun generateTypeOfCheck(argument: IrExpression, toType: KotlinType): IrExpression { + val marker = when { + toType.isFunctionOrKFunctionType -> functionMarker + toType.isBoolean() -> booleanMarker + KotlinBuiltIns.isString(toType) -> stringMarker + else -> numberMarker + } + + val typeOf = JsIrBuilder.buildCall(typeOfIntrinsicSymbol).apply { putValueArgument(0, argument) } + return JsIrBuilder.buildCall(eqeq).apply { + putValueArgument(0, typeOf) + putValueArgument(1, marker) + } + } + + private fun wrapTypeReference(toType: KotlinType) = + JsIrBuilder.buildCall(toJSTypeIntrinsicSymbol).apply { putTypeArgument(0, toType) } + + private fun generateGenericArrayCheck(argument: IrExpression) = + JsIrBuilder.buildCall(isArraySymbol).apply { putValueArgument(0, argument) } + + private fun generatePrimitiveArrayTypeCheck(argument: IrExpression, toType: KotlinType): IrExpression { + TODO("Implement Typed Array check") + } + + private fun generateInterfaceCheck(argument: IrExpression, toType: KotlinType): IrExpression { + val irType = wrapTypeReference(toType) + return JsIrBuilder.buildCall(isInterfaceSymbol).apply { + putValueArgument(0, argument) + putValueArgument(1, irType) + } + } + + private fun generateNativeInstanceOf(argument: IrExpression, toType: KotlinType): IrExpression { + val irType = wrapTypeReference(toType) + return JsIrBuilder.buildCall(instanceOfIntrinsicSymbol).apply { + putValueArgument(0, argument) + putValueArgument(1, irType) + } + } + + private fun lowerCoercionToUnit(expression: IrTypeOperatorCall): IrExpression { + assert(expression.operator === IrTypeOperator.IMPLICIT_COERCION_TO_UNIT) + return expression.run { IrCompositeImpl(startOffset, endOffset, unit.defaultType, null, listOf(argument, unitValue)) } + } + + private fun lowerIntegerCoercion(expression: IrTypeOperatorCall, containingDeclaration: DeclarationDescriptor): IrExpression { + assert(expression.operator === IrTypeOperator.IMPLICIT_INTEGER_COERCION) + assert(KotlinBuiltIns.isInt(expression.argument.type)) + + val isNullable = expression.argument.type.isNullable() + val toType = expression.typeOperand + + fun maskOp(arg: IrExpression, mask: IrExpression, shift: IrExpression) = calculator.run { + shr(shl(and(arg, mask), shift), shift) + } + + val newStatements = mutableListOf() + + val argument = + if (isNullable) cacheValue(expression.argument, newStatements, containingDeclaration) else expression.argument + + val casted = when { + KotlinBuiltIns.isByte(toType) -> maskOp(argument, byteMask, lit24) + KotlinBuiltIns.isShort(toType) -> maskOp(argument, shortMask, lit16) + KotlinBuiltIns.isLong(toType) -> TODO("Long coercion") + else -> error("Unreachable execution (coercion to non-Integer type") + } + + newStatements += if (isNullable) JsIrBuilder.buildIfElse(toType, nullCheck(argument), litNull, casted) else casted + + return expression.run { IrCompositeImpl(startOffset, endOffset, toType, null, newStatements) } + } + }, irFile.packageFragmentDescriptor) + } +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/symbols/SymbolBuilder.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/symbols/SymbolBuilder.kt index d738fd1d0f0..15bc3d4474e 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/symbols/SymbolBuilder.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/symbols/SymbolBuilder.kt @@ -68,9 +68,12 @@ object JsSymbolBuilder { ) fun buildTempVar(containingSymbol: IrFunctionSymbol, type: KotlinType, name: String? = null, mutable: Boolean = false) = + buildTempVar(containingSymbol.descriptor, type, name, mutable) + + fun buildTempVar(containingDeclaration: DeclarationDescriptor, type: KotlinType, name: String? = null, mutable: Boolean = false) = IrVariableSymbolImpl( IrTemporaryVariableDescriptorImpl( - containingSymbol.descriptor, + containingDeclaration, Name.identifier(name ?: "tmp"), type, mutable ) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt index b1752cc30e7..ddb44782fd0 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt @@ -10,7 +10,6 @@ import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext import org.jetbrains.kotlin.ir.backend.js.utils.Namer import org.jetbrains.kotlin.ir.declarations.IrConstructor -import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.js.backend.ast.* @@ -70,7 +69,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer { - // TODO: + // TODO: return unit instance instead of null if (expression.type.isUnit()) JsNullLiteral() else { val className = context.getNameForSymbol(expression.symbol) @@ -175,7 +174,6 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer { - // TODO: is it right place for this logic? Or should it be implemented as a separate lowering? - override fun visitTypeOperator(expression: IrTypeOperatorCall, context: JsGenerationContext): JsStatement { - if (expression.operator == IrTypeOperator.IMPLICIT_COERCION_TO_UNIT) { - return expression.argument.accept(this, context) - } - return super.visitTypeOperator(expression, context) - } - override fun visitBlockBody(body: IrBlockBody, context: JsGenerationContext): JsStatement { return JsBlock(body.statements.map { it.accept(this, context) }) } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt index 258091cab6a..7fc406e049e 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext +import org.jetbrains.kotlin.ir.backend.js.utils.Namer import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.js.backend.ast.* @@ -15,6 +16,12 @@ class IrModuleToJsTransformer(val backendContext: JsIrBackendContext) : BaseIrEl val program = JsProgram() val rootContext = JsGenerationContext(JsRootScope(program), backendContext) + // TODO: fix it up with new name generator + val kotlinAny = "kotlin\$${backendContext.builtIns.any.name.asString()}" + val anyName = rootContext.currentScope.declareName(kotlinAny) + + program.globalBlock.statements += JsVars(JsVars.JsVar(anyName, Namer.JS_OBJECT)) + declaration.files.forEach { program.globalBlock.statements.add(it.accept(IrFileToJsTransformer(), rootContext)) } @@ -38,7 +45,12 @@ class IrModuleToJsTransformer(val backendContext: JsIrBackendContext) : BaseIrEl return statements } - private fun addPostDeclaration(name: JsName, visited: MutableSet, statements: MutableList, classModels: Map) { + private fun addPostDeclaration( + name: JsName, + visited: MutableSet, + statements: MutableList, + classModels: Map + ) { if (visited.add(name)) { classModels[name]?.run { superName?.let { addPostDeclaration(it, visited, statements, classModels) } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt index da1860b1584..1e9f94cf591 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt @@ -37,6 +37,8 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) { binOp(intrinsics.jsLtEq, JsBinaryOperator.LTE) prefixOp(intrinsics.jsNot, JsUnaryOperator.NOT) + binOp(intrinsics.jsAnd, JsBinaryOperator.AND) + binOp(intrinsics.jsOr, JsBinaryOperator.OR) prefixOp(intrinsics.jsUnaryPlus, JsUnaryOperator.POS) prefixOp(intrinsics.jsUnaryMinus, JsUnaryOperator.NEG) @@ -63,6 +65,8 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) { binOp(intrinsics.jsInstanceOf, JsBinaryOperator.INSTANCEOF) + prefixOp(intrinsics.jsTypeOf, JsUnaryOperator.TYPEOF) + add(intrinsics.jsObjectCreate) { call, context -> val classToCreate = call.getTypeArgument(0)!! val className = context.getNameForSymbol(IrClassSymbolImpl(classToCreate.constructor.declarationDescriptor as ClassDescriptor)) @@ -81,6 +85,12 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) { jsAssignment(JsNameRef(fieldNameLiteral, receiver), fieldValue) } + add(intrinsics.jsToJsType) { call, context -> + val typeParameter = call.getTypeArgument(0)!! + val typeName = context.getNameForSymbol(IrClassSymbolImpl(typeParameter.constructor.declarationDescriptor as ClassDescriptor)) + typeName.makeRef() + } + add(backendContext.sharedVariablesManager.closureBoxConstructorTypeSymbol) { call, context -> val args = translateCallArguments(call, context) val initializer = args[0] diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsCode.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsCode.kt index d746bddf8f5..040015cd292 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsCode.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsCode.kt @@ -6,24 +6,43 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs import com.google.gwt.dev.js.ThrowExceptionOnErrorReporter +import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrConst -import org.jetbrains.kotlin.ir.expressions.IrConstKind +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrStringConcatenation +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid +import org.jetbrains.kotlin.ir.visitors.acceptVoid import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.parser.parse + fun translateJsCode(call: IrCall, scope: JsScope): JsNode { //TODO check non simple compile time constants (expressions) - val arg = call.getValueArgument(0) as? IrConst<*> - val kind = arg?.kind as? IrConstKind.String ?: error("Parameter of js function must be compile time String constant") + fun foldString(expression: IrExpression): String { + val builder = StringBuilder() + expression.acceptVoid(object : IrElementVisitorVoid { + override fun visitElement(element: IrElement) = error("Parameter of js function must be compile time String constant") - val statements = parseJsCode(kind.valueOf(arg), JsFunctionScope(scope, "")).orEmpty() + override fun visitConst(expression: IrConst) { + builder.append(expression.kind.valueOf(expression)) + } + + override fun visitStringConcatenation(expression: IrStringConcatenation) = expression.acceptChildrenVoid(this) + }) + + return builder.toString() + } + + val code = call.getValueArgument(0)!! + val statements = parseJsCode(code.run(::foldString), JsFunctionScope(scope, "")).orEmpty() val size = statements.size return when (size) { 0 -> JsEmpty - 1 -> statements[0].let { if (it is JsExpressionStatement) it.expression else it } + 1 -> statements[0].let { (it as? JsExpressionStatement)?.expression ?: it } else -> JsBlock(statements) } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/OperatorNames.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/OperatorNames.kt new file mode 100644 index 00000000000..7248a0c4a90 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/OperatorNames.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. 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.utils + +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.util.OperatorNameConventions + +object OperatorNames { + val UNARY_PLUS = OperatorNameConventions.UNARY_PLUS + val UNARY_MINUS = OperatorNameConventions.UNARY_MINUS + + val ADD = OperatorNameConventions.PLUS + val SUB = OperatorNameConventions.MINUS + val MUL = OperatorNameConventions.TIMES + val DIV = OperatorNameConventions.DIV + val MOD = OperatorNameConventions.MOD + val REM = OperatorNameConventions.REM + + val AND = OperatorNameConventions.AND + val OR = OperatorNameConventions.OR + val XOR = Name.identifier("xor") + val INV = Name.identifier("inv") + + val SHL = Name.identifier("shl") + val SHR = Name.identifier("shr") + val SHRU = Name.identifier("shru") + + val NOT = OperatorNameConventions.NOT + + val INC = OperatorNameConventions.INC + val DEC = OperatorNameConventions.DEC + + + val BINARY = setOf(ADD, SUB, MUL, DIV, MOD, REM, AND, OR, XOR, SHL, SHR, SHRU) + val UNARY = setOf(UNARY_PLUS, UNARY_MINUS, INV, NOT, INC, DEC) + val ALL = BINARY + UNARY +} \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DataClassMembersGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DataClassMembersGenerator.kt index e94410b3df7..f48d29a8b58 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DataClassMembersGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DataClassMembersGenerator.kt @@ -142,7 +142,7 @@ class DataClassMembersGenerator(declarationGenerator: DeclarationGenerator) : De buildMember(function, declaration) { +irIfThenReturnTrue(irEqeqeq(irThis(), irOther())) +irIfThenReturnFalse(irNotIs(irOther(), classDescriptor.defaultType, irClass.symbol)) - val otherWithCast = irTemporary(irAs(irOther(), classDescriptor.defaultType, irClass.symbol), "other_with_cast") + val otherWithCast = irTemporary(irImplicitCast(irOther(), classDescriptor.defaultType, irClass.symbol), "other_with_cast") for (property in properties) { val arg1 = irGet(irThis(), getPropertyGetterSymbol(property)) val arg2 = irGet(irGet(otherWithCast.symbol), getPropertyGetterSymbol(property)) diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBuiltIns.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBuiltIns.kt index 557a458b292..8fbe55e4ac0 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBuiltIns.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBuiltIns.kt @@ -99,18 +99,21 @@ class IrBuiltIns(val builtIns: KotlinBuiltIns) { val eqeqeqFun = defineOperator("EQEQEQ", bool, listOf(anyN, anyN)) val eqeqFun = defineOperator("EQEQ", bool, listOf(anyN, anyN)) val throwNpeFun = defineOperator("THROW_NPE", nothing, listOf()) + val throwCceFun = defineOperator("THROW_CCE", nothing, listOf()) val booleanNotFun = defineOperator("NOT", bool, listOf(bool)) val noWhenBranchMatchedExceptionFun = defineOperator("noWhenBranchMatchedException", unit, listOf()) val eqeqeq = eqeqeqFun.descriptor val eqeq = eqeqFun.descriptor val throwNpe = throwNpeFun.descriptor + val throwCce = throwCceFun.descriptor val booleanNot = booleanNotFun.descriptor val noWhenBranchMatchedException = noWhenBranchMatchedExceptionFun.descriptor val eqeqeqSymbol = eqeqeqFun.symbol val eqeqSymbol = eqeqFun.symbol val throwNpeSymbol = throwNpeFun.symbol + val throwCceSymbol = throwCceFun.symbol val booleanNotSymbol = booleanNotFun.symbol val noWhenBranchMatchedExceptionSymbol = noWhenBranchMatchedExceptionFun.symbol diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java index 0ef3976a2b0..b22a8f0f005 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java @@ -944,6 +944,10 @@ public abstract class KotlinBuiltIns { return classFqNameEquals(classDescriptor, FQ_NAMES._boolean); } + public static boolean isNumber(@NotNull KotlinType type) { + return isConstructedFromGivenClassAndNotNullable(type, FQ_NAMES.number); + } + public static boolean isChar(@NotNull KotlinType type) { return isConstructedFromGivenClassAndNotNullable(type, FQ_NAMES._char); } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt index adb294a9398..42f3d48df12 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt @@ -45,7 +45,9 @@ abstract class BasicIrBoxTest( ) { val runtime = listOf( "libraries/stdlib/js/src/kotlin/core.kt", - "libraries/stdlib/js/irRuntime/dummy.kt" + "libraries/stdlib/js/irRuntime/annotations.kt", + "libraries/stdlib/js/irRuntime/internalAnnotations.kt", + "libraries/stdlib/js/irRuntime/typeCheckUtils.kt" ).map { createPsiFile(it) } val filesToIgnore = listOf( diff --git a/libraries/stdlib/js/irRuntime/annotations.kt b/libraries/stdlib/js/irRuntime/annotations.kt new file mode 100644 index 00000000000..7fedf93f688 --- /dev/null +++ b/libraries/stdlib/js/irRuntime/annotations.kt @@ -0,0 +1,8 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlin + +interface Annotation \ No newline at end of file diff --git a/libraries/stdlib/js/irRuntime/internalAnnotations.kt b/libraries/stdlib/js/irRuntime/internalAnnotations.kt new file mode 100644 index 00000000000..48688d47fc7 --- /dev/null +++ b/libraries/stdlib/js/irRuntime/internalAnnotations.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlin.internal + +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY) +@Retention(AnnotationRetention.BINARY) +internal annotation class DynamicExtension \ No newline at end of file diff --git a/libraries/stdlib/js/irRuntime/typeCheckUtils.kt b/libraries/stdlib/js/irRuntime/typeCheckUtils.kt new file mode 100644 index 00000000000..7ef8b526ad4 --- /dev/null +++ b/libraries/stdlib/js/irRuntime/typeCheckUtils.kt @@ -0,0 +1,98 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlin.js + +@kotlin.internal.DynamicExtension +public fun dynamic.unsafeCast(): T = this + +private fun isInterfaceImpl(ctor: dynamic, iface: dynamic): Boolean { + if (ctor === iface) return true; + + val self = ::isInterfaceImpl + return js( + """ + var metadata = ctor.${'$'}metadata${'$'}; + if (metadata != null) { + var interfaces = metadata.interfaces; + for (var i = 0; i < interfaces.length; i++) { + if (self_0(interfaces[i], iface)) { + return true; + } + } + } + + var superPrototype = ctor.prototype != null ? Object.getPrototypeOf(ctor.prototype) : null; + var superConstructor = superPrototype != null ? superPrototype.constructor : null; + return superConstructor != null && self_0(superConstructor, iface); + """ + ).unsafeCast() +} + +public fun isInterface(obj: dynamic, iface: dynamic): Boolean { + //TODO: val ctor = obj.constructor + val ctor = js("obj.constructor") + + if (ctor == null) return false + + return isInterfaceImpl(ctor, iface) +} + +/* + +internal interface ClassMetadata { + val simpleName: String + val interfaces: Array +} + +// TODO: replace `isInterface` with the following +public fun isInterface(ctor: dynamic, IType: dynamic): Boolean { + if (ctor === IType) return true + + val metadata = ctor.`$metadata$`.unsafeCast() + + if (metadata !== null) { + val interfaces = metadata.interfaces + for (i in interfaces) { + if (isInterface(i, IType)) { + return true + } + } + } + + var superPrototype = ctor.prototype + if (superPrototype !== null) { + superPrototype = js("Object.getPrototypeOf(superPrototype)") + } + + val superConstructor = if (superPrototype !== null) { + superPrototype.constructor + } else null + + return superConstructor != null && isInterface(superConstructor, IType) +} +*/ + +inline private fun typeOf(obj: dynamic) = js("typeof obj").unsafeCast() + +fun isObject(obj: dynamic): Boolean { + val objTypeOf = typeOf(obj) + + return when (objTypeOf) { + "string" -> true + "number" -> true + "boolean" -> true + "function" -> true + else -> js("obj instanceof Object").unsafeCast() + } +} + +public fun isArray(obj: Any): Boolean { + return js("Array.isArray(obj)").unsafeCast() +} + +public fun isChar(c: Any): Boolean { + return js("throw Error(\"isChar is not implemented\")").unsafeCast() +} \ No newline at end of file