[JS IR BE] Implement type check
* IrTypeOperator lowering * runtime support * refactoring
This commit is contained in:
@@ -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("<code>")
|
||||
|
||||
// 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
|
||||
|
||||
|
||||
+40
-26
@@ -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<CommonBackendContext> = object : Ir<CommonBackendContext>(this, irModuleFragment) {
|
||||
override val symbols: Symbols<CommonBackendContext> = object : Symbols<CommonBackendContext>(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<IrConstructorSymbol, SecondaryCtorPair>()
|
||||
|
||||
fun getOperatorByName(name: Name, type: KotlinType) = operatorMap[name]?.get(type)
|
||||
|
||||
override val ir = object : Ir<CommonBackendContext>(this, irModuleFragment) {
|
||||
override val symbols = object : Symbols<CommonBackendContext>(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<KotlinType, IrFunctionSymbol>()) { 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<IrConstructorSymbol, SecondaryCtorPair>()
|
||||
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<FunctionDescriptor> {
|
||||
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*/
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
+1
@@ -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(
|
||||
|
||||
+26
-28
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+299
@@ -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<DeclarationDescriptor> {
|
||||
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<IrStatement>()
|
||||
|
||||
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<IrStatement>()
|
||||
|
||||
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<IrStatement>()
|
||||
|
||||
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<IrStatement>, 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<IrStatement>()
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
+4
-1
@@ -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
|
||||
)
|
||||
|
||||
+2
-4
@@ -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<JsEx
|
||||
body.expression.accept(this, context)
|
||||
|
||||
override fun visitFunctionReference(expression: IrFunctionReference, context: JsGenerationContext): JsExpression {
|
||||
val irFunction = expression.symbol.owner as IrFunction
|
||||
val irFunction = expression.symbol.owner
|
||||
return irFunction.accept(IrFunctionToJsTransformer(), context).apply { name = null }
|
||||
}
|
||||
|
||||
@@ -111,7 +110,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
||||
|
||||
override fun visitGetObjectValue(expression: IrGetObjectValue, context: JsGenerationContext) = when (expression.symbol.owner.kind) {
|
||||
ClassKind.OBJECT -> {
|
||||
// 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<JsEx
|
||||
return if (symbol is IrConstructorSymbol) {
|
||||
JsNew(context.getNameForSymbol(symbol).makeRef(), arguments)
|
||||
} else {
|
||||
// TODO sanitize name
|
||||
val symbolName = context.getNameForSymbol(symbol)
|
||||
val ref = if (jsDispatchReceiver != null) JsNameRef(symbolName, jsDispatchReceiver) else JsNameRef(symbolName)
|
||||
JsInvocation(ref, jsExtensionReceiver?.let { listOf(jsExtensionReceiver) + arguments } ?: arguments)
|
||||
|
||||
-8
@@ -14,14 +14,6 @@ import org.jetbrains.kotlin.js.backend.ast.*
|
||||
|
||||
class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsStatement, JsGenerationContext> {
|
||||
|
||||
// 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) })
|
||||
}
|
||||
|
||||
+13
-1
@@ -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<JsName>, statements: MutableList<JsStatement>, classModels: Map<JsName, JsClassModel>) {
|
||||
private fun addPostDeclaration(
|
||||
name: JsName,
|
||||
visited: MutableSet<JsName>,
|
||||
statements: MutableList<JsStatement>,
|
||||
classModels: Map<JsName, JsClassModel>
|
||||
) {
|
||||
if (visited.add(name)) {
|
||||
classModels[name]?.run {
|
||||
superName?.let { addPostDeclaration(it, visited, statements, classModels) }
|
||||
|
||||
+10
@@ -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]
|
||||
|
||||
+24
-5
@@ -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, "<js-code>")).orEmpty()
|
||||
override fun <T> visitConst(expression: IrConst<T>) {
|
||||
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, "<js-code>")).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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
+1
-1
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 <T> 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<Boolean>()
|
||||
}
|
||||
|
||||
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<dynamic>
|
||||
}
|
||||
|
||||
// TODO: replace `isInterface` with the following
|
||||
public fun isInterface(ctor: dynamic, IType: dynamic): Boolean {
|
||||
if (ctor === IType) return true
|
||||
|
||||
val metadata = ctor.`$metadata$`.unsafeCast<ClassMetadata?>()
|
||||
|
||||
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<String>()
|
||||
|
||||
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<Boolean>()
|
||||
}
|
||||
}
|
||||
|
||||
public fun isArray(obj: Any): Boolean {
|
||||
return js("Array.isArray(obj)").unsafeCast<Boolean>()
|
||||
}
|
||||
|
||||
public fun isChar(c: Any): Boolean {
|
||||
return js("throw Error(\"isChar is not implemented\")").unsafeCast<Boolean>()
|
||||
}
|
||||
Reference in New Issue
Block a user