[JS IR BE] Implement type check

* IrTypeOperator lowering
 * runtime support
 * refactoring
This commit is contained in:
Roman Artemev
2018-05-31 15:35:08 +03:00
parent 50eda3b8a0
commit 17d71f1e46
22 changed files with 673 additions and 89 deletions
@@ -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
@@ -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)
}
@@ -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(
@@ -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))
}
}
}
@@ -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)
}
}
@@ -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
)
@@ -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)
@@ -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) })
}
@@ -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) }
@@ -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]
@@ -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
}
@@ -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>()
}