[Wasm] Major compiler and stdlib update

This commit is contained in:
Svyatoslav Kuzmich
2020-04-27 14:39:20 +03:00
parent 3be38d1796
commit bfd0f21e9d
196 changed files with 12635 additions and 4774 deletions
+1
View File
@@ -29,6 +29,7 @@ buildscript {
classpath("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.20") classpath("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.20")
classpath(kotlin("gradle-plugin", bootstrapKotlinVersion)) classpath(kotlin("gradle-plugin", bootstrapKotlinVersion))
classpath(kotlin("serialization", bootstrapKotlinVersion))
classpath("org.jetbrains.dokka:dokka-gradle-plugin:0.9.17") classpath("org.jetbrains.dokka:dokka-gradle-plugin:0.9.17")
classpath("org.jfrog.buildinfo:build-info-extractor-gradle:4.17.2") classpath("org.jfrog.buildinfo:build-info-extractor-gradle:4.17.2")
} }
@@ -12415,6 +12415,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/functions/recursiveIncrementCall.kt"); runTest("compiler/testData/codegen/box/functions/recursiveIncrementCall.kt");
} }
@TestMetadata("typeParameterAsUpperBound.kt")
public void testTypeParameterAsUpperBound() throws Exception {
runTest("compiler/testData/codegen/box/functions/typeParameterAsUpperBound.kt");
}
@TestMetadata("typeParametersInLocalFunction.kt") @TestMetadata("typeParametersInLocalFunction.kt")
public void testTypeParametersInLocalFunction() throws Exception { public void testTypeParametersInLocalFunction() throws Exception {
runTest("compiler/testData/codegen/box/functions/typeParametersInLocalFunction.kt"); runTest("compiler/testData/codegen/box/functions/typeParametersInLocalFunction.kt");
@@ -295,6 +295,9 @@ abstract class Symbols<out T : CommonBackendContext>(val context: T, irBuiltIns:
abstract val throwKotlinNothingValueException: IrSimpleFunctionSymbol abstract val throwKotlinNothingValueException: IrSimpleFunctionSymbol
open val throwISE: IrSimpleFunctionSymbol
get() = error("throwISE is not implemented")
abstract val stringBuilder: IrClassSymbol abstract val stringBuilder: IrClassSymbol
abstract val defaultConstructorMarker: IrClassSymbol abstract val defaultConstructorMarker: IrClassSymbol
@@ -271,9 +271,13 @@ class InlineClassLowering(val context: CommonBackendContext) {
} }
} }
private fun Name.toInlineClassImplementationName() = when { private fun IrFunction.toInlineClassImplementationName(): Name {
isSpecial -> Name.special(asString() + INLINE_CLASS_IMPL_SUFFIX) val klass = this.parentAsClass!!
else -> Name.identifier(asString() + INLINE_CLASS_IMPL_SUFFIX) val newName = klass.name.asString() + "__" + name.asString() + INLINE_CLASS_IMPL_SUFFIX
return when {
name.isSpecial -> Name.special("<" + newName + ">")
else -> Name.identifier(newName)
}
} }
private fun collectTypeParameters(declaration: IrTypeParametersContainer): List<IrTypeParameter> { private fun collectTypeParameters(declaration: IrTypeParametersContainer): List<IrTypeParameter> {
@@ -292,7 +296,7 @@ class InlineClassLowering(val context: CommonBackendContext) {
private fun createStaticBodilessMethod(function: IrFunction): IrSimpleFunction = private fun createStaticBodilessMethod(function: IrFunction): IrSimpleFunction =
context.irFactory.createStaticFunctionWithReceivers( context.irFactory.createStaticFunctionWithReceivers(
function.parent, function.parent,
function.name.toInlineClassImplementationName(), function.toInlineClassImplementationName(),
function, function,
typeParametersFromContext = collectTypeParameters(function.parentAsClass) typeParametersFromContext = collectTypeParameters(function.parentAsClass)
) )
@@ -284,7 +284,7 @@ fun usefulDeclarations(roots: Iterable<IrDeclaration>, context: JsIrBackendConte
when (expression.symbol) { when (expression.symbol) {
context.intrinsics.jsBoxIntrinsic -> { context.intrinsics.jsBoxIntrinsic -> {
val inlineClass = expression.getTypeArgument(0)!!.getInlinedClass()!! val inlineClass = context.inlineClassesUtils.getInlinedClass(expression.getTypeArgument(0)!!)!!
val constructor = inlineClass.declarations.filterIsInstance<IrConstructor>().single { it.isPrimary } val constructor = inlineClass.declarations.filterIsInstance<IrConstructor>().single { it.isPrimary }
constructor.enqueue("intrinsic: jsBoxIntrinsic") constructor.enqueue("intrinsic: jsBoxIntrinsic")
} }
@@ -6,10 +6,35 @@
package org.jetbrains.kotlin.ir.backend.js package org.jetbrains.kotlin.ir.backend.js
import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.ir.isOverridableOrOverrides
import org.jetbrains.kotlin.ir.backend.js.utils.isDispatchReceiver
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrType
interface JsCommonBackendContext : CommonBackendContext { interface JsCommonBackendContext : CommonBackendContext {
override val mapping: JsMapping override val mapping: JsMapping
val inlineClassesUtils: InlineClassesUtils
val es6mode: Boolean val es6mode: Boolean
get() = false get() = false
} }
interface InlineClassesUtils {
fun isTypeInlined(type: IrType): Boolean
fun shouldValueParameterBeBoxed(parameter: IrValueParameter): Boolean {
val function = parameter.parent as? IrSimpleFunction ?: return false
val klass = function.parent as? IrClass ?: return false
if (!isClassInlineLike(klass)) return false
return parameter.isDispatchReceiver && function.isOverridableOrOverrides
}
fun getInlinedClass(type: IrType): IrClass?
fun isClassInlineLike(klass: IrClass): Boolean
val boxIntrinsic: IrSimpleFunctionSymbol
val unboxIntrinsic: IrSimpleFunctionSymbol
}
@@ -16,9 +16,8 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.* import org.jetbrains.kotlin.ir.*
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.lower.JsInnerClassesSupport import org.jetbrains.kotlin.ir.backend.js.lower.JsInnerClassesSupport
import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames import org.jetbrains.kotlin.ir.backend.js.utils.*
import org.jetbrains.kotlin.ir.builders.declarations.addFunction import org.jetbrains.kotlin.ir.builders.declarations.addFunction
import org.jetbrains.kotlin.ir.backend.js.utils.getInlinedClass
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
@@ -123,6 +122,9 @@ class JsIrBackendContext(
get() = testContainerFuns get() = testContainerFuns
override val mapping = JsMapping() override val mapping = JsMapping()
override val inlineClassesUtils = JsInlineClassesUtils(this)
val innerClassesSupport = JsInnerClassesSupport(mapping, irFactory) val innerClassesSupport = JsInnerClassesSupport(mapping, irFactory)
companion object { companion object {
@@ -196,6 +198,9 @@ class JsIrBackendContext(
override val defaultConstructorMarker = override val defaultConstructorMarker =
symbolTable.referenceClass(context.getJsInternalClass("DefaultConstructorMarker")) symbolTable.referenceClass(context.getJsInternalClass("DefaultConstructorMarker"))
override val throwISE: IrSimpleFunctionSymbol =
symbolTable.referenceSimpleFunction(getFunctions(kotlinPackageFqn.child(Name.identifier("THROW_ISE"))).single())
override val stringBuilder override val stringBuilder
get() = TODO("not implemented") get() = TODO("not implemented")
override val copyRangeTo: Map<ClassDescriptor, IrSimpleFunctionSymbol> override val copyRangeTo: Map<ClassDescriptor, IrSimpleFunctionSymbol>
@@ -226,7 +231,7 @@ class JsIrBackendContext(
} }
override fun unfoldInlineClassType(irType: IrType): IrType? { override fun unfoldInlineClassType(irType: IrType): IrType? {
return irType.getInlinedClass()?.typeWith() return inlineClassesUtils.getInlinedClass(irType)?.typeWith()
} }
override fun shouldGenerateHandlerParameterForDefaultBodyFun() = true override fun shouldGenerateHandlerParameterForDefaultBodyFun() = true
@@ -314,6 +314,12 @@ private val enumUsageLoweringPhase = makeBodyLoweringPhase(
prerequisite = setOf(enumEntryCreateGetInstancesFunsLoweringPhase) prerequisite = setOf(enumEntryCreateGetInstancesFunsLoweringPhase)
) )
private val externalEnumUsageLoweringPhase = makeBodyLoweringPhase(
::ExternalEnumUsagesLowering,
name = "ExternalEnumUsagesLowering",
description = "Replace external enum entry accesses with field accesses"
)
private val enumEntryRemovalLoweringPhase = makeDeclarationTransformerPhase( private val enumEntryRemovalLoweringPhase = makeDeclarationTransformerPhase(
::EnumClassRemoveEntriesLowering, ::EnumClassRemoveEntriesLowering,
name = "EnumEntryRemovalLowering", name = "EnumEntryRemovalLowering",
@@ -535,7 +541,7 @@ private val errorDeclarationLoweringPhase = makeDeclarationTransformerPhase(
) )
private val bridgesConstructionPhase = makeDeclarationTransformerPhase( private val bridgesConstructionPhase = makeDeclarationTransformerPhase(
::BridgesConstruction, ::JsBridgesConstruction,
name = "BridgesConstruction", name = "BridgesConstruction",
description = "Generate bridges", description = "Generate bridges",
prerequisite = setOf(suspendFunctionsLoweringPhase) prerequisite = setOf(suspendFunctionsLoweringPhase)
@@ -712,6 +718,7 @@ val loweringList = listOf<Lowering>(
enumEntryCreateGetInstancesFunsLoweringPhase, enumEntryCreateGetInstancesFunsLoweringPhase,
enumSyntheticFunsLoweringPhase, enumSyntheticFunsLoweringPhase,
enumUsageLoweringPhase, enumUsageLoweringPhase,
externalEnumUsageLoweringPhase,
enumEntryRemovalLoweringPhase, enumEntryRemovalLoweringPhase,
suspendFunctionsLoweringPhase, suspendFunctionsLoweringPhase,
propertyReferenceLoweringPhase, propertyReferenceLoweringPhase,
@@ -7,20 +7,26 @@ package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.lower.AbstractValueUsageTransformer import org.jetbrains.kotlin.backend.common.lower.AbstractValueUsageTransformer
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.getInlinedClass
import org.jetbrains.kotlin.ir.backend.js.utils.isInlined
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrDeclarationParent
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.isPrimitiveArray
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.ir.util.render
// Copied and adapted from Kotlin/Native // Copied and adapted from Kotlin/Native
class AutoboxingTransformer(val context: JsIrBackendContext) : AbstractValueUsageTransformer(context.irBuiltIns), BodyLoweringPass { abstract class AbstractValueUsageLowering(val context: JsCommonBackendContext) : AbstractValueUsageTransformer(context.irBuiltIns),
BodyLoweringPass {
val icUtils = context.inlineClassesUtils
override fun lower(irBody: IrBody, container: IrDeclaration) { override fun lower(irBody: IrBody, container: IrDeclaration) {
// TODO workaround for callable references // TODO workaround for callable references
@@ -35,36 +41,27 @@ class AutoboxingTransformer(val context: JsIrBackendContext) : AbstractValueUsag
irBody.patchDeclarationParents(container as? IrDeclarationParent ?: container.parent) irBody.patchDeclarationParents(container as? IrDeclarationParent ?: container.parent)
} }
private tailrec fun IrExpression.isGetUnit(): Boolean =
when(this) {
is IrContainerExpression ->
when (val lastStmt = this.statements.lastOrNull()) {
is IrExpression -> lastStmt.isGetUnit()
else -> false
}
is IrGetObjectValue -> abstract fun IrExpression.useExpressionAsType(actualType: IrType, expectedType: IrType): IrExpression
this.symbol == irBuiltIns.unitClass
else -> false
}
override fun IrExpression.useAs(type: IrType): IrExpression { override fun IrExpression.useAs(type: IrType): IrExpression {
val actualType = when (this) { val actualType = when (this) {
is IrConstructorCall -> symbol.owner.returnType is IrConstructorCall -> symbol.owner.returnType
is IrCall -> symbol.owner.realOverrideTarget.returnType is IrCall -> symbol.owner.realOverrideTarget.returnType
is IrGetField -> this.symbol.owner.type is IrGetField -> this.symbol.owner.type
is IrTypeOperatorCall -> { is IrTypeOperatorCall -> {
assert(operator == IrTypeOperator.REINTERPRET_CAST) { "Only REINTERPRET_CAST expected at this point" } if (operator == IrTypeOperator.REINTERPRET_CAST) {
this.typeOperand this.typeOperand
} else {
this.type
}
} }
is IrGetValue -> { is IrGetValue -> {
val value = this.symbol.owner val value = this.symbol.owner
if (value is IrValueParameter && value.isDispatchReceiver) { if (value is IrValueParameter && icUtils.shouldValueParameterBeBoxed(value)) {
irBuiltIns.anyNType irBuiltIns.anyType
} else { } else {
this.type this.type
} }
@@ -73,12 +70,60 @@ class AutoboxingTransformer(val context: JsIrBackendContext) : AbstractValueUsag
else -> this.type else -> this.type
} }
return useExpressionAsType(actualType, type)
}
private val IrFunctionAccessExpression.target: IrFunction
get() = when (this) {
is IrConstructorCall -> this.symbol.owner
is IrDelegatingConstructorCall -> this.symbol.owner
is IrCall -> this.callTarget
else -> TODO(this.render())
}
private val IrCall.callTarget: IrFunction
get() = symbol.owner.realOverrideTarget
override fun IrExpression.useAsDispatchReceiver(expression: IrFunctionAccessExpression): IrExpression {
return if (expression.symbol.owner.dispatchReceiverParameter?.let { icUtils.shouldValueParameterBeBoxed(it) } == true)
this.useAs(irBuiltIns.anyType)
else
this.useAsArgument(expression.target.dispatchReceiverParameter!!)
}
override fun IrExpression.useAsExtensionReceiver(expression: IrFunctionAccessExpression): IrExpression {
return this.useAsArgument(expression.target.extensionReceiverParameter!!)
}
override fun IrExpression.useAsValueArgument(
expression: IrFunctionAccessExpression,
parameter: IrValueParameter
): IrExpression {
return this.useAsArgument(expression.target.valueParameters[parameter.index])
}
override fun IrExpression.useAsVarargElement(expression: IrVararg): IrExpression {
return this.useAs(
// Do not box primitive inline classes
if (icUtils.isTypeInlined(type) && !icUtils.isTypeInlined(expression.type) && !expression.type.isPrimitiveArray())
irBuiltIns.anyNType
else
expression.varargElementType
)
}
}
class AutoboxingTransformer(context: JsCommonBackendContext) : AbstractValueUsageLowering(context) {
override fun IrExpression.useExpressionAsType(actualType: IrType, expectedType: IrType): IrExpression {
// // TODO: Default parameters are passed as nulls and they need not to be unboxed. Fix this // // TODO: Default parameters are passed as nulls and they need not to be unboxed. Fix this
if (actualType.makeNotNull().isNothing()) if (actualType.makeNotNull().isNothing())
return this return this
val expectedType = type
if (actualType.isUnit() && !expectedType.isUnit()) { if (actualType.isUnit() && !expectedType.isUnit()) {
// Don't materialize Unit if value is known to be proper Unit on runtime // Don't materialize Unit if value is known to be proper Unit on runtime
if (!this.isGetUnit()) { if (!this.isGetUnit()) {
@@ -87,8 +132,8 @@ class AutoboxingTransformer(val context: JsIrBackendContext) : AbstractValueUsag
} }
} }
val actualInlinedClass = actualType.getInlinedClass() val actualInlinedClass = icUtils.getInlinedClass(actualType)
val expectedInlinedClass = expectedType.getInlinedClass() val expectedInlinedClass = icUtils.getInlinedClass(expectedType)
// Mimicking behaviour of current JS backend // Mimicking behaviour of current JS backend
// TODO: Revisit // TODO: Revisit
@@ -99,8 +144,8 @@ class AutoboxingTransformer(val context: JsIrBackendContext) : AbstractValueUsag
val function = when { val function = when {
actualInlinedClass == null && expectedInlinedClass == null -> return this actualInlinedClass == null && expectedInlinedClass == null -> return this
actualInlinedClass != null && expectedInlinedClass == null -> context.intrinsics.jsBoxIntrinsic actualInlinedClass != null && expectedInlinedClass == null -> icUtils.boxIntrinsic
actualInlinedClass == null && expectedInlinedClass != null -> context.intrinsics.jsUnboxIntrinsic actualInlinedClass == null && expectedInlinedClass != null -> icUtils.unboxIntrinsic
else -> return this else -> return this
} }
@@ -115,14 +160,31 @@ class AutoboxingTransformer(val context: JsIrBackendContext) : AbstractValueUsag
} }
} }
private tailrec fun IrExpression.isGetUnit(): Boolean =
when (this) {
is IrContainerExpression ->
when (val lastStmt = this.statements.lastOrNull()) {
is IrExpression -> lastStmt.isGetUnit()
else -> false
}
is IrGetObjectValue ->
this.symbol == irBuiltIns.unitClass
else -> false
}
private fun buildSafeCall( private fun buildSafeCall(
arg: IrExpression, arg: IrExpression,
actualType: IrType, actualType: IrType,
resultType: IrType, resultType: IrType,
call: (IrExpression) -> IrExpression call: (IrExpression) -> IrExpression
): IrExpression { ): IrExpression {
if (!actualType.isNullable()) // Safe call is only needed if we cast from Nullable type to Nullable type.
// Otherwise, null value cannot occur.
if (!actualType.isNullable() || !resultType.isNullable())
return call(arg) return call(arg)
return JsIrBuilder.run { return JsIrBuilder.run {
// TODO: Set parent of local variables // TODO: Set parent of local variables
val tmp = buildVar(actualType, parent = null, initializer = arg) val tmp = buildVar(actualType, parent = null, initializer = arg)
@@ -144,55 +206,4 @@ class AutoboxingTransformer(val context: JsIrBackendContext) : AbstractValueUsag
) )
} }
} }
}
private val IrFunctionAccessExpression.target: IrFunction
get() = when (this) {
is IrConstructorCall -> this.symbol.owner
is IrDelegatingConstructorCall -> this.symbol.owner
is IrCall -> this.callTarget
else -> TODO(this.render())
}
private val IrCall.callTarget: IrFunction
get() = symbol.owner.realOverrideTarget
override fun IrExpression.useAsDispatchReceiver(expression: IrFunctionAccessExpression): IrExpression {
return this.useAsArgument(expression.target.dispatchReceiverParameter!!)
}
override fun IrExpression.useAsExtensionReceiver(expression: IrFunctionAccessExpression): IrExpression {
return this.useAsArgument(expression.target.extensionReceiverParameter!!)
}
override fun IrExpression.useAsValueArgument(
expression: IrFunctionAccessExpression,
parameter: IrValueParameter
): IrExpression {
return this.useAsArgument(expression.target.valueParameters[parameter.index])
}
override fun IrExpression.useAsVarargElement(expression: IrVararg): IrExpression {
return this.useAs(
// Do not box primitive inline classes
if (this.type.isInlined() && !expression.type.isInlined() && !expression.type.isPrimitiveArray())
irBuiltIns.anyNType
else
expression.varargElementType
)
}
private val IrValueParameter.isDispatchReceiver: Boolean
get() {
val parent = this.parent
if (parent is IrClass)
return true
if (parent is IrFunction && parent.dispatchReceiverParameter == this)
return true
return false
}
}
@@ -15,8 +15,8 @@ import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
import org.jetbrains.kotlin.ir.backend.js.utils.functionSignature
import org.jetbrains.kotlin.ir.backend.js.utils.getJsName import org.jetbrains.kotlin.ir.backend.js.utils.getJsName
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.buildFun import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
@@ -45,10 +45,15 @@ import org.jetbrains.kotlin.ir.util.*
// } // }
// //
@OptIn(ObsoleteDescriptorBasedAPI::class) @OptIn(ObsoleteDescriptorBasedAPI::class)
class BridgesConstruction(val context: JsCommonBackendContext) : DeclarationTransformer { abstract class BridgesConstruction(val context: JsCommonBackendContext) : DeclarationTransformer {
private val specialBridgeMethods = SpecialBridgeMethods(context) private val specialBridgeMethods = SpecialBridgeMethods(context)
abstract fun getFunctionSignature(function: IrSimpleFunction): Any
// Should dispatch receiver type be casted inside a bridge.
open val shouldCastDispatchReceiver: Boolean = false
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? { override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (declaration !is IrSimpleFunction || declaration.isStaticMethodOfClass || declaration.parent !is IrClass) return null if (declaration !is IrSimpleFunction || declaration.isStaticMethodOfClass || declaration.parent !is IrClass) return null
@@ -56,10 +61,6 @@ class BridgesConstruction(val context: JsCommonBackendContext) : DeclarationTran
} }
private fun generateBridges(function: IrSimpleFunction): List<IrDeclaration>? { private fun generateBridges(function: IrSimpleFunction): List<IrDeclaration>? {
// equals(Any?), hashCode(), toString() never need bridges
if (function.isMethodOfAny())
return null
val (specialOverride: IrSimpleFunction?, specialOverrideInfo) = val (specialOverride: IrSimpleFunction?, specialOverrideInfo) =
specialBridgeMethods.findSpecialWithOverride(function) ?: Pair(null, null) specialBridgeMethods.findSpecialWithOverride(function) ?: Pair(null, null)
@@ -83,6 +84,11 @@ class BridgesConstruction(val context: JsCommonBackendContext) : DeclarationTran
continue continue
} }
// Don't build bridges for functions with the same signature.
// TODO: This should be caught earlier in bridgesToGenerate
if (FunctionAndSignature(to.function.realOverrideTarget) == FunctionAndSignature(from.function.realOverrideTarget))
continue
if (from.function.correspondingPropertySymbol != null && from.function.isEffectivelyExternal()) { if (from.function.correspondingPropertySymbol != null && from.function.isEffectivelyExternal()) {
// TODO: Revisit bridges from external properties // TODO: Revisit bridges from external properties
continue continue
@@ -150,7 +156,13 @@ class BridgesConstruction(val context: JsCommonBackendContext) : DeclarationTran
} }
val call = irCall(delegateTo.symbol) val call = irCall(delegateTo.symbol)
call.dispatchReceiver = irGet(irFunction.dispatchReceiverParameter!!) val dispatchReceiver = irGet(irFunction.dispatchReceiverParameter!!)
call.dispatchReceiver = if (shouldCastDispatchReceiver)
irCastIfNeeded(dispatchReceiver, delegateTo.dispatchReceiverParameter!!.type)
else
dispatchReceiver
irFunction.extensionReceiverParameter?.let { irFunction.extensionReceiverParameter?.let {
call.extensionReceiver = irCastIfNeeded(irGet(it), delegateTo.extensionReceiverParameter!!.type) call.extensionReceiver = irCastIfNeeded(irGet(it), delegateTo.extensionReceiverParameter!!.type)
} }
@@ -171,6 +183,25 @@ class BridgesConstruction(val context: JsCommonBackendContext) : DeclarationTran
// TODO: get rid of Unit check // TODO: get rid of Unit check
private fun IrBlockBodyBuilder.irCastIfNeeded(argument: IrExpression, type: IrType): IrExpression = private fun IrBlockBodyBuilder.irCastIfNeeded(argument: IrExpression, type: IrType): IrExpression =
if (argument.type.classifierOrNull == type.classifierOrNull) argument else irAs(argument, type) if (argument.type.classifierOrNull == type.classifierOrNull) argument else irAs(argument, type)
// Wrapper around function that compares and hashCodes it based on signature
// Designed to be used as a Signature type parameter in backend.common.bridges
inner class FunctionAndSignature(val function: IrSimpleFunction) {
// TODO: Use type-upper-bound-based signature instead of Strings
// Currently strings are used for compatibility with a hack-based name generator
private val signature = getFunctionSignature(function)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is BridgesConstruction.FunctionAndSignature) return false
return signature == other.signature
}
override fun hashCode(): Int = signature.hashCode()
}
} }
// Handle for common.bridges // Handle for common.bridges
@@ -187,23 +218,5 @@ data class IrBasedFunctionHandle(val function: IrSimpleFunction) : FunctionHandl
function.overriddenSymbols.map { IrBasedFunctionHandle(it.owner) } function.overriddenSymbols.map { IrBasedFunctionHandle(it.owner) }
} }
// Wrapper around function that compares and hashCodes it based on signature
// Designed to be used as a Signature type parameter in backend.common.bridges
class FunctionAndSignature(val function: IrSimpleFunction) {
// TODO: Use type-upper-bound-based signature instead of Strings
// Currently strings are used for compatibility with a hack-based name generator
private val signature = functionSignature(function)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is FunctionAndSignature) return false
return signature == other.signature
}
override fun hashCode(): Int = signature.hashCode()
}
@@ -17,18 +17,14 @@ import org.jetbrains.kotlin.backend.common.lower.parents
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.toJsArrayLiteral
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor
import org.jetbrains.kotlin.ir.builders.declarations.buildField import org.jetbrains.kotlin.ir.builders.declarations.buildField
import org.jetbrains.kotlin.ir.builders.declarations.buildFun import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.makeNullable import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
@@ -36,7 +32,7 @@ import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
class EnumUsageLowering(val context: JsIrBackendContext) : BodyLoweringPass { class EnumUsageLowering(val context: JsCommonBackendContext) : BodyLoweringPass {
private var IrEnumEntry.getInstanceFun by context.mapping.enumEntryToGetInstanceFun private var IrEnumEntry.getInstanceFun by context.mapping.enumEntryToGetInstanceFun
override fun lower(irBody: IrBody, container: IrDeclaration) { override fun lower(irBody: IrBody, container: IrDeclaration) {
@@ -44,41 +40,13 @@ class EnumUsageLowering(val context: JsIrBackendContext) : BodyLoweringPass {
override fun visitGetEnumValue(expression: IrGetEnumValue): IrExpression { override fun visitGetEnumValue(expression: IrGetEnumValue): IrExpression {
val enumEntry = expression.symbol.owner val enumEntry = expression.symbol.owner
val klass = enumEntry.parent as IrClass val klass = enumEntry.parent as IrClass
return if (klass.isExternal) lowerExternalEnumEntry(enumEntry, klass) else lowerEnumEntry(enumEntry) if (klass.isExternal) return expression
return lowerEnumEntry(enumEntry, klass)
} }
}) })
} }
private fun lowerExternalEnumEntry(enumEntry: IrEnumEntry, klass: IrClass) = private fun lowerEnumEntry(enumEntry: IrEnumEntry, klass: IrClass) =
context.mapping.enumEntryToInstanceField.getOrPut(enumEntry) { createFieldForEntry(enumEntry, klass) }.let {
JsIrBuilder.buildGetField(it.symbol, classAsReceiver(klass), null, klass.defaultType)
}
private fun classAsReceiver(irClass: IrClass): IrExpression {
val intrinsic = context.intrinsics.jsClass
return JsIrBuilder.buildCall(intrinsic, context.irBuiltIns.anyType, listOf(irClass.defaultType))
}
private fun createFieldForEntry(entry: IrEnumEntry, irClass: IrClass): IrField =
context.irFactory.buildField {
startOffset = entry.startOffset
endOffset = entry.endOffset
origin = entry.origin
name = entry.name
type = irClass.defaultType
isFinal = false
isExternal = true
isStatic = true
}.also {
it.parent = irClass
// TODO need a way to emerge local declarations from BodyLoweringPass
stageController.unrestrictDeclarationListsAccess {
irClass.declarations += it
}
}
private fun lowerEnumEntry(enumEntry: IrEnumEntry) =
enumEntry.getInstanceFun!!.run { JsIrBuilder.buildCall(symbol) } enumEntry.getInstanceFun!!.run { JsIrBuilder.buildCall(symbol) }
} }
@@ -313,7 +281,7 @@ class EnumClassConstructorBodyTransformer(val context: JsCommonBackendContext) :
private val IrClass.goodEnum: Boolean private val IrClass.goodEnum: Boolean
get() = isEnumClass && !isExpect && !isEffectivelyExternal() get() = isEnumClass && !isExpect && !isEffectivelyExternal()
class EnumEntryInstancesLowering(val context: JsIrBackendContext) : DeclarationTransformer { class EnumEntryInstancesLowering(val context: JsCommonBackendContext) : DeclarationTransformer {
private var IrEnumEntry.correspondingField by context.mapping.enumEntryToCorrespondingField private var IrEnumEntry.correspondingField by context.mapping.enumEntryToCorrespondingField
@@ -346,7 +314,7 @@ class EnumEntryInstancesLowering(val context: JsIrBackendContext) : DeclarationT
} }
} }
class EnumEntryInstancesBodyLowering(val context: JsIrBackendContext) : BodyLoweringPass { class EnumEntryInstancesBodyLowering(val context: JsCommonBackendContext) : BodyLoweringPass {
private var IrEnumEntry.correspondingField by context.mapping.enumEntryToCorrespondingField private var IrEnumEntry.correspondingField by context.mapping.enumEntryToCorrespondingField
@@ -370,7 +338,7 @@ class EnumEntryInstancesBodyLowering(val context: JsIrBackendContext) : BodyLowe
} }
} }
class EnumClassCreateInitializerLowering(val context: JsIrBackendContext) : DeclarationTransformer { class EnumClassCreateInitializerLowering(val context: JsCommonBackendContext) : DeclarationTransformer {
private var IrEnumEntry.correspondingField by context.mapping.enumEntryToCorrespondingField private var IrEnumEntry.correspondingField by context.mapping.enumEntryToCorrespondingField
private var IrClass.initEntryInstancesFun: IrSimpleFunction? by context.mapping.enumClassToInitEntryInstancesFun private var IrClass.initEntryInstancesFun: IrSimpleFunction? by context.mapping.enumClassToInitEntryInstancesFun
@@ -431,7 +399,7 @@ class EnumClassCreateInitializerLowering(val context: JsIrBackendContext) : Decl
} }
} }
class EnumEntryCreateGetInstancesFunsLowering(val context: JsIrBackendContext) : DeclarationTransformer { class EnumEntryCreateGetInstancesFunsLowering(val context: JsCommonBackendContext) : DeclarationTransformer {
private var IrEnumEntry.correspondingField by context.mapping.enumEntryToCorrespondingField private var IrEnumEntry.correspondingField by context.mapping.enumEntryToCorrespondingField
private var IrClass.initEntryInstancesFun: IrSimpleFunction? by context.mapping.enumClassToInitEntryInstancesFun private var IrClass.initEntryInstancesFun: IrSimpleFunction? by context.mapping.enumClassToInitEntryInstancesFun
@@ -477,7 +445,7 @@ class EnumEntryCreateGetInstancesFunsLowering(val context: JsIrBackendContext) :
} }
} }
class EnumSyntheticFunctionsLowering(val context: JsIrBackendContext) : DeclarationTransformer { class EnumSyntheticFunctionsLowering(val context: JsCommonBackendContext) : DeclarationTransformer {
private var IrEnumEntry.getInstanceFun by context.mapping.enumEntryToGetInstanceFun private var IrEnumEntry.getInstanceFun by context.mapping.enumEntryToGetInstanceFun
@@ -500,7 +468,7 @@ class EnumSyntheticFunctionsLowering(val context: JsIrBackendContext) : Declarat
return null return null
} }
private val throwISESymbol = context.throwISEsymbol private val throwISESymbol = context.ir.symbols.throwISE
private fun createEnumValueOfBody(valueOfFun: IrFunction, irClass: IrClass): IrBlockBody { private fun createEnumValueOfBody(valueOfFun: IrFunction, irClass: IrClass): IrBlockBody {
val nameParameter = valueOfFun.valueParameters[0] val nameParameter = valueOfFun.valueParameters[0]
@@ -521,26 +489,12 @@ class EnumSyntheticFunctionsLowering(val context: JsIrBackendContext) : Declarat
} }
} }
private fun List<IrExpression>.toArrayLiteral(arrayType: IrType, elementType: IrType): IrExpression {
val irVararg = IrVarargImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, arrayType, elementType, this)
return IrCallImpl(
UNDEFINED_OFFSET, UNDEFINED_OFFSET, arrayType,
context.intrinsics.arrayLiteral,
typeArgumentsCount = 0,
valueArgumentsCount = 1
).apply {
putValueArgument(0, irVararg)
}
}
private fun createEnumValuesBody(valuesFun: IrFunction, irClass: IrClass): IrBlockBody { private fun createEnumValuesBody(valuesFun: IrFunction, irClass: IrClass): IrBlockBody {
val backendContext = context
return context.createIrBuilder(valuesFun.symbol).run { return context.createIrBuilder(valuesFun.symbol).run {
irBlockBody { irBlockBody {
val instances = irClass.enumEntries.map { irCall(it.getInstanceFun!!) }
+irReturn( +irReturn(
irClass.enumEntries.map { irCall(it.getInstanceFun!!) } IrVarargImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, valuesFun.returnType, irClass.defaultType, instances)
.toJsArrayLiteral(backendContext, valuesFun.returnType, irClass.defaultType)
) )
} }
} }
@@ -551,7 +505,7 @@ private val IrClass.enumEntries: List<IrEnumEntry>
get() = declarations.filterIsInstance<IrEnumEntry>() get() = declarations.filterIsInstance<IrEnumEntry>()
// Should be applied recursively // Should be applied recursively
class EnumClassRemoveEntriesLowering(val context: JsIrBackendContext) : DeclarationTransformer { class EnumClassRemoveEntriesLowering(val context: JsCommonBackendContext) : DeclarationTransformer {
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? { override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
// Remove IrEnumEntry nodes from class declarations. Replace them with corresponding class declarations (if they have them). // Remove IrEnumEntry nodes from class declarations. Replace them with corresponding class declarations (if they have them).
if (declaration is IrEnumEntry && !declaration.isExpect && !declaration.isEffectivelyExternal()) { if (declaration is IrEnumEntry && !declaration.isExpect && !declaration.isEffectivelyExternal()) {
@@ -560,4 +514,4 @@ class EnumClassRemoveEntriesLowering(val context: JsIrBackendContext) : Declarat
return null return null
} }
} }
@@ -0,0 +1,60 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.getOrPut
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.builders.declarations.buildField
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrGetEnumValue
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
class ExternalEnumUsagesLowering(val context: JsIrBackendContext) : BodyLoweringPass {
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitGetEnumValue(expression: IrGetEnumValue): IrExpression {
val enumEntry = expression.symbol.owner
val klass = enumEntry.parent as IrClass
return if (klass.isExternal) lowerExternalEnumEntry(enumEntry, klass) else expression
}
})
}
private fun lowerExternalEnumEntry(enumEntry: IrEnumEntry, klass: IrClass) =
context.mapping.enumEntryToInstanceField.getOrPut(enumEntry) { createFieldForEntry(enumEntry, klass) }.let {
JsIrBuilder.buildGetField(it.symbol, classAsReceiver(klass), null, klass.defaultType)
}
private fun classAsReceiver(irClass: IrClass): IrExpression {
val intrinsic = context.intrinsics.jsClass
return JsIrBuilder.buildCall(intrinsic, context.irBuiltIns.anyType, listOf(irClass.defaultType))
}
private fun createFieldForEntry(entry: IrEnumEntry, irClass: IrClass): IrField =
context.irFactory.buildField {
startOffset = entry.startOffset
endOffset = entry.endOffset
origin = entry.origin
name = entry.name
type = irClass.defaultType
isFinal = false
isExternal = true
isStatic = true
}.also {
it.parent = irClass
// TODO need a way to emerge local declarations from BodyLoweringPass
stageController.unrestrictDeclarationListsAccess {
irClass.declarations += it
}
}
}
@@ -0,0 +1,16 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
import org.jetbrains.kotlin.ir.backend.js.utils.Signature
import org.jetbrains.kotlin.ir.backend.js.utils.jsFunctionSignature
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
class JsBridgesConstruction(context: JsCommonBackendContext) : BridgesConstruction(context) {
override fun getFunctionSignature(function: IrSimpleFunction): Signature =
jsFunctionSignature(function)
}
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.ir.backend.js.lower package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.DeclarationTransformer import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.export.isExported import org.jetbrains.kotlin.ir.backend.js.export.isExported
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
@@ -13,7 +14,7 @@ import org.jetbrains.kotlin.ir.util.file
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
// Move static member declarations from classes to top level // Move static member declarations from classes to top level
class StaticMembersLowering(val context: JsIrBackendContext) : DeclarationTransformer { class StaticMembersLowering(val context: JsCommonBackendContext) : DeclarationTransformer {
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? { override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
(declaration.parent as? IrClass)?.let { irClass -> (declaration.parent as? IrClass)?.let { irClass ->
val isStatic = when (declaration) { val isStatic = when (declaration) {
@@ -26,7 +27,7 @@ class StaticMembersLowering(val context: JsIrBackendContext) : DeclarationTransf
if (isStatic) { if (isStatic) {
// JsExport might be inherited from parent declaration which would be broken if we move it out of its parent. // JsExport might be inherited from parent declaration which would be broken if we move it out of its parent.
// Marking declaration as exported explicitly. // Marking declaration as exported explicitly.
if (declaration.isExported(context)) { if (context is JsIrBackendContext && declaration.isExported(context)) {
context.additionalExportedDeclarations.add(declaration) context.additionalExportedDeclarations.add(declaration)
} }
var extractedUnder = declaration var extractedUnder = declaration
@@ -10,7 +10,6 @@ import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext 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.JsIrArithBuilder
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.getInlinedClass
import org.jetbrains.kotlin.ir.backend.js.utils.isPure import org.jetbrains.kotlin.ir.backend.js.utils.isPure
import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.declarations.IrDeclaration
@@ -64,6 +63,8 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : BodyLoweringPass {
private val litFalse: IrExpression get() = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, false) private val litFalse: IrExpression get() = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, false)
private val litNull: IrExpression get() = JsIrBuilder.buildNull(context.irBuiltIns.nothingNType) private val litNull: IrExpression get() = JsIrBuilder.buildNull(context.irBuiltIns.nothingNType)
private val icUtils = context.inlineClassesUtils
override fun lower(irBody: IrBody, container: IrDeclaration) { override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transformChildren(object : IrElementTransformer<IrDeclarationParent> { irBody.transformChildren(object : IrElementTransformer<IrDeclarationParent> {
override fun visitDeclaration(declaration: IrDeclarationBase, data: IrDeclarationParent) = override fun visitDeclaration(declaration: IrDeclarationBase, data: IrDeclarationParent) =
@@ -102,7 +103,7 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : BodyLoweringPass {
} }
private fun needBoxingOrUnboxing(fromType: IrType, toType: IrType): Boolean { private fun needBoxingOrUnboxing(fromType: IrType, toType: IrType): Boolean {
return ((fromType.getInlinedClass() != null) xor (toType.getInlinedClass() != null)) || (fromType.isUnit() && !toType.isUnit()) return ((icUtils.getInlinedClass(fromType) != null) xor (icUtils.getInlinedClass(toType) != null)) || (fromType.isUnit() && !toType.isUnit())
} }
private fun IrTypeOperatorCall.wrapWithUnsafeCast(arg: IrExpression): IrExpression { private fun IrTypeOperatorCall.wrapWithUnsafeCast(arg: IrExpression): IrExpression {
@@ -9,7 +9,6 @@ import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.utils.getInlinedClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
@@ -32,6 +31,8 @@ private class VarargTransformer(
val context: JsIrBackendContext val context: JsIrBackendContext
) : IrElementTransformerVoid() { ) : IrElementTransformerVoid() {
fun IrType.getInlinedClass() = context.inlineClassesUtils.getInlinedClass(this)
@OptIn(ObsoleteDescriptorBasedAPI::class) @OptIn(ObsoleteDescriptorBasedAPI::class)
private fun List<IrExpression>.toArrayLiteral(type: IrType, varargElementType: IrType): IrExpression { private fun List<IrExpression>.toArrayLiteral(type: IrType, varargElementType: IrType): IrExpression {
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.isUnit
import org.jetbrains.kotlin.ir.util.explicitParameters import org.jetbrains.kotlin.ir.util.explicitParameters
import org.jetbrains.kotlin.ir.util.patchDeclarationParents import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.ir.visitors.* import org.jetbrains.kotlin.ir.visitors.*
@@ -225,6 +226,13 @@ class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunct
return result return result
} }
private fun needUnboxingOrUnit(fromType: IrType, toType: IrType): Boolean {
val icUtils = context.inlineClassesUtils
return (icUtils.getInlinedClass(fromType) == null && icUtils.getInlinedClass(toType) != null) ||
(fromType.isUnit() && !toType.isUnit())
}
override fun IrBuilderWithScope.generateDelegatedCall(expectedType: IrType, delegatingCall: IrExpression): IrExpression { override fun IrBuilderWithScope.generateDelegatedCall(expectedType: IrType, delegatingCall: IrExpression): IrExpression {
val fromType = (delegatingCall as? IrCall)?.symbol?.owner?.returnType ?: delegatingCall.type val fromType = (delegatingCall as? IrCall)?.symbol?.owner?.returnType ?: delegatingCall.type
if (!needUnboxingOrUnit(fromType, expectedType)) return delegatingCall if (!needUnboxingOrUnit(fromType, expectedType)) return delegatingCall
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.ir.backend.js.lower.coroutines package org.jetbrains.kotlin.ir.backend.js.lower.coroutines
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.ir.isElseBranch import org.jetbrains.kotlin.backend.common.ir.isElseBranch
import org.jetbrains.kotlin.backend.common.ir.isSuspend import org.jetbrains.kotlin.backend.common.ir.isSuspend
import org.jetbrains.kotlin.backend.common.peek import org.jetbrains.kotlin.backend.common.peek
@@ -14,9 +13,9 @@ import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.isPure import org.jetbrains.kotlin.ir.backend.js.utils.isPure
import org.jetbrains.kotlin.ir.backend.js.utils.getInlinedClass
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
@@ -69,7 +68,7 @@ class DispatchPointTransformer(val action: (SuspendState) -> IrExpression) : IrE
class StateMachineBuilder( class StateMachineBuilder(
private val suspendableNodes: MutableSet<IrElement>, private val suspendableNodes: MutableSet<IrElement>,
val context: CommonBackendContext, val context: JsCommonBackendContext,
val function: IrFunctionSymbol, val function: IrFunctionSymbol,
private val rootLoop: IrLoop, private val rootLoop: IrLoop,
private val exceptionSymbolGetter: IrSimpleFunction, private val exceptionSymbolGetter: IrSimpleFunction,
@@ -279,7 +278,7 @@ class StateMachineBuilder(
if (expression.isSuspend) { if (expression.isSuspend) {
val result = lastExpression() val result = lastExpression()
val expectedType = expression.symbol.owner.returnType val expectedType = expression.symbol.owner.returnType
val isInlineClassExpected = expectedType.getInlinedClass() != null val isInlineClassExpected = context.inlineClassesUtils.getInlinedClass(expectedType) != null
val continueState = SuspendState(unit) val continueState = SuspendState(unit)
val unboxState = if (isInlineClassExpected) SuspendState(unit) else null val unboxState = if (isInlineClassExpected) SuspendState(unit) else null
@@ -9,7 +9,6 @@ import org.jetbrains.kotlin.backend.common.ir.isSuspend
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.getInlinedClass
import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
@@ -106,9 +105,4 @@ class LiveLocalsTransformer(
JsIrBuilder.buildComposite(declaration.type) JsIrBuilder.buildComposite(declaration.type)
} }
} }
}
internal fun needUnboxingOrUnit(fromType: IrType, toType: IrType): Boolean {
return (fromType.getInlinedClass() == null && toType.getInlinedClass() != null) ||
(fromType.isUnit() && !toType.isUnit())
} }
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext 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.JsGenerationContext
import org.jetbrains.kotlin.ir.backend.js.utils.Namer import org.jetbrains.kotlin.ir.backend.js.utils.Namer
import org.jetbrains.kotlin.ir.backend.js.utils.getInlinedClass
import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
@@ -27,6 +26,7 @@ typealias IrCallTransformer = (IrCall, context: JsGenerationContext) -> JsExpres
class JsIntrinsicTransformers(backendContext: JsIrBackendContext) { class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
private val transformers: Map<IrSymbol, IrCallTransformer> private val transformers: Map<IrSymbol, IrCallTransformer>
val icUtils = backendContext.inlineClassesUtils
init { init {
val intrinsics = backendContext.intrinsics val intrinsics = backendContext.intrinsics
@@ -188,14 +188,14 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
add(intrinsics.jsBoxIntrinsic) { call, context -> add(intrinsics.jsBoxIntrinsic) { call, context ->
val arg = translateCallArguments(call, context).single() val arg = translateCallArguments(call, context).single()
val inlineClass = call.getTypeArgument(0)!!.getInlinedClass()!! val inlineClass = icUtils.getInlinedClass(call.getTypeArgument(0)!!)!!
val constructor = inlineClass.declarations.filterIsInstance<IrConstructor>().single { it.isPrimary } val constructor = inlineClass.declarations.filterIsInstance<IrConstructor>().single { it.isPrimary }
JsNew(context.getNameForConstructor(constructor).makeRef(), listOf(arg)) JsNew(context.getNameForConstructor(constructor).makeRef(), listOf(arg))
} }
add(intrinsics.jsUnboxIntrinsic) { call, context -> add(intrinsics.jsUnboxIntrinsic) { call, context ->
val arg = translateCallArguments(call, context).single() val arg = translateCallArguments(call, context).single()
val inlineClass = call.getTypeArgument(1)!!.getInlinedClass()!! val inlineClass = icUtils.getInlinedClass(call.getTypeArgument(1)!!)!!
val field = getInlineClassBackingField(inlineClass) val field = getInlineClassBackingField(inlineClass)
val fieldName = context.getNameForField(field) val fieldName = context.getNameForField(field)
JsNameRef(fieldName, arg) JsNameRef(fieldName, arg)
@@ -45,7 +45,7 @@ private fun IrClassifierSymbol.asString() = when (this) {
/** /**
* Returns inline class for given class or null of type is not inlined * Returns inline class for given class or null of type is not inlined
*/ */
fun IrType.getInlinedClass(): IrClass? { fun IrType.getJsInlinedClass(): IrClass? {
if (this is IrSimpleType) { if (this is IrSimpleType) {
val erased = erase(this) ?: return null val erased = erase(this) ?: return null
if (erased.isInline) { if (erased.isInline) {
@@ -58,7 +58,7 @@ fun IrType.getInlinedClass(): IrClass? {
return null return null
} }
fieldInlinedClass = fieldType.getInlinedClass() ?: break fieldInlinedClass = fieldType.getJsInlinedClass() ?: break
} }
} }
@@ -68,8 +68,6 @@ fun IrType.getInlinedClass(): IrClass? {
return null return null
} }
fun IrType.isInlined(): Boolean = this.getInlinedClass() != null
tailrec fun erase(type: IrType): IrClass? { tailrec fun erase(type: IrType): IrClass? {
val classifier = type.classifierOrFail val classifier = type.classifierOrFail
@@ -0,0 +1,33 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.backend.js.utils
import org.jetbrains.kotlin.ir.backend.js.InlineClassesUtils
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.isMarkedNullable
import org.jetbrains.kotlin.ir.util.getInlineClassUnderlyingType
class JsInlineClassesUtils(val context: JsIrBackendContext) : InlineClassesUtils {
override fun isTypeInlined(type: IrType): Boolean {
return getInlinedClass(type) != null
}
override fun getInlinedClass(type: IrType): IrClass? =
type.getJsInlinedClass()
override fun isClassInlineLike(klass: IrClass): Boolean =
klass.isInline
override val boxIntrinsic: IrSimpleFunctionSymbol
get() = context.intrinsics.jsBoxIntrinsic
override val unboxIntrinsic: IrSimpleFunctionSymbol
get() = context.intrinsics.jsUnboxIntrinsic
}
@@ -104,7 +104,7 @@ fun fieldSignature(field: IrField): Signature {
return BackingFieldSignature(field) return BackingFieldSignature(field)
} }
fun functionSignature(declaration: IrFunction): Signature { fun jsFunctionSignature(declaration: IrFunction): Signature {
require(!declaration.isStaticMethodOfClass) require(!declaration.isStaticMethodOfClass)
require(declaration.dispatchReceiverParameter != null) require(declaration.dispatchReceiverParameter != null)
@@ -143,7 +143,7 @@ fun functionSignature(declaration: IrFunction): Signature {
declaration.returnType.let { declaration.returnType.let {
// Return type is only used in signature for inline class and Unit types because // Return type is only used in signature for inline class and Unit types because
// they are binary incompatible with supertypes. // they are binary incompatible with supertypes.
if (it.isInlined() || it.isUnit()) { if (it.getJsInlinedClass() != null || it.isUnit()) {
nameBuilder.append("_ret$${it.asString()}") nameBuilder.append("_ret$${it.asString()}")
} }
} }
@@ -286,7 +286,7 @@ class NameTables(
} }
private fun generateNameForMemberFunction(declaration: IrSimpleFunction) { private fun generateNameForMemberFunction(declaration: IrSimpleFunction) {
when (val signature = functionSignature(declaration)) { when (val signature = jsFunctionSignature(declaration)) {
is StableNameSignature -> memberNames.declareStableName(signature, signature.name) is StableNameSignature -> memberNames.declareStableName(signature, signature.name)
is ParameterTypeBasedSignature -> memberNames.declareFreshName(signature, signature.suggestedName) is ParameterTypeBasedSignature -> memberNames.declareFreshName(signature, signature.suggestedName)
} }
@@ -335,7 +335,7 @@ class NameTables(
} }
fun getNameForMemberFunction(function: IrSimpleFunction): String { fun getNameForMemberFunction(function: IrSimpleFunction): String {
val signature = functionSignature(function) val signature = jsFunctionSignature(function)
val name = memberNames.names[signature] ?: mappedNames[mapToKey(signature)] val name = memberNames.names[signature] ?: mappedNames[mapToKey(signature)]
// TODO Add a compiler flag, which enables this behaviour // TODO Add a compiler flag, which enables this behaviour
@@ -79,4 +79,14 @@ fun IrExpression?.isPure(anyVariable: Boolean, checkFields: Boolean = true): Boo
} }
return false return false
} }
val IrValueDeclaration.isDispatchReceiver: Boolean
get() {
val parent = this.parent
if (parent is IrClass)
return true
if (parent is IrFunction && parent.dispatchReceiverParameter == this)
return true
return false
}
@@ -12,6 +12,7 @@ dependencies {
compile(project(":compiler:ir.backend.common")) compile(project(":compiler:ir.backend.common"))
compile(project(":compiler:ir.serialization.common")) compile(project(":compiler:ir.serialization.common"))
compile(project(":compiler:ir.serialization.js")) compile(project(":compiler:ir.serialization.js"))
compile(project(":compiler:ir.tree.persistent"))
compile(project(":js:js.ast")) compile(project(":js:js.ast"))
compile(project(":js:js.frontend")) compile(project(":js:js.frontend"))
compile(project(":compiler:backend.js")) compile(project(":compiler:backend.js"))
@@ -7,24 +7,36 @@ package org.jetbrains.kotlin.backend.wasm
import org.jetbrains.kotlin.backend.common.ir.Ir import org.jetbrains.kotlin.backend.common.ir.Ir
import org.jetbrains.kotlin.backend.common.ir.Symbols import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.backend.common.ir.addChild
import org.jetbrains.kotlin.backend.wasm.lower.WasmSharedVariablesManager
import org.jetbrains.kotlin.backend.wasm.utils.WasmInlineClassesUtils
import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.impl.EmptyPackageFragmentDescriptor
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.SourceManager
import org.jetbrains.kotlin.ir.SourceRangeInfo
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
import org.jetbrains.kotlin.ir.backend.js.JsMapping import org.jetbrains.kotlin.ir.backend.js.JsMapping
import org.jetbrains.kotlin.ir.backend.js.JsSharedVariablesManager import org.jetbrains.kotlin.ir.backend.js.JsSharedVariablesManager
import org.jetbrains.kotlin.ir.backend.js.lower.JsInnerClassesSupport import org.jetbrains.kotlin.ir.backend.js.lower.JsInnerClassesSupport
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
import org.jetbrains.kotlin.ir.declarations.persistent.PersistentIrFactory import org.jetbrains.kotlin.ir.declarations.persistent.PersistentIrFactory
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.impl.DescriptorlessExternalPackageFragmentSymbol import org.jetbrains.kotlin.ir.symbols.impl.DescriptorlessExternalPackageFragmentSymbol
import org.jetbrains.kotlin.ir.util.IdSignature import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
class WasmBackendContext( class WasmBackendContext(
val module: ModuleDescriptor, val module: ModuleDescriptor,
@@ -43,10 +55,12 @@ class WasmBackendContext(
override val irFactory: IrFactory = PersistentIrFactory override val irFactory: IrFactory = PersistentIrFactory
// Place to store declarations excluded from code generation // Place to store declarations excluded from code generation
val excludedDeclarations: IrPackageFragment by lazy { private val excludedDeclarations = mutableMapOf<FqName, IrPackageFragment>()
fun getExcludedPackageFragment(fqName: FqName): IrPackageFragment = excludedDeclarations.getOrPut(fqName) {
IrExternalPackageFragmentImpl( IrExternalPackageFragmentImpl(
DescriptorlessExternalPackageFragmentSymbol(), DescriptorlessExternalPackageFragmentSymbol(),
FqName("kotlin") fqName
) )
} }
@@ -54,14 +68,44 @@ class WasmBackendContext(
val innerClassesSupport = JsInnerClassesSupport(mapping, irFactory) val innerClassesSupport = JsInnerClassesSupport(mapping, irFactory)
val objectToGetInstanceFunction = mutableMapOf<IrClassSymbol, IrSimpleFunction>()
override val internalPackageFqn = FqName("kotlin.wasm") override val internalPackageFqn = FqName("kotlin.wasm")
private val internalPackageFragment = IrExternalPackageFragmentImpl.createEmptyExternalPackageFragment( private val internalPackageFragmentDescriptor = EmptyPackageFragmentDescriptor(builtIns.builtInsModule, FqName("kotlin.wasm.internal"))
builtIns.builtInsModule, FqName("kotlin.wasm.internal") // TODO: Merge with JS IR Backend context lazy file
) val internalPackageFragment by lazy {
IrFileImpl(object : SourceManager.FileEntry {
override val name = "<implicitDeclarations>"
override val maxOffset = UNDEFINED_OFFSET
override val sharedVariablesManager = JsSharedVariablesManager(TODO("...")) override fun getSourceRangeInfo(beginOffset: Int, endOffset: Int) =
SourceRangeInfo(
"",
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
UNDEFINED_OFFSET
)
override fun getLineNumber(offset: Int) = UNDEFINED_OFFSET
override fun getColumnNumber(offset: Int) = UNDEFINED_OFFSET
}, internalPackageFragmentDescriptor).also {
irModuleFragment.files += it
}
}
val startFunction = irFactory.buildFun {
name = Name.identifier("startFunction")
returnType = irBuiltIns.unitType
}.apply {
body = irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET)
internalPackageFragment.addChild(this)
}
override val sharedVariablesManager =
WasmSharedVariablesManager(this, irBuiltIns, internalPackageFragment)
val wasmSymbols: WasmSymbols = WasmSymbols(this@WasmBackendContext, symbolTable) val wasmSymbols: WasmSymbols = WasmSymbols(this@WasmBackendContext, symbolTable)
override val ir = object : Ir<WasmBackendContext>(this, irModuleFragment) { override val ir = object : Ir<WasmBackendContext>(this, irModuleFragment) {
@@ -69,6 +113,8 @@ class WasmBackendContext(
override fun shouldGenerateHandlerParameterForDefaultBodyFun() = true override fun shouldGenerateHandlerParameterForDefaultBodyFun() = true
} }
override val inlineClassesUtils = WasmInlineClassesUtils(wasmSymbols)
override fun log(message: () -> String) { override fun log(message: () -> String) {
/*TODO*/ /*TODO*/
if (inVerbosePhase) print(message()) if (inVerbosePhase) print(message())
@@ -5,23 +5,16 @@
package org.jetbrains.kotlin.backend.wasm package org.jetbrains.kotlin.backend.wasm
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.lower.* import org.jetbrains.kotlin.backend.common.lower.*
import org.jetbrains.kotlin.backend.common.lower.inline.FunctionInlining import org.jetbrains.kotlin.backend.common.lower.inline.FunctionInlining
import org.jetbrains.kotlin.backend.common.phaser.* import org.jetbrains.kotlin.backend.common.phaser.*
import org.jetbrains.kotlin.backend.common.runOnFilePostfix import org.jetbrains.kotlin.backend.wasm.lower.*
import org.jetbrains.kotlin.backend.wasm.lower.BuiltInsLowering
import org.jetbrains.kotlin.backend.wasm.lower.WasmBlockDecomposerLowering
import org.jetbrains.kotlin.backend.wasm.lower.excludeDeclarationsFromCodegen
import org.jetbrains.kotlin.ir.backend.js.lower.* import org.jetbrains.kotlin.ir.backend.js.lower.*
import org.jetbrains.kotlin.ir.backend.js.lower.inline.RemoveInlineFunctionsWithReifiedTypeParametersLowering import org.jetbrains.kotlin.ir.backend.js.lower.inline.RemoveInlineFunctionsWithReifiedTypeParametersLowering
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.util.patchDeclarationParents import org.jetbrains.kotlin.ir.util.patchDeclarationParents
private fun ClassLoweringPass.runOnFilesPostfix(moduleFragment: IrModuleFragment) = moduleFragment.files.forEach { runOnFilePostfix(it) }
private fun makeWasmModulePhase( private fun makeWasmModulePhase(
lowering: (WasmBackendContext) -> FileLoweringPass, lowering: (WasmBackendContext) -> FileLoweringPass,
name: String, name: String,
@@ -60,6 +53,12 @@ private val expectDeclarationsRemovingPhase = makeWasmModulePhase(
description = "Remove expect declaration from module fragment" description = "Remove expect declaration from module fragment"
) )
private val stringConstructorLowering = makeWasmModulePhase(
::SimpleStringConcatenationLowering,
name = "StringConcatenation",
description = "String concatenation lowering"
)
private val lateinitNullableFieldsPhase = makeWasmModulePhase( private val lateinitNullableFieldsPhase = makeWasmModulePhase(
::NullableFieldsForLateinitCreationLowering, ::NullableFieldsForLateinitCreationLowering,
name = "LateinitNullableFields", name = "LateinitNullableFields",
@@ -86,12 +85,6 @@ private val provisionalFunctionExpressionPhase = makeWasmModulePhase(
description = "Transform IrFunctionExpression to a local function reference" description = "Transform IrFunctionExpression to a local function reference"
) )
private val arrayConstructorPhase = makeWasmModulePhase(
::ArrayConstructorLowering,
name = "ArrayConstructor",
description = "Transform `Array(size) { index -> value }` into a loop"
)
private val functionInliningPhase = makeCustomWasmModulePhase( private val functionInliningPhase = makeCustomWasmModulePhase(
{ context, module -> { context, module ->
FunctionInlining(context).inline(module) FunctionInlining(context).inline(module)
@@ -112,7 +105,7 @@ private val removeInlineFunctionsWithReifiedTypeParametersLoweringPhase = makeWa
private val tailrecLoweringPhase = makeWasmModulePhase( private val tailrecLoweringPhase = makeWasmModulePhase(
::TailrecLowering, ::TailrecLowering,
name = "TailrecLowering", name = "TailrecLowering",
description = "Replace `tailrec` callsites with equivalent loop" description = "Replace `tailrec` call sites with equivalent loop"
) )
private val enumClassConstructorLoweringPhase = makeWasmModulePhase( private val enumClassConstructorLoweringPhase = makeWasmModulePhase(
@@ -121,6 +114,62 @@ private val enumClassConstructorLoweringPhase = makeWasmModulePhase(
description = "Transform Enum Class into regular Class" description = "Transform Enum Class into regular Class"
) )
private val enumClassConstructorBodyLoweringPhase = makeWasmModulePhase(
::EnumClassConstructorBodyTransformer,
name = "EnumClassConstructorBodyLowering",
description = "Transform Enum Class into regular Class"
)
private val enumEntryInstancesLoweringPhase = makeWasmModulePhase(
::EnumEntryInstancesLowering,
name = "EnumEntryInstancesLowering",
description = "Create instance variable for each enum entry initialized with `null`",
prerequisite = setOf(enumClassConstructorLoweringPhase)
)
private val enumEntryInstancesBodyLoweringPhase = makeWasmModulePhase(
::EnumEntryInstancesBodyLowering,
name = "EnumEntryInstancesBodyLowering",
description = "Insert enum entry field initialization into corresponding class constructors",
prerequisite = setOf(enumEntryInstancesLoweringPhase)
)
private val enumClassCreateInitializerLoweringPhase = makeWasmModulePhase(
::EnumClassCreateInitializerLowering,
name = "EnumClassCreateInitializerLowering",
description = "Create initializer for enum entries",
prerequisite = setOf(enumClassConstructorLoweringPhase)
)
private val enumEntryCreateGetInstancesFunsLoweringPhase = makeWasmModulePhase(
::EnumEntryCreateGetInstancesFunsLowering,
name = "EnumEntryCreateGetInstancesFunsLowering",
description = "Create enumEntry_getInstance functions",
prerequisite = setOf(enumClassConstructorLoweringPhase)
)
private val enumSyntheticFunsLoweringPhase = makeWasmModulePhase(
::EnumSyntheticFunctionsLowering,
name = "EnumSyntheticFunctionsLowering",
description = "Implement `valueOf` and `values`",
prerequisite = setOf(enumClassConstructorLoweringPhase)
)
private val enumUsageLoweringPhase = makeWasmModulePhase(
::EnumUsageLowering,
name = "EnumUsageLowering",
description = "Replace enum access with invocation of corresponding function",
prerequisite = setOf(enumEntryCreateGetInstancesFunsLoweringPhase)
)
private val enumEntryRemovalLoweringPhase = makeWasmModulePhase(
::EnumClassRemoveEntriesLowering,
name = "EnumEntryRemovalLowering",
description = "Replace enum entry with corresponding class",
prerequisite = setOf(enumUsageLoweringPhase)
)
private val sharedVariablesLoweringPhase = makeWasmModulePhase( private val sharedVariablesLoweringPhase = makeWasmModulePhase(
::SharedVariablesLowering, ::SharedVariablesLowering,
@@ -128,6 +177,12 @@ private val sharedVariablesLoweringPhase = makeWasmModulePhase(
description = "Box captured mutable variables" description = "Box captured mutable variables"
) )
private val callableReferencePhase = makeWasmModulePhase(
::WasmCallableReferenceLowering,
name = "WasmCallableReferenceLowering",
description = "Handle callable references"
)
private val localDelegatedPropertiesLoweringPhase = makeWasmModulePhase( private val localDelegatedPropertiesLoweringPhase = makeWasmModulePhase(
{ LocalDelegatedPropertiesLowering() }, { LocalDelegatedPropertiesLowering() },
name = "LocalDelegatedPropertiesLowering", name = "LocalDelegatedPropertiesLowering",
@@ -183,7 +238,7 @@ private val defaultArgumentPatchOverridesPhase = makeWasmModulePhase(
private val defaultParameterInjectorPhase = makeWasmModulePhase( private val defaultParameterInjectorPhase = makeWasmModulePhase(
{ context -> DefaultParameterInjector(context, skipExternalMethods = true) }, { context -> DefaultParameterInjector(context, skipExternalMethods = true) },
name = "DefaultParameterInjector", name = "DefaultParameterInjector",
description = "Replace callsite with default parameters with corresponding stub function", description = "Replace call site with default parameters with corresponding stub function",
prerequisite = setOf(innerClassesLoweringPhase) prerequisite = setOf(innerClassesLoweringPhase)
) )
@@ -193,18 +248,6 @@ private val defaultParameterCleanerPhase = makeWasmModulePhase(
description = "Clean default parameters up" description = "Clean default parameters up"
) )
//private val jsDefaultCallbackGeneratorPhase = makeJsModulePhase(
// ::JsDefaultCallbackGenerator,
// name = "JsDefaultCallbackGenerator",
// description = "Build binding for super calls with default parameters"
//)
//private val varargLoweringPhase = makeJsModulePhase(
// ::VarargLowering,
// name = "VarargLowering",
// description = "Lower vararg arguments"
//)
private val propertiesLoweringPhase = makeWasmModulePhase( private val propertiesLoweringPhase = makeWasmModulePhase(
{ PropertiesLowering() }, { PropertiesLowering() },
name = "PropertiesLowering", name = "PropertiesLowering",
@@ -254,7 +297,7 @@ private val returnableBlockLoweringPhase = makeWasmModulePhase(
) )
private val bridgesConstructionPhase = makeWasmModulePhase( private val bridgesConstructionPhase = makeWasmModulePhase(
::BridgesConstruction, ::WasmBridgesConstruction,
name = "BridgesConstruction", name = "BridgesConstruction",
description = "Generate bridges" description = "Generate bridges"
) )
@@ -271,65 +314,57 @@ private val inlineClassUsageLoweringPhase = makeWasmModulePhase(
description = "Handle inline class usages" description = "Handle inline class usages"
) )
//private val autoboxingTransformerPhase = makeJsModulePhase( private val autoboxingTransformerPhase = makeWasmModulePhase(
// ::AutoboxingTransformer, { context -> AutoboxingTransformer(context) },
// name = "AutoboxingTransformer", name = "AutoboxingTransformer",
// description = "Insert box/unbox intrinsics" description = "Insert box/unbox intrinsics"
//) )
private val blockDecomposerLoweringPhase = makeCustomWasmModulePhase( private val wasmNullSpecializationLowering = makeWasmModulePhase(
{ context, module -> { context -> WasmNullCoercingLowering(context) },
WasmBlockDecomposerLowering(context).lower(module) name = "WasmNullCoercingLowering",
module.patchDeclarationParents() description = "Specialize assigning Nothing? values to other types."
}, )
name = "BlockDecomposerLowering",
description = "Transform statement-like-expression nodes into pure-statement to make it easily transform into JS" private val staticMembersLoweringPhase = makeWasmModulePhase(
::StaticMembersLowering,
name = "StaticMembersLowering",
description = "Move static member declarations to top-level"
)
private val wasmVarargExpressionLoweringPhase = makeWasmModulePhase(
::WasmVarargExpressionLowering,
name = "WasmVarargExpressionLowering",
description = "Lower varargs"
)
private val wasmThrowDebugLoweringPhase = makeWasmModulePhase(
::WasmThrowDebugLowering,
name = "WasmThrowDebugLowering",
description = "Instrument throws with debug print information"
)
private val fieldInitializersLoweringPhase = makeWasmModulePhase(
::FieldInitializersLowering,
name = "FieldInitializersLowering",
description = "Move field initializers to start function"
)
private val builtInsLoweringPhase0 = makeWasmModulePhase(
::BuiltInsLowering,
name = "BuiltInsLowering0",
description = "Lower IR builtins 0"
) )
//private val classReferenceLoweringPhase = makeJsModulePhase(
// ::ClassReferenceLowering,
// name = "ClassReferenceLowering",
// description = "Handle class references"
//)
//
//private val primitiveCompanionLoweringPhase = makeJsModulePhase(
// ::PrimitiveCompanionLowering,
// name = "PrimitiveCompanionLowering",
// description = "Replace common companion object access with platform one"
//)
//
//private val constLoweringPhase = makeJsModulePhase(
// ::ConstLowering,
// name = "ConstLowering",
// description = "Wrap Long and Char constants into constructor invocation"
//)
//
//private val callsLoweringPhase = makeJsModulePhase(
// ::CallsLowering,
// name = "CallsLowering",
// description = "Handle intrinsics"
//)
//
//private val testGenerationPhase = makeJsModulePhase(
// ::TestGenerator,
// name = "TestGenerationLowering",
// description = "Generate invocations to kotlin.test suite and test functions"
//)
//
//private val staticMembersLoweringPhase = makeWasmModulePhase(
// ::StaticMembersLowering,
// name = "StaticMembersLowering",
// description = "Move static member declarations to top-level"
//)
private val builtInsLoweringPhase = makeWasmModulePhase( private val builtInsLoweringPhase = makeWasmModulePhase(
::BuiltInsLowering, ::BuiltInsLowering,
name = "BuiltInsLowering", name = "BuiltInsLowering",
description = "Lower IR buildins" description = "Lower IR builtins"
) )
private val objectDeclarationLoweringPhase = makeWasmModulePhase( private val objectDeclarationLoweringPhase = makeWasmModulePhase(
::ObjectUsageLowering, ::ObjectDeclarationLowering,
name = "ObjectDeclarationLowering", name = "ObjectDeclarationLowering",
description = "Create lazy object instance generator functions" description = "Create lazy object instance generator functions"
) )
@@ -340,26 +375,52 @@ private val objectUsageLoweringPhase = makeWasmModulePhase(
description = "Transform IrGetObjectValue into instance generator call" description = "Transform IrGetObjectValue into instance generator call"
) )
private val typeOperatorLoweringPhase = makeWasmModulePhase(
::WasmTypeOperatorLowering,
name = "TypeOperatorLowering",
description = "Lower IrTypeOperator with corresponding logic"
)
private val genericReturnTypeLowering = makeWasmModulePhase(
::GenericReturnTypeLowering,
name = "GenericReturnTypeLowering",
description = "Cast calls to functions with generic return types"
)
private val eraseVirtualDispatchReceiverParametersTypes = makeWasmModulePhase(
::EraseVirtualDispatchReceiverParametersTypes,
name = "EraseVirtualDispatchReceiverParametersTypes",
description = "Erase types of virtual dispatch receivers to Any"
)
private val virtualDispatchReceiverExtractionPhase = makeWasmModulePhase(
::VirtualDispatchReceiverExtraction,
name = "VirtualDispatchReceiverExtraction",
description = "Eliminate side-effects in dispatch receivers of virtual function calls"
)
val wasmPhases = NamedCompilerPhase( val wasmPhases = NamedCompilerPhase(
name = "IrModuleLowering", name = "IrModuleLowering",
description = "IR module lowering", description = "IR module lowering",
lower = validateIrBeforeLowering then lower = validateIrBeforeLowering then
excludeDeclarationsFromCodegenPhase then excludeDeclarationsFromCodegenPhase then
expectDeclarationsRemovingPhase then expectDeclarationsRemovingPhase then
provisionalFunctionExpressionPhase then
// TODO: Need some helpers from stdlib // TODO: Need some helpers from stdlib
// arrayConstructorPhase then // arrayConstructorPhase then
functionInliningPhase then functionInliningPhase then
provisionalFunctionExpressionPhase then
lateinitNullableFieldsPhase then lateinitNullableFieldsPhase then
lateinitDeclarationLoweringPhase then lateinitDeclarationLoweringPhase then
lateinitUsageLoweringPhase then lateinitUsageLoweringPhase then
tailrecLoweringPhase then tailrecLoweringPhase then
enumClassConstructorLoweringPhase then enumClassConstructorLoweringPhase then
enumClassConstructorBodyLoweringPhase then
sharedVariablesLoweringPhase then sharedVariablesLoweringPhase then
callableReferencePhase then
localDelegatedPropertiesLoweringPhase then localDelegatedPropertiesLoweringPhase then
localDeclarationsLoweringPhase then localDeclarationsLoweringPhase then
localClassExtractionPhase then localClassExtractionPhase then
@@ -373,68 +434,52 @@ val wasmPhases = NamedCompilerPhase(
initializersCleanupLoweringPhase then initializersCleanupLoweringPhase then
// Common prefix ends // Common prefix ends
builtInsLoweringPhase then enumEntryInstancesLoweringPhase then
enumEntryInstancesBodyLoweringPhase then
// TODO: Commonize enumEntryToGetInstanceFunction enumClassCreateInitializerLoweringPhase then
// Commonize array literal creation enumEntryCreateGetInstancesFunsLoweringPhase then
// Extract external enum lowering to JS part enumSyntheticFunsLoweringPhase then
// enumUsageLoweringPhase then
// enumClassLoweringPhase then enumEntryRemovalLoweringPhase then
// enumUsageLoweringPhase then
// TODO: Requires stdlib // TODO: Requires stdlib
// suspendFunctionsLoweringPhase then // suspendFunctionsLoweringPhase then
stringConstructorLowering then
returnableBlockLoweringPhase then returnableBlockLoweringPhase then
// TODO: Callable reference lowering is too JS specific.
// Should we reuse JVM or Native lowering?
// callableReferenceLoweringPhase then
defaultArgumentStubGeneratorPhase then defaultArgumentStubGeneratorPhase then
defaultArgumentPatchOverridesPhase then defaultArgumentPatchOverridesPhase then
defaultParameterInjectorPhase then defaultParameterInjectorPhase then
defaultParameterCleanerPhase then defaultParameterCleanerPhase then
// TODO: Investigate
// jsDefaultCallbackGeneratorPhase then
removeInlineFunctionsWithReifiedTypeParametersLoweringPhase then removeInlineFunctionsWithReifiedTypeParametersLoweringPhase then
// TODO:
// TODO: Varargs are too platform-specific. Reimplement.
// varargLoweringPhase then
// TODO: Investigate exception proposal
// multipleCatchesLoweringPhase then // multipleCatchesLoweringPhase then
bridgesConstructionPhase then
// TODO: Reimplement
// typeOperatorLoweringPhase then
// TODO: Reimplement
// secondaryConstructorLoweringPhase then
// secondaryFactoryInjectorLoweringPhase then
// TODO: Reimplement
// classReferenceLoweringPhase then // classReferenceLoweringPhase then
wasmVarargExpressionLoweringPhase then
inlineClassDeclarationLoweringPhase then inlineClassDeclarationLoweringPhase then
inlineClassUsageLoweringPhase then inlineClassUsageLoweringPhase then
// TODO: Commonize box/unbox intrinsics eraseVirtualDispatchReceiverParametersTypes then
// autoboxingTransformerPhase then bridgesConstructionPhase then
blockDecomposerLoweringPhase then
// TODO: Reimplement
// constLoweringPhase then
objectDeclarationLoweringPhase then objectDeclarationLoweringPhase then
objectUsageLoweringPhase then fieldInitializersLoweringPhase then
// staticMembersLoweringPhase then genericReturnTypeLowering then
// Replace builtins before autoboxing
builtInsLoweringPhase0 then
autoboxingTransformerPhase then
objectUsageLoweringPhase then
typeOperatorLoweringPhase then
// Clean up built-ins after type operator lowering
builtInsLoweringPhase then
virtualDispatchReceiverExtractionPhase then
wasmThrowDebugLoweringPhase then
staticMembersLoweringPhase then
wasmNullSpecializationLowering then
validateIrAfterLowering validateIrAfterLowering
) )
@@ -7,10 +7,12 @@ package org.jetbrains.kotlin.backend.wasm
import org.jetbrains.kotlin.backend.common.ir.Symbols import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.builders.declarations.addFunction import org.jetbrains.kotlin.ir.builders.declarations.addFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
@@ -18,24 +20,26 @@ import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.util.OperatorNameConventions
class WasmSymbols( class WasmSymbols(
context: WasmBackendContext, context: WasmBackendContext,
private val symbolTable: SymbolTable private val symbolTable: SymbolTable
) : Symbols<WasmBackendContext>(context, context.irBuiltIns, symbolTable) { ) : Symbols<WasmBackendContext>(context, context.irBuiltIns, symbolTable) {
override val throwNullPointerException private val wasmInternalPackage: PackageViewDescriptor =
get() = TODO() context.module.getPackage(FqName("kotlin.wasm.internal"))
override val throwNoWhenBranchMatchedException
get() = TODO() override val throwNullPointerException = getInternalFunction("THROW_NPE")
override val throwTypeCastException override val throwISE = getInternalFunction("THROW_ISE")
get() = TODO() override val throwNoWhenBranchMatchedException = throwISE
override val throwUninitializedPropertyAccessException override val throwTypeCastException = getInternalFunction("THROW_CCE")
get() = TODO() override val throwUninitializedPropertyAccessException =
getInternalFunction("throwUninitializedPropertyAccessException")
override val defaultConstructorMarker =
getIrClass(FqName("kotlin.wasm.internal.DefaultConstructorMarker"))
override val throwKotlinNothingValueException: IrSimpleFunctionSymbol override val throwKotlinNothingValueException: IrSimpleFunctionSymbol
get() = TODO() get() = TODO()
override val defaultConstructorMarker
get() = TODO()
override val stringBuilder override val stringBuilder
get() = TODO() get() = TODO()
override val copyRangeTo: Map<ClassDescriptor, IrSimpleFunctionSymbol> override val copyRangeTo: Map<ClassDescriptor, IrSimpleFunctionSymbol>
@@ -47,7 +51,7 @@ class WasmSymbols(
override val getContinuation override val getContinuation
get() = TODO() get() = TODO()
override val coroutineContextGetter by lazy { override val coroutineContextGetter by lazy {
context.irFactory.addFunction(context.excludedDeclarations) { context.irFactory.addFunction(context.getExcludedPackageFragment(FqName("kotlin.excluded"))) {
name = Name.identifier("coroutineContextGetter\$Stub") name = Name.identifier("coroutineContextGetter\$Stub")
}.symbol }.symbol
} }
@@ -62,7 +66,9 @@ class WasmSymbols(
override val functionAdapter: IrClassSymbol override val functionAdapter: IrClassSymbol
get() = TODO() get() = TODO()
private val wasmInternalPackage = context.module.getPackage(FqName("kotlin.wasm.internal")) val wasmUnreachable = getInternalFunction("wasm_unreachable")
val wasmFloatNaN = getInternalFunction("wasm_float_nan")
val wasmDoubleNaN = getInternalFunction("wasm_double_nan")
val equalityFunctions = mapOf( val equalityFunctions = mapOf(
context.irBuiltIns.booleanType to getInternalFunction("wasm_i32_eq"), context.irBuiltIns.booleanType to getInternalFunction("wasm_i32_eq"),
@@ -71,12 +77,16 @@ class WasmSymbols(
context.irBuiltIns.charType to getInternalFunction("wasm_i32_eq"), context.irBuiltIns.charType to getInternalFunction("wasm_i32_eq"),
context.irBuiltIns.intType to getInternalFunction("wasm_i32_eq"), context.irBuiltIns.intType to getInternalFunction("wasm_i32_eq"),
context.irBuiltIns.longType to getInternalFunction("wasm_i64_eq"), context.irBuiltIns.longType to getInternalFunction("wasm_i64_eq"),
context.irBuiltIns.stringType to getInternalFunction("wasm_string_eq")
)
val floatEqualityFunctions = mapOf(
context.irBuiltIns.floatType to getInternalFunction("wasm_f32_eq"), context.irBuiltIns.floatType to getInternalFunction("wasm_f32_eq"),
context.irBuiltIns.doubleType to getInternalFunction("wasm_f64_eq") context.irBuiltIns.doubleType to getInternalFunction("wasm_f64_eq")
) )
private fun wasmString(classfier: IrClassifierSymbol): String = with(context.irBuiltIns) { private fun wasmPrimitiveTypeName(classifier: IrClassifierSymbol): String = with(context.irBuiltIns) {
when (classfier) { when (classifier) {
booleanClass, byteClass, shortClass, charClass, intClass -> "i32" booleanClass, byteClass, shortClass, charClass, intClass -> "i32"
floatClass -> "f32" floatClass -> "f32"
doubleClass -> "f64" doubleClass -> "f64"
@@ -85,23 +95,66 @@ class WasmSymbols(
} }
} }
val irBuiltInsToWasmIntrinsics = context.irBuiltIns.run { val comparisonBuiltInsToWasmIntrinsics = context.irBuiltIns.run {
mapOf( listOf(
lessFunByOperandType to "lt", lessFunByOperandType to "lt",
lessOrEqualFunByOperandType to "le", lessOrEqualFunByOperandType to "le",
greaterOrEqualFunByOperandType to "ge", greaterOrEqualFunByOperandType to "ge",
greaterFunByOperandType to "gt" greaterFunByOperandType to "gt"
).map { (typeToBuiltIn, wasmOp) -> ).map { (typeToBuiltIn, wasmOp) ->
typeToBuiltIn.map { (type, builtin) -> typeToBuiltIn.map { (type, builtin) ->
val wasmType = wasmString(type) val wasmType = wasmPrimitiveTypeName(type)
val markSign = if (wasmType == "i32" || wasmType == "i64") "_s" else "" val markSign = if (wasmType == "i32" || wasmType == "i64") "_s" else ""
builtin to getInternalFunction("wasm_${wasmType}_$wasmOp$markSign") builtin to getInternalFunction("wasm_${wasmType}_$wasmOp$markSign")
} }
}.flatten().toMap() }.flatten().toMap()
} }
val booleanAnd = getInternalFunction("wasm_i32_and")
val refEq = getInternalFunction("wasm_ref_eq")
val refIsNull = getInternalFunction("wasm_ref_is_null")
val intToLong = getInternalFunction("wasm_i64_extend_i32_s")
val wasmRefCast = getInternalFunction("wasm_ref_cast")
val boxIntrinsic: IrSimpleFunctionSymbol = getInternalFunction("boxIntrinsic")
val unboxIntrinsic: IrSimpleFunctionSymbol = getInternalFunction("unboxIntrinsic")
val stringGetLiteral = getInternalFunction("stringLiteral") val stringGetLiteral = getInternalFunction("stringLiteral")
val wasmClassId = getInternalFunction("wasmClassId")
val wasmInterfaceId = getInternalFunction("wasmInterfaceId")
val getVirtualMethodId = getInternalFunction("getVirtualMethodId")
val getInterfaceMethodId = getInternalFunction("getInterfaceMethodId")
val isSubClass = getInternalFunction("isSubClass")
val isInterface = getInternalFunction("isInterface")
val nullableEquals = getInternalFunction("nullableEquals")
val ensureNotNull = getInternalFunction("ensureNotNull")
val anyNtoString = getInternalFunction("anyNtoString")
val nullableFloatIeee754Equals = getInternalFunction("nullableFloatIeee754Equals")
val nullableDoubleIeee754Equals = getInternalFunction("nullableDoubleIeee754Equals")
val wasmThrow = getInternalFunction("wasmThrow")
private val functionNInterfaces = (0..22).map { arity ->
getIrClass(FqName("kotlin.wasm.internal.Function$arity"))
}
val functionNInvokeMethods by lazy {
functionNInterfaces.map { interfaceSymbol ->
interfaceSymbol.owner.declarations.filterIsInstance<IrSimpleFunction>().single { method ->
method.name == OperatorNameConventions.INVOKE
}.symbol
}
}
override fun functionN(n: Int): IrClassSymbol =
functionNInterfaces[n]
private fun findClass(memberScope: MemberScope, name: Name): ClassDescriptor = private fun findClass(memberScope: MemberScope, name: Name): ClassDescriptor =
memberScope.getContributedClassifier(name, NoLookupLocation.FROM_BACKEND) as ClassDescriptor memberScope.getContributedClassifier(name, NoLookupLocation.FROM_BACKEND) as ClassDescriptor
@@ -117,7 +170,7 @@ class WasmSymbols(
internal fun getProperty(fqName: FqName): PropertyDescriptor = internal fun getProperty(fqName: FqName): PropertyDescriptor =
findProperty(context.module.getPackage(fqName.parent()).memberScope, fqName.shortName()).single() findProperty(context.module.getPackage(fqName.parent()).memberScope, fqName.shortName()).single()
internal fun getInternalFunction(name: String): IrSimpleFunctionSymbol { private fun getInternalFunction(name: String): IrSimpleFunctionSymbol {
val tmp = findFunctions(wasmInternalPackage.memberScope, Name.identifier(name)).single() val tmp = findFunctions(wasmInternalPackage.memberScope, Name.identifier(name)).single()
return symbolTable.referenceSimpleFunction(tmp) return symbolTable.referenceSimpleFunction(tmp)
} }
@@ -1,66 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.ast
import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly
// TODO: Abstract out S-expression part of dumping?
fun WasmInstruction.toWat(ident: String = ""): String =
"$ident($mnemonic${immediate.toWat()}${operands.joinToString("") { " " + it.toWat("") }})"
fun WasmImmediate.toWat(): String = when (this) {
WasmImmediate.None -> ""
is WasmImmediate.DeclarationReference -> " $$name"
// SpiderMonkey jsshell won't parse Uppercase letters in literals
is WasmImmediate.LiteralValue<*> -> " $value".toLowerCaseAsciiOnly()
}
fun wasmModuleToWat(module: WasmModule): String =
"(module\n${module.fields.joinToString("") { wasmModuleFieldToWat(it) + "\n" }})"
fun wasmFunctionToWat(function: WasmFunction): String {
val watId = "$${function.name}"
val watImport = function.importPair?.let { importPair ->
" (import ${toWasString(importPair.module)} ${toWasString(importPair.name)})"
} ?: ""
val watLocals = function.locals.joinToString("") { " " + wasmLocalToWat(it) + "\n" }
val watParameters = function.parameters.joinToString("") { " " + wasmParameterToWat(it, function.importPair == null) }
val watResult = function.returnType?.let { type -> " (result ${type.mnemonic})" } ?: ""
val watBody = function.instructions.joinToString("") { it.toWat(" ") + "\n" }
return " (func $watId$watImport$watParameters$watResult\n$watLocals$watBody )"
}
fun wasmParameterToWat(parameter: WasmParameter, includeName: Boolean): String {
val name = if (includeName) " $${parameter.name}" else ""
return "(param$name ${parameter.type.mnemonic})"
}
fun wasmLocalToWat(local: WasmLocal): String =
local.run { "(local $$name ${type.mnemonic})" }
fun wasmGlobalToWat(global: WasmGlobal): String {
val watMut = if (global.isMutable) "mut " else ""
val watInit = global.init?.toWat("") ?: ""
return global.run { " (global $$name ($watMut${type.mnemonic}) $watInit)" }
}
fun wasmExportToWat(export: WasmExport): String =
export.run { " (export \"$exportedName\" (${kind.keyword} $$wasmName))" }
fun wasmModuleFieldToWat(moduleField: WasmModuleField): String =
when (moduleField) {
is WasmFunction -> wasmFunctionToWat(moduleField)
is WasmGlobal -> wasmGlobalToWat(moduleField)
is WasmExport -> wasmExportToWat(moduleField)
is WasmModuleFieldList -> moduleField.fields.joinToString("") { wasmModuleFieldToWat(it) + "\n" }
}
fun toWasString(s: String): String {
// TODO: escape characters according to
// https://webassembly.github.io/spec/core/text/values.html#strings
return "\"" + s + "\""
}
@@ -1,55 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.ast
import org.jetbrains.kotlin.backend.wasm.utils.WasmImportPair
class WasmModule(
val fields: List<WasmModuleField>
)
sealed class WasmModuleField
class WasmModuleFieldList(
val fields: List<WasmModuleField>
) : WasmModuleField()
class WasmFunction(
val name: String,
val parameters: List<WasmParameter>,
val returnType: WasmValueType?,
val locals: List<WasmLocal>,
val instructions: List<WasmInstruction>,
val importPair: WasmImportPair?
) : WasmModuleField()
class WasmParameter(
val name: String,
val type: WasmValueType
)
class WasmLocal(
val name: String,
val type: WasmValueType
)
class WasmGlobal(
val name: String,
val type: WasmValueType,
val isMutable: Boolean,
val init: WasmInstruction?
) : WasmModuleField()
class WasmExport(
val wasmName: String,
val exportedName: String,
val kind: Kind
) : WasmModuleField() {
enum class Kind(val keyword: String) {
FUNCTION("func"),
GLOBAL("global")
}
}
@@ -1,65 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.ast
sealed class WasmImmediate {
object None : WasmImmediate()
class DeclarationReference(val name: String) : WasmImmediate()
class LiteralValue<T : Number>(val value: T) : WasmImmediate()
}
sealed class WasmInstruction(
val mnemonic: String,
val immediate: WasmImmediate = WasmImmediate.None,
val operands: List<WasmInstruction> = emptyList()
)
class WasmSimpleInstruction(mnemonic: String, operands: List<WasmInstruction>) :
WasmInstruction(mnemonic, operands = operands)
class WasmNop : WasmInstruction("nop")
class WasmReturn(values: List<WasmInstruction>) :
WasmInstruction("return", operands = values)
class WasmDrop(instructions: List<WasmInstruction>) :
WasmInstruction("drop", operands = instructions)
class WasmCall(name: String, operands: List<WasmInstruction>) :
WasmInstruction("call", WasmImmediate.DeclarationReference(name), operands)
class WasmGetLocal(name: String) :
WasmInstruction("get_local", WasmImmediate.DeclarationReference(name))
class WasmGetGlobal(name: String) :
WasmInstruction("get_global", WasmImmediate.DeclarationReference(name))
class WasmSetGlobal(name: String, value: WasmInstruction) :
WasmInstruction("set_global", WasmImmediate.DeclarationReference(name), listOf(value))
class WasmSetLocal(name: String, value: WasmInstruction) :
WasmInstruction("set_local", WasmImmediate.DeclarationReference(name), listOf(value))
class WasmIf(condition: WasmInstruction, thenInstructions: WasmThen?, elseInstruction: WasmElse?) :
WasmInstruction("if", operands = listOfNotNull(condition, thenInstructions, elseInstruction))
class WasmThen(inst: WasmInstruction) :
WasmInstruction("then", operands = listOf(inst))
class WasmElse(inst: WasmInstruction) :
WasmInstruction("else", operands = listOf(inst))
class WasmBlock(instructions: List<WasmInstruction>) :
WasmInstruction("block", operands = instructions)
sealed class WasmConst<KotlinType : Number, WasmType : WasmValueType>(value: KotlinType, type: WasmType) :
WasmInstruction(type.mnemonic + ".const", WasmImmediate.LiteralValue<KotlinType>(value))
class WasmI32Const(value: Int) : WasmConst<Int, WasmI32>(value, WasmI32)
class WasmI64Const(value: Long) : WasmConst<Long, WasmI64>(value, WasmI64)
class WasmF32Const(value: Float) : WasmConst<Float, WasmF32>(value, WasmF32)
class WasmF64Const(value: Double) : WasmConst<Double, WasmF64>(value, WasmF64)
@@ -1,15 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.ast
sealed class WasmValueType(val mnemonic: String)
object WasmI32 : WasmValueType("i32")
object WasmI64 : WasmValueType("i64")
object WasmF32 : WasmValueType("f32")
object WasmF64 : WasmValueType("f64")
object WasmAnyRef : WasmValueType("anyref")
@@ -1,16 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.codegen
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.backend.js.utils.TODO
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
interface BaseTransformer<out R, in D> : IrElementVisitor<R, D> {
override fun visitElement(element: IrElement, data: D): R {
TODO(element)
}
}
@@ -1,123 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.codegen
import org.jetbrains.kotlin.backend.wasm.ast.*
import org.jetbrains.kotlin.backend.wasm.utils.getWasmImportAnnotation
import org.jetbrains.kotlin.backend.wasm.utils.getWasmInstructionAnnotation
import org.jetbrains.kotlin.backend.wasm.utils.hasExcludedFromCodegenAnnotation
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.ir.util.isAnnotationClass
import org.jetbrains.kotlin.ir.util.isFakeOverride
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
class DeclarationTransformer : BaseTransformer<WasmModuleField?, WasmCodegenContext> {
override fun visitSimpleFunction(declaration: IrSimpleFunction, data: WasmCodegenContext): WasmModuleField? {
if (declaration.hasExcludedFromCodegenAnnotation())
return null
if (declaration.getWasmInstructionAnnotation() != null)
return null
if (declaration.isFakeOverride)
return null
// Virtual functions are not supported yet
if (declaration.origin == IrDeclarationOrigin.BRIDGE)
return null
// Collect local variables
val localNames = wasmNameTable<IrValueDeclaration>()
val wasmName = data.getGlobalName(declaration)
val irParameters = declaration.run {
listOfNotNull(dispatchReceiverParameter, extensionReceiverParameter) + valueParameters
}
val wasmParameters = irParameters.map { parameter ->
val name = localNames.declareFreshName(parameter, parameter.name.asString())
WasmParameter(name, data.transformType(parameter.type))
}
val wasmReturnType = when {
declaration.returnType.isUnit() -> null
else -> data.transformType(declaration.returnType)
}
val importedName = declaration.getWasmImportAnnotation()
if (importedName != null) {
data.imports.add(
WasmFunction(
name = wasmName,
parameters = wasmParameters,
returnType = wasmReturnType,
locals = emptyList(),
instructions = emptyList(),
importPair = importedName
)
)
return null
}
val body = declaration.body
?: error("Function ${declaration.fqNameWhenAvailable} without a body")
data.localNames = localNames.names
val locals = mutableListOf<WasmLocal>()
body.acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitVariable(declaration: IrVariable) {
val name = localNames.declareFreshName(declaration, declaration.name.asString())
locals += WasmLocal(name, data.transformType(declaration.type))
super.visitVariable(declaration)
}
})
return WasmFunction(
name = wasmName,
parameters = wasmParameters,
returnType = wasmReturnType,
locals = locals,
instructions = bodyToWasmInstructionList(body, data),
importPair = null
)
}
override fun visitConstructor(declaration: IrConstructor, data: WasmCodegenContext): WasmModuleField? {
TODO()
}
override fun visitClass(declaration: IrClass, data: WasmCodegenContext): WasmModuleField? {
if (declaration.isAnnotationClass) return null
if (declaration.hasExcludedFromCodegenAnnotation()) return null
val wasmMembers = declaration.declarations.mapNotNull { member ->
when (member) {
is IrSimpleFunction -> this.visitSimpleFunction(member, data)
else -> null
}
}
return WasmModuleFieldList(wasmMembers)
}
override fun visitField(declaration: IrField, data: WasmCodegenContext): WasmModuleField {
return WasmGlobal(
name = data.getGlobalName(declaration),
type = data.transformType(declaration.type),
isMutable = true,
// TODO: move non-constexpr initializers out
init = declaration.initializer?.let {
expressionToWasmInstruction(it.expression, data)
}
)
}
}
@@ -1,204 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.codegen
import org.jetbrains.kotlin.backend.common.ir.isElseBranch
import org.jetbrains.kotlin.backend.wasm.ast.*
import org.jetbrains.kotlin.backend.wasm.utils.getWasmInstructionAnnotation
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.types.isUnit
import org.jetbrains.kotlin.ir.util.dump
class ExpressionTransformer : BaseTransformer<WasmInstruction, WasmCodegenContext> {
override fun visitVararg(expression: IrVararg, data: WasmCodegenContext): WasmInstruction {
TODO("Support arrays")
}
override fun visitExpressionBody(body: IrExpressionBody, data: WasmCodegenContext): WasmInstruction =
body.expression.accept(this, data)
override fun visitFunctionReference(expression: IrFunctionReference, data: WasmCodegenContext): WasmInstruction {
TODO("?")
}
override fun <T> visitConst(expression: IrConst<T>, data: WasmCodegenContext): WasmInstruction {
return when (val kind = expression.kind) {
is IrConstKind.Null -> TODO()
is IrConstKind.String -> {
val value = kind.valueOf(expression)
val index = data.stringLiterals.size
data.stringLiterals.add(value)
val funName = data.getGlobalName(data.backendContext.wasmSymbols.stringGetLiteral.owner)
val operand = WasmI32Const(index)
WasmCall(funName, listOf(operand))
}
is IrConstKind.Boolean -> WasmI32Const(if (kind.valueOf(expression)) 1 else 0)
is IrConstKind.Byte -> WasmI32Const(kind.valueOf(expression).toInt())
is IrConstKind.Short -> WasmI32Const(kind.valueOf(expression).toInt())
is IrConstKind.Int -> WasmI32Const(kind.valueOf(expression))
is IrConstKind.Long -> WasmI64Const(kind.valueOf(expression))
is IrConstKind.Char -> WasmI32Const(kind.valueOf(expression).toInt())
is IrConstKind.Float -> WasmF32Const(kind.valueOf(expression))
is IrConstKind.Double -> WasmF64Const(kind.valueOf(expression))
}
}
override fun visitStringConcatenation(expression: IrStringConcatenation, data: WasmCodegenContext): WasmInstruction {
TODO("Implement kotlin.String")
}
override fun visitGetField(expression: IrGetField, data: WasmCodegenContext): WasmInstruction {
val fieldName = data.getGlobalName(expression.symbol.owner)
if (expression.receiver != null)
TODO("Support member fields")
return WasmGetGlobal(fieldName)
}
override fun visitGetValue(expression: IrGetValue, data: WasmCodegenContext): WasmInstruction =
WasmGetLocal(data.getLocalName(expression.symbol.owner))
override fun visitGetObjectValue(expression: IrGetObjectValue, data: WasmCodegenContext): WasmInstruction {
TODO("IrGetObjectValue")
}
override fun visitSetField(expression: IrSetField, data: WasmCodegenContext): WasmInstruction {
val fieldName = data.getGlobalName(expression.symbol.owner)
if (expression.receiver != null)
TODO("Support member fields")
val value = expression.value.accept(this, data)
return WasmSetGlobal(fieldName, value)
}
override fun visitSetValue(expression: IrSetValue, data: WasmCodegenContext): WasmInstruction {
val fieldName = data.getLocalName(expression.symbol.owner)
val value = expression.value.accept(this, data)
return WasmSetLocal(fieldName, value)
}
override fun visitConstructorCall(expression: IrConstructorCall, data: WasmCodegenContext): WasmInstruction {
TODO("IrConstructorCall")
}
override fun visitCall(expression: IrCall, data: WasmCodegenContext): WasmInstruction {
val function = expression.symbol.owner.realOverrideTarget
val valueArgs = (0 until expression.valueArgumentsCount).mapNotNull { expression.getValueArgument(it) }
val irArguments = listOfNotNull(expression.dispatchReceiver, expression.extensionReceiver) + valueArgs
val wasmArguments = irArguments.map { expressionToWasmInstruction(it, data) }
val wasmInstruction = function.getWasmInstructionAnnotation()
if (wasmInstruction != null) {
if (wasmInstruction == "nop") {
return wasmArguments.single()
}
return WasmSimpleInstruction(wasmInstruction, wasmArguments)
}
val name = data.getGlobalName(function)
return WasmCall(name, wasmArguments)
}
override fun visitTypeOperator(expression: IrTypeOperatorCall, data: WasmCodegenContext): WasmInstruction {
val wasmArgument = expressionToWasmInstruction(expression.argument, data)
if (expression.operator == IrTypeOperator.IMPLICIT_COERCION_TO_UNIT) {
return wasmArgument
}
TODO("IrTypeOperatorCall:\n ${expression.dump()}")
}
override fun visitGetEnumValue(expression: IrGetEnumValue, data: WasmCodegenContext): WasmInstruction {
TODO("IrGetEnumValue")
}
override fun visitBlockBody(body: IrBlockBody, data: WasmCodegenContext): WasmInstruction {
TODO()
}
override fun visitContainerExpression(expression: IrContainerExpression, data: WasmCodegenContext): WasmInstruction {
val expressions = expression.statements.map { it.accept(this, data) }
if (!expression.type.isUnit())
return WasmBlock(expressions + listOf(WasmDrop(emptyList())))
return WasmBlock(expressions)
}
override fun visitExpression(expression: IrExpression, data: WasmCodegenContext): WasmInstruction {
return expressionToWasmInstruction(expression, data)
}
override fun visitBreak(jump: IrBreak, data: WasmCodegenContext): WasmInstruction {
TODO()
}
override fun visitContinue(jump: IrContinue, data: WasmCodegenContext): WasmInstruction {
TODO()
}
override fun visitReturn(expression: IrReturn, data: WasmCodegenContext): WasmInstruction {
if (expression.value.type.isUnit()) return WasmReturn(emptyList())
return WasmReturn(listOf(expressionToWasmInstruction(expression.value, data)))
}
override fun visitThrow(expression: IrThrow, data: WasmCodegenContext): WasmInstruction {
TODO("IrThrow")
}
override fun visitVariable(declaration: IrVariable, data: WasmCodegenContext): WasmInstruction {
val init = declaration.initializer ?: return WasmNop()
val varName = data.getLocalName(declaration)
return WasmSetLocal(varName, expressionToWasmInstruction(init, data))
}
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, data: WasmCodegenContext): WasmInstruction {
TODO("IrDelegatingConstructorCall")
}
override fun visitInstanceInitializerCall(expression: IrInstanceInitializerCall, data: WasmCodegenContext): WasmInstruction {
TODO("IrInstanceInitializerCall")
}
override fun visitTry(aTry: IrTry, data: WasmCodegenContext): WasmInstruction {
TODO("IrTry")
}
override fun visitWhen(expression: IrWhen, data: WasmCodegenContext): WasmInstruction {
return expression.branches.foldRight(null) { br: IrBranch, inst: WasmInstruction? ->
val body = expressionToWasmInstruction(br.result, data)
if (isElseBranch(br)) body
else {
val condition = expressionToWasmInstruction(br.condition, data)
WasmIf(condition, WasmThen(body), inst?.let { WasmElse(inst) })
}
}!!
}
override fun visitWhileLoop(loop: IrWhileLoop, data: WasmCodegenContext): WasmInstruction {
TODO("IrWhileLoop")
}
override fun visitDoWhileLoop(loop: IrDoWhileLoop, data: WasmCodegenContext): WasmInstruction {
TODO("IrDoWhileLoop")
}
override fun visitSyntheticBody(body: IrSyntheticBody, data: WasmCodegenContext): WasmInstruction {
TODO("IrSyntheticBody")
}
override fun visitDynamicMemberExpression(expression: IrDynamicMemberExpression, data: WasmCodegenContext): WasmInstruction =
error("Dynamic operators are not supported for WASM target")
override fun visitDynamicOperatorExpression(expression: IrDynamicOperatorExpression, data: WasmCodegenContext): WasmInstruction =
error("Dynamic operators are not supported for WASM target")
}
fun expressionToWasmInstruction(expression: IrExpression, context: WasmCodegenContext): WasmInstruction {
return expression.accept(ExpressionTransformer(), context)
}
@@ -1,82 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.codegen
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.backend.wasm.WasmCompilerResult
import org.jetbrains.kotlin.backend.wasm.ast.WasmExport
import org.jetbrains.kotlin.backend.wasm.ast.WasmModule
import org.jetbrains.kotlin.backend.wasm.ast.wasmModuleToWat
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.jsAssignment
import org.jetbrains.kotlin.ir.backend.js.utils.sanitizeName
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.js.backend.ast.JsArrayLiteral
import org.jetbrains.kotlin.js.backend.ast.JsBlock
import org.jetbrains.kotlin.js.backend.ast.JsNameRef
import org.jetbrains.kotlin.js.backend.ast.JsStringLiteral
import org.jetbrains.kotlin.utils.addIfNotNull
class IrModuleToWasm(private val backendContext: WasmBackendContext) {
fun generateModule(module: IrModuleFragment): WasmCompilerResult {
val nameTable = generateWatTopLevelNames(module.files)
val context = WasmCodegenContext(nameTable, backendContext)
val irDeclarations = module.files.flatMap { it.declarations }
val wasmDeclarations = irDeclarations.mapNotNull { it.accept(DeclarationTransformer(), context) }
val exports = generateExports(module, context)
val wasmModule = WasmModule(context.imports + wasmDeclarations + exports)
val wat = wasmModuleToWat(wasmModule)
return WasmCompilerResult(wat, generateStringLiteralsSupport(context.stringLiterals))
}
private fun generateStringLiteralsSupport(literals: List<String>): String {
return JsBlock(
jsAssignment(
JsNameRef("stringLiterals", "runtime"),
JsArrayLiteral(literals.map { JsStringLiteral(it) })
).makeStmt()
).toString()
}
private fun generateExports(module: IrModuleFragment, context: WasmCodegenContext): List<WasmExport> {
val exports = mutableListOf<WasmExport>()
for (file in module.files) {
for (declaration in file.declarations) {
exports.addIfNotNull(generateExport(declaration, context))
}
}
return exports
}
private fun generateExport(declaration: IrDeclaration, context: WasmCodegenContext): WasmExport? {
if (declaration !is IrDeclarationWithVisibility ||
declaration !is IrDeclarationWithName ||
declaration !is IrSimpleFunction ||
declaration.visibility != DescriptorVisibilities.PUBLIC
) {
return null
}
if (!declaration.isExported(context))
return null
val internalName = context.getGlobalName(declaration)
val exportedName = sanitizeName(declaration.name.identifier)
return WasmExport(
wasmName = internalName,
exportedName = exportedName,
kind = WasmExport.Kind.FUNCTION
)
}
}
fun IrFunction.isExported(context: WasmCodegenContext): Boolean =
fqNameWhenAvailable in context.backendContext.additionalExportedDeclarations
@@ -1,64 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.codegen
import org.jetbrains.kotlin.backend.common.ir.isTopLevel
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.backend.js.utils.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
fun <T> wasmNameTable() = NameTable<T>(sanitizer = ::sanitizeWatIdentifier)
fun generateWatTopLevelNames(packages: List<IrPackageFragment>): Map<IrDeclarationWithName, String> {
val names = wasmNameTable<IrDeclarationWithName>()
fun nameTopLevelDecl(declaration: IrDeclarationWithName) {
val suggestedName = declaration.fqNameWhenAvailable?.toString()
?: "fqname???" + declaration.name.asString()
names.declareFreshName(declaration, suggestedName)
}
for (p in packages) {
p.acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitSimpleFunction(declaration: IrSimpleFunction) {
nameTopLevelDecl(declaration)
super.visitSimpleFunction(declaration)
}
override fun visitField(declaration: IrField) {
if (declaration.isTopLevel)
nameTopLevelDecl(declaration)
super.visitField(declaration)
}
})
}
return names.names
}
fun sanitizeWatIdentifier(ident: String): String {
if (ident.isEmpty())
return "_"
if (ident.all(::isValidWatIdentifier))
return ident
return ident.map { if (isValidWatIdentifier(it)) it else "_" }.joinToString("")
}
// https://webassembly.github.io/spec/core/text/values.html#text-id
fun isValidWatIdentifier(c: Char): Boolean =
c in '0'..'9' || c in 'A'..'Z' || c in 'a'..'z'
// TODO: SpiderMonkey js shell can't parse some of the
// permitted identifiers: '?', '<'
// || c in "!#$%&*+-./:<=>?@\\^_`|~"
|| c in "$.@_"
@@ -1,35 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.codegen
import org.jetbrains.kotlin.backend.wasm.ast.WasmInstruction
import org.jetbrains.kotlin.backend.wasm.ast.WasmNop
import org.jetbrains.kotlin.backend.wasm.ast.WasmSetLocal
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.*
class StatementTransformer : BaseTransformer<WasmInstruction, WasmCodegenContext> {
override fun visitVariable(declaration: IrVariable, data: WasmCodegenContext): WasmInstruction {
val init = declaration.initializer ?: return WasmNop()
val varName = data.getLocalName(declaration)
return WasmSetLocal(varName, expressionToWasmInstruction(init, data))
}
override fun visitExpression(expression: IrExpression, data: WasmCodegenContext): WasmInstruction {
return expressionToWasmInstruction(expression, data)
}
}
fun statementToWasmInstruction(statement: IrStatement, context: WasmCodegenContext): WasmInstruction {
return statement.accept(StatementTransformer(), context)
}
fun bodyToWasmInstructionList(body: IrBody, context: WasmCodegenContext): List<WasmInstruction> {
if (body is IrBlockBody) {
return body.statements.map { statementToWasmInstruction(it, context) }
} else TODO()
}
@@ -1,27 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.codegen
import org.jetbrains.kotlin.backend.wasm.ast.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.render
fun WasmCodegenContext.transformType(irType: IrType): WasmValueType =
when {
irType.isBoolean() -> WasmI32
irType.isByte() -> WasmI32
irType.isShort() -> WasmI32
irType.isInt() -> WasmI32
irType.isLong() -> WasmI64
irType.isChar() -> WasmI32
irType.isFloat() -> WasmF32
irType.isDouble() -> WasmF64
irType.isString() -> WasmAnyRef
irType.isAny() || irType.isNullableAny() -> WasmAnyRef
else ->
TODO("Unsupported type: ${irType.render()}")
}
@@ -1,29 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.codegen
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.backend.wasm.ast.WasmModuleField
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
class WasmCodegenContext(
private val topLevelNames: Map<IrDeclarationWithName, String>,
val backendContext: WasmBackendContext
) {
val imports = mutableListOf<WasmModuleField>()
var localNames: Map<IrValueDeclaration, String> = emptyMap()
val stringLiterals = mutableListOf<String>()
fun getGlobalName(declaration: IrDeclarationWithName): String =
topLevelNames[declaration]
?: error("Can't find name for ${declaration.fqNameWhenAvailable}")
fun getLocalName(declaration: IrValueDeclaration): String =
localNames[declaration]
?: error("Can't find local name for ${declaration.fqNameWhenAvailable}")
}
@@ -9,7 +9,9 @@ import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analyzer.AbstractAnalyzerWithCompilerReport import org.jetbrains.kotlin.analyzer.AbstractAnalyzerWithCompilerReport
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel
import org.jetbrains.kotlin.backend.wasm.codegen.IrModuleToWasm import org.jetbrains.kotlin.backend.wasm.ir2wasm.WasmModuleFragmentGenerator
import org.jetbrains.kotlin.backend.wasm.ir2wasm.WasmCompiledModuleFragment
import org.jetbrains.kotlin.backend.wasm.ir2wasm.generateStringLiteralsSupport
import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.ir.backend.js.MainModule import org.jetbrains.kotlin.ir.backend.js.MainModule
@@ -22,8 +24,11 @@ import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.library.resolver.KotlinLibraryResolveResult import org.jetbrains.kotlin.library.resolver.KotlinLibraryResolveResult
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.wasm.ir.convertors.WasmIrToBinary
import org.jetbrains.kotlin.wasm.ir.convertors.WasmIrToText
import java.io.ByteArrayOutputStream
data class WasmCompilerResult(val wat: String, val js: String) class WasmCompilerResult(val wat: String, val js: String, val wasm: ByteArray)
fun compileWasm( fun compileWasm(
project: Project, project: Project,
@@ -60,9 +65,25 @@ fun compileWasm(
val irProviders = generateTypicalIrProviderList(moduleDescriptor, irBuiltIns, symbolTable, deserializer) val irProviders = generateTypicalIrProviderList(moduleDescriptor, irBuiltIns, symbolTable, deserializer)
ExternalDependenciesGenerator(symbolTable, irProviders, configuration.languageVersionSettings).generateUnboundSymbolsAsDependencies() ExternalDependenciesGenerator(symbolTable, irProviders, configuration.languageVersionSettings).generateUnboundSymbolsAsDependencies()
moduleFragment.patchDeclarationParents() moduleFragment.patchDeclarationParents()
deserializer.postProcess()
wasmPhases.invokeToplevel(phaseConfig, context, moduleFragment) wasmPhases.invokeToplevel(phaseConfig, context, moduleFragment)
return IrModuleToWasm(context).generateModule(moduleFragment) val compiledWasmModule = WasmCompiledModuleFragment()
val codeGenerator = WasmModuleFragmentGenerator(context, compiledWasmModule)
codeGenerator.generateModule(moduleFragment)
val linkedModule = compiledWasmModule.linkWasmCompiledFragments()
val watGenerator = WasmIrToText()
watGenerator.appendWasmModule(linkedModule)
val wat = watGenerator.toString()
val os = ByteArrayOutputStream()
WasmIrToBinary(os, linkedModule).appendWasmModule()
val byteArray = os.toByteArray()
return WasmCompilerResult(
wat,
generateStringLiteralsSupport(compiledWasmModule.stringLiterals),
wasm = byteArray
)
} }
@@ -0,0 +1,492 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:OptIn(ExperimentalUnsignedTypes::class)
package org.jetbrains.kotlin.backend.wasm.ir2wasm
import org.jetbrains.kotlin.backend.common.ir.isElseBranch
import org.jetbrains.kotlin.backend.common.ir.isOverridable
import org.jetbrains.kotlin.backend.common.ir.returnType
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.backend.wasm.WasmSymbols
import org.jetbrains.kotlin.backend.wasm.lower.wasmSignature
import org.jetbrains.kotlin.backend.wasm.utils.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.getInlineClassBackingField
import org.jetbrains.kotlin.ir.util.isInterface
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.wasm.ir.*
class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorVoid {
val body: WasmExpressionBuilder = context.bodyGen
// Shortcuts
private val backendContext: WasmBackendContext = context.backendContext
private val wasmSymbols: WasmSymbols = backendContext.wasmSymbols
private val irBuiltIns: IrBuiltIns = backendContext.irBuiltIns
override fun visitElement(element: IrElement) {
error("Unexpected element of type ${element::class}")
}
override fun <T> visitConst(expression: IrConst<T>) {
when (val kind = expression.kind) {
is IrConstKind.Null -> generateDefaultInitializerForType(context.transformType(expression.type), body)
is IrConstKind.Boolean -> body.buildConstI32(if (kind.valueOf(expression)) 1 else 0)
is IrConstKind.Byte -> body.buildConstI32(kind.valueOf(expression).toInt())
is IrConstKind.Short -> body.buildConstI32(kind.valueOf(expression).toInt())
is IrConstKind.Int -> body.buildConstI32(kind.valueOf(expression))
is IrConstKind.Long -> body.buildConstI64(kind.valueOf(expression))
is IrConstKind.Char -> body.buildConstI32(kind.valueOf(expression).toInt())
is IrConstKind.Float -> body.buildConstF32(kind.valueOf(expression))
is IrConstKind.Double -> body.buildConstF64(kind.valueOf(expression))
is IrConstKind.String -> {
body.buildConstI32Symbol(context.referenceStringLiteral(kind.valueOf(expression)))
body.buildCall(context.referenceFunction(wasmSymbols.stringGetLiteral))
}
else -> error("Unknown constant kind")
}
}
override fun visitGetField(expression: IrGetField) {
val field: IrField = expression.symbol.owner
val receiver: IrExpression? = expression.receiver
if (receiver != null) {
generateExpression(receiver)
if (backendContext.inlineClassesUtils.isClassInlineLike(field.parentAsClass)) {
// Unboxed inline class instance is already represented as backing field.
// Doing nothing.
} else {
generateInstanceFieldAccess(field)
}
} else {
body.buildGetGlobal(context.referenceGlobal(field.symbol))
}
}
private fun generateInstanceFieldAccess(field: IrField) {
body.buildStructGet(
context.referenceStructType(field.parentAsClass.symbol),
context.getStructFieldRef(field)
)
}
override fun visitSetField(expression: IrSetField) {
val field = expression.symbol.owner
val receiver = expression.receiver
if (receiver != null) {
generateExpression(receiver)
generateExpression(expression.value)
body.buildStructSet(
struct = context.referenceStructType(field.parentAsClass.symbol),
fieldId = context.getStructFieldRef(field),
)
} else {
generateExpression(expression.value)
body.buildSetGlobal(context.referenceGlobal(expression.symbol))
}
}
override fun visitGetValue(expression: IrGetValue) {
body.buildGetLocal(context.referenceLocal(expression.symbol))
}
override fun visitSetValue(expression: IrSetValue) {
generateExpression(expression.value)
body.buildSetLocal(context.referenceLocal(expression.symbol))
}
override fun visitCall(expression: IrCall) {
generateCall(expression)
}
override fun visitConstructorCall(expression: IrConstructorCall) {
val klass: IrClass = expression.symbol.owner.parentAsClass
if (backendContext.inlineClassesUtils.isClassInlineLike(klass)) {
// Unboxed instance is just a constructor argument.
generateExpression(expression.getValueArgument(0)!!)
return
}
val wasmStruct: WasmSymbol<WasmStructDeclaration> = context.referenceStructType(klass.symbol)
val wasmClassId = context.referenceClassId(klass.symbol)
val irFields: List<IrField> = klass.allFields(backendContext.irBuiltIns)
irFields.forEachIndexed { index, field ->
if (index == 0)
body.buildConstI32Symbol(wasmClassId)
else
generateDefaultInitializerForType(context.transformType(field.type), body)
}
body.buildGetGlobal(context.referenceClassRTT(klass.symbol))
body.buildStructNew(wasmStruct)
generateCall(expression)
}
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall) {
val klass = context.irFunction.parentAsClass
// Don't delegate constructors of Any to Any.
if (klass.defaultType.isAny()) {
return
}
body.buildGetLocal(context.referenceLocal(0)) // this parameter
generateCall(expression)
}
private fun generateCall(call: IrFunctionAccessExpression) {
// Box intrinsic has an additional klass ID argument.
// Processing it separately
if (call.symbol == wasmSymbols.boxIntrinsic) {
val toType = call.getTypeArgument(0)!!
val klass = toType.erasedUpperBound!!
val structTypeName = context.referenceStructType(klass.symbol)
val klassId = context.referenceClassId(klass.symbol)
body.buildConstI32Symbol(klassId)
generateExpression(call.getValueArgument(0)!!)
body.buildGetGlobal(context.referenceClassRTT(klass.symbol))
body.buildStructNew(structTypeName)
return
}
call.dispatchReceiver?.let { generateExpression(it) }
call.extensionReceiver?.let { generateExpression(it) }
for (i in 0 until call.valueArgumentsCount) {
generateExpression(call.getValueArgument(i)!!)
}
val function: IrFunction = call.symbol.owner.realOverrideTarget
if (tryToGenerateIntrinsicCall(call, function)) {
return
}
val isSuperCall = call is IrCall && call.superQualifierSymbol != null
if (function is IrSimpleFunction && function.isOverridable && !isSuperCall) {
// Generating index for indirect call
val klass = function.parentAsClass
if (!klass.isInterface) {
val classMetadata = context.getClassMetadata(klass.symbol)
val vfSlot = classMetadata.virtualMethods.map { it.function }.indexOf(function)
// Dispatch receiver should be simple and without side effects at this point
// TODO: Verify
generateExpression(call.dispatchReceiver!!)
body.buildConstI32(vfSlot)
body.buildCall(context.referenceFunction(wasmSymbols.getVirtualMethodId))
} else {
val signatureId = context.referenceSignatureId(function.wasmSignature(backendContext.irBuiltIns))
generateExpression(call.dispatchReceiver!!)
body.buildConstI32Symbol(signatureId)
body.buildCall(context.referenceFunction(wasmSymbols.getInterfaceMethodId))
}
body.buildCallIndirect(
symbol = context.referenceFunctionType(function.symbol)
)
} else {
// Static function call
body.buildCall(context.referenceFunction(function.symbol))
}
// Return types of imported functions cannot have concrete struct/array references.
// Non-primitive return types are represented as eqref which need to be casted back to expected type on call site.
if (function.getWasmImportAnnotation() != null) {
val resT = context.transformResultType(function.returnType)
if (resT is WasmRefNullType) {
generateTypeRTT(function.returnType)
body.buildRefCast(fromType = WasmEqRef, toType = resT)
}
}
}
private fun generateTypeRTT(type: IrType) {
val rtClass = type.erasedUpperBound?.symbol ?: context.backendContext.irBuiltIns.anyClass
body.buildGetGlobal(context.referenceClassRTT(rtClass))
}
// Return true if generated.
// Assumes call arguments are already on the stack
private fun tryToGenerateIntrinsicCall(
call: IrFunctionAccessExpression,
function: IrFunction
): Boolean {
if (tryToGenerateWasmOpIntrinsicCall(function)) {
return true
}
when (function.symbol) {
wasmSymbols.wasmClassId -> {
val klass = call.getTypeArgument(0)!!.getClass()
?: error("No class given for wasmClassId intrinsic")
assert(!klass.isInterface)
body.buildConstI32Symbol(context.referenceClassId(klass.symbol))
}
wasmSymbols.wasmInterfaceId -> {
val irInterface = call.getTypeArgument(0)!!.getClass()
?: error("No interface given for wasmInterfaceId intrinsic")
assert(irInterface.isInterface)
body.buildConstI32Symbol(context.referenceInterfaceId(irInterface.symbol))
}
wasmSymbols.wasmRefCast -> {
val fromType = call.getTypeArgument(0)!!
val toType = call.getTypeArgument(1)!!
generateTypeRTT(toType)
body.buildRefCast(context.transformType(fromType), context.transformType(toType))
}
wasmSymbols.wasmFloatNaN -> {
body.buildConstF32(Float.NaN)
}
wasmSymbols.wasmDoubleNaN -> {
body.buildConstF64(Double.NaN)
}
wasmSymbols.unboxIntrinsic -> {
val fromType = call.getTypeArgument(0)!!
if (fromType.isNothing()) {
body.buildUnreachable()
// TODO: Investigate why?
return true
}
// Workaround test codegen/box/elvis/nullNullOk.kt
if (fromType.makeNotNull().isNothing()) {
body.buildUnreachable()
return true
}
val toType = call.getTypeArgument(1)!!
val klass: IrClass = backendContext.inlineClassesUtils.getInlinedClass(toType)!!
val field = getInlineClassBackingField(klass)
generateTypeRTT(toType)
body.buildRefCast(context.transformType(fromType), context.transformBoxedType(toType))
generateInstanceFieldAccess(field)
}
else -> {
return false
}
}
return true
}
override fun visitContainerExpression(expression: IrContainerExpression) {
val statements = expression.statements
if (statements.isEmpty()) return
statements.dropLast(1).forEach {
statementToWasmInstruction(it)
}
if (expression.type != irBuiltIns.unitType) {
generateExpression(statements.last() as IrExpression)
} else {
statementToWasmInstruction(statements.last())
}
}
override fun visitBreak(jump: IrBreak) {
body.buildBr(context.referenceLoopLevel(jump.loop, LoopLabelType.BREAK))
}
override fun visitContinue(jump: IrContinue) {
body.buildBr(context.referenceLoopLevel(jump.loop, LoopLabelType.CONTINUE))
}
override fun visitReturn(expression: IrReturn) {
generateExpression(expression.value)
// FIXME: Hack for "returning" Unit from functions with generic return type.
// Common case -- lambdas returning unit.
if (expression.value.type == irBuiltIns.unitType &&
expression.returnTargetSymbol.owner.returnType(backendContext) != irBuiltIns.unitType
) {
val irReturnType = expression.returnTargetSymbol.owner.returnType(backendContext)
if (irReturnType != irBuiltIns.unitType) {
generateDefaultInitializerForType(context.transformType(irReturnType), body)
}
}
body.buildInstr(WasmOp.RETURN)
}
override fun visitWhen(expression: IrWhen) {
if (expression.type == irBuiltIns.unitType) {
var ifCount = 0
for (branch in expression.branches) {
if (!isElseBranch(branch)) {
generateExpression(branch.condition)
body.buildIf(label = null, resultType = null)
statementToWasmInstruction(branch.result)
body.buildElse()
ifCount++
} else {
statementToWasmInstruction(branch.result)
break
}
}
repeat(ifCount) { body.buildEnd() }
return
}
val resultType = context.transformBlockResultType(expression.type)
var ifCount = 0
for (branch in expression.branches) {
if (!isElseBranch(branch)) {
generateExpression(branch.condition)
body.buildIf(null, resultType)
generateExpression(branch.result)
if (expression.type == irBuiltIns.nothingType) {
body.buildUnreachable()
}
body.buildElse()
ifCount++
} else {
generateExpression(branch.result)
if (expression.type == irBuiltIns.nothingType) {
body.buildUnreachable()
}
break
}
}
repeat(ifCount) { body.buildEnd() }
}
override fun visitDoWhileLoop(loop: IrDoWhileLoop) {
// (loop $LABEL
// (block $BREAK_LABEL
// (block $CONTINUE_LABEL <LOOP BODY>)
// (br_if $LABEL <CONDITION>)))
val label = loop.label
body.buildLoop(label)
val wasmLoop = body.numberOfNestedBlocks
body.buildBlock("BREAK_$label")
val wasmBreakBlock = body.numberOfNestedBlocks
body.buildBlock("CONTINUE_$label")
val wasmContinueBlock = body.numberOfNestedBlocks
context.defineLoopLevel(loop, LoopLabelType.BREAK, wasmBreakBlock)
context.defineLoopLevel(loop, LoopLabelType.CONTINUE, wasmContinueBlock)
loop.body?.let { statementToWasmInstruction(it) }
body.buildEnd()
generateExpression(loop.condition)
body.buildBrIf(wasmLoop)
body.buildEnd()
body.buildEnd()
}
override fun visitWhileLoop(loop: IrWhileLoop) {
// (loop $CONTINUE_LABEL
// (block $BREAK_LABEL
// (br_if $BREAK_LABEL (i32.eqz <CONDITION>))
// <LOOP_BODY>
// (br $CONTINUE_LABEL)))
val label = loop.label
body.buildLoop(label)
val wasmLoop = body.numberOfNestedBlocks
body.buildBlock("BREAK_$label")
val wasmBreakBlock = body.numberOfNestedBlocks
context.defineLoopLevel(loop, LoopLabelType.BREAK, wasmBreakBlock)
context.defineLoopLevel(loop, LoopLabelType.CONTINUE, wasmLoop)
generateExpression(loop.condition)
body.buildInstr(WasmOp.I32_EQZ)
body.buildBrIf(wasmBreakBlock)
loop.body?.let {
statementToWasmInstruction(it)
}
body.buildBr(wasmLoop)
body.buildEnd()
body.buildEnd()
}
fun generateExpression(expression: IrExpression) {
expression.acceptVoid(this)
if (expression.type == irBuiltIns.nothingType) {
body.buildUnreachable()
}
}
fun statementToWasmInstruction(statement: IrStatement) {
if (statement is IrVariable) {
context.defineLocal(statement.symbol)
val init = statement.initializer ?: return
generateExpression(init)
val varName = context.referenceLocal(statement.symbol)
body.buildSetLocal(varName)
return
}
generateExpression(statement as IrExpression)
if (statement.type != irBuiltIns.unitType && statement.type != irBuiltIns.nothingType) {
body.buildInstr(WasmOp.DROP)
}
}
// Return true if function is recognized as intrinsic.
fun tryToGenerateWasmOpIntrinsicCall(function: IrFunction): Boolean {
if (function.hasWasmReinterpretAnnotation()) {
return true
}
val opString = function.getWasmOpAnnotation()
if (opString != null) {
val op = WasmOp.valueOf(opString)
var immediates = emptyArray<WasmImmediate>()
when (op.immediates.size) {
0 -> {
}
1 -> {
when (val imm = op.immediates[0]) {
WasmImmediateKind.MEM_ARG ->
immediates = arrayOf(WasmImmediate.MemArg(0u, 0u))
else ->
error("Immediate $imm is unsupported")
}
}
else ->
error("Op $opString is unsupported")
}
body.buildInstr(op, *immediates)
return true
}
return false
}
}
@@ -0,0 +1,113 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.ir2wasm
import org.jetbrains.kotlin.backend.common.ir.isOverridableOrOverrides
import org.jetbrains.kotlin.backend.wasm.lower.WasmSignature
import org.jetbrains.kotlin.backend.wasm.lower.wasmSignature
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.ir.util.isInterface
class ClassMetadata(
val klass: IrClass,
val superClass: ClassMetadata?,
irBuiltIns: IrBuiltIns
) {
// List of all fields including fields of super classes
// In Wasm order
val fields: List<IrField> =
superClass?.fields.orEmpty() + klass.declarations.filterIsInstance<IrField>()
// Implemented interfaces in no particular order
val interfaces: List<IrClass> = klass.allSuperInterfaces()
// Virtual methods in Wasm order
// TODO: Collect interface methods separately
val virtualMethods: List<VirtualMethodMetadata> = run {
val virtualFunctions =
klass.declarations
.filterVirtualFunctions()
.map {
VirtualMethodMetadata(
it,
it.wasmSignature(irBuiltIns)
)
}
val signatureToVirtualFunction = virtualFunctions.associateBy { it.signature }
val superSignatures = superClass?.virtualMethodsSignatures.orEmpty()
val newVirtualMethods = virtualFunctions.filter { it.signature !in superSignatures }
val superVirtualMethods = superClass?.virtualMethods.orEmpty().map {
signatureToVirtualFunction[it.signature] ?: it
}
val orderedVirtualFunctions = superVirtualMethods + newVirtualMethods
orderedVirtualFunctions
}
init {
val signatureToFunctions = mutableMapOf<WasmSignature, MutableList<IrSimpleFunction>>()
for (vm in virtualMethods) {
signatureToFunctions.getOrPut(vm.signature) { mutableListOf() }.add(vm.function)
}
for ((sig, functions) in signatureToFunctions) {
if (functions.size > 1) {
val funcList = functions.joinToString { " ---- ${it.fqNameWhenAvailable} \n" }
// TODO: Check in FE
error(
"Class ${klass.fqNameWhenAvailable} has ${functions.size} methods with the same signature $sig\n $funcList"
)
}
}
}
private val virtualMethodsSignatures: Set<WasmSignature> =
virtualMethods.map { it.signature }.toSet()
}
class VirtualMethodMetadata(
val function: IrSimpleFunction,
val signature: WasmSignature
)
fun IrClass.allSuperInterfaces(): List<IrClass> =
superTypes.map {
it.classifierOrFail.owner as IrClass
}.flatMap {
(if (it.isInterface) listOf(it) else emptyList()) + it.allSuperInterfaces()
}
fun List<IrDeclaration>.filterVirtualFunctions(): List<IrSimpleFunction> =
asSequence()
.filterIsInstance<IrSimpleFunction>()
.filter { it.dispatchReceiverParameter != null }
.map { it.realOverrideTarget }
.filter { it.isOverridableOrOverrides }
.distinct()
.toList()
fun IrClass.getSuperClass(builtIns: IrBuiltIns): IrClass? =
when (this) {
builtIns.anyClass.owner -> null
else -> {
superTypes
.map { it.classifierOrFail.owner as IrClass }
.singleOrNull { !it.isInterface } ?: builtIns.anyClass.owner
}
}
fun IrClass.allFields(builtIns: IrBuiltIns): List<IrField> =
getSuperClass(builtIns)?.allFields(builtIns).orEmpty() + declarations.filterIsInstance<IrField>()
@@ -0,0 +1,70 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.ir2wasm
import org.jetbrains.kotlin.wasm.ir.WasmSymbol
// Representation of constant data in Wasm memory
sealed class ConstantDataElement {
abstract val sizeInBytes: Int
abstract fun dump(indent: String = "", startAddress: Int = 0): String
abstract fun toBytes(): ByteArray
}
private fun addressToString(address: Int): String =
address.toString().padEnd(6, ' ')
class ConstantDataIntField(val name: String, val value: WasmSymbol<Int>) : ConstantDataElement() {
constructor(name: String, value: Int) : this(name, WasmSymbol(value))
override fun toBytes(): ByteArray = value.owner.toLittleEndianBytes()
override fun dump(indent: String, startAddress: Int): String {
return "${addressToString(startAddress)}: $indent i32 : ${value.owner} ;; $name\n"
}
override val sizeInBytes: Int = 4
}
class ConstantDataIntArray(val name: String, val value: List<WasmSymbol<Int>>) : ConstantDataElement() {
override fun toBytes(): ByteArray {
return value.fold(byteArrayOf()) { acc, el -> acc + el.owner.toLittleEndianBytes() }
}
override fun dump(indent: String, startAddress: Int): String {
if (value.isEmpty()) return ""
return "${addressToString(startAddress)}: $indent i32[] : ${value.map { it.owner }.toIntArray().contentToString()} ;; $name\n"
}
override val sizeInBytes: Int = value.size * 4
}
class ConstantDataStruct(val name: String, val elements: List<ConstantDataElement>) : ConstantDataElement() {
override fun toBytes(): ByteArray {
return elements.fold(byteArrayOf()) { acc, el -> acc + el.toBytes() }
}
override fun dump(indent: String, startAddress: Int): String {
var res = "$indent;; $name\n"
var elemStartAddr = startAddress
for (el in elements) {
res += el.dump("$indent ", elemStartAddr)
elemStartAddr += el.sizeInBytes
}
return res
}
override val sizeInBytes: Int = elements.map { it.sizeInBytes }.sum()
}
fun Int.toLittleEndianBytes(): ByteArray {
return ByteArray(4) {
(this ushr (it * 8)).toByte()
}
}
@@ -0,0 +1,320 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.ir2wasm
import org.jetbrains.kotlin.backend.common.ir.isOverridableOrOverrides
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.backend.wasm.utils.*
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.wasm.ir.*
class DeclarationGenerator(val context: WasmModuleCodegenContext) : IrElementVisitorVoid {
// Shortcuts
private val backendContext: WasmBackendContext = context.backendContext
private val irBuiltIns: IrBuiltIns = backendContext.irBuiltIns
override fun visitElement(element: IrElement) {
error("Unexpected element of type ${element::class}")
}
override fun visitTypeAlias(declaration: IrTypeAlias) {
// Type aliases are not material
}
override fun visitFunction(declaration: IrFunction) {
// Inline class constructors are currently empty
if (declaration is IrConstructor && backendContext.inlineClassesUtils.isClassInlineLike(declaration.parentAsClass))
return
val importedName = declaration.getWasmImportAnnotation()
val isIntrinsic = declaration.hasWasmReinterpretAnnotation() || declaration.getWasmOpAnnotation() != null
if (isIntrinsic) {
return
}
if (declaration.isFakeOverride)
return
// Generate function type
val watName = declaration.fqNameWhenAvailable.toString()
val irParameters = declaration.getEffectiveValueParameters()
val wasmFunctionType =
WasmFunctionType(
name = watName,
parameterTypes = irParameters.map {
val t = context.transformValueParameterType(it)
if (importedName != null && t is WasmRefNullType) {
WasmEqRef
} else {
t
}
},
resultTypes = listOfNotNull(
context.transformResultType(declaration.returnType).let {
if (importedName != null && it is WasmRefNullType) WasmEqRef else it
}
)
)
context.defineFunctionType(declaration.symbol, wasmFunctionType)
if (declaration is IrSimpleFunction) {
if (declaration.modality == Modality.ABSTRACT) return
if (declaration.isOverridableOrOverrides) {
// Register function as virtual, meaning this function
// will be stored Wasm table and could be called indirectly.
context.registerVirtualFunction(declaration.symbol)
}
}
assert(declaration == declaration.realOverrideTarget) {
"Sanity check that $declaration is a real function that can be used in calls"
}
if (importedName != null) {
// Imported functions don't have bodies. Declaring the signature:
context.defineFunction(
declaration.symbol,
WasmFunction.Imported(watName, wasmFunctionType, importedName)
)
// TODO: Support re-export of imported functions.
return
}
val function = WasmFunction.Defined(watName, wasmFunctionType)
val functionCodegenContext = WasmFunctionCodegenContextImpl(
declaration,
function,
backendContext,
context
)
for (irParameter in irParameters) {
functionCodegenContext.defineLocal(irParameter.symbol)
}
val exprGen = functionCodegenContext.bodyGen
val bodyBuilder = BodyGenerator(functionCodegenContext)
when (val body = declaration.body) {
is IrBlockBody ->
for (statement in body.statements) {
bodyBuilder.statementToWasmInstruction(statement)
}
is IrExpressionBody ->
bodyBuilder.generateExpression(body.expression)
else -> error("Unexpected body $body")
}
// Return implicit this from constructions to avoid extra tmp
// variables on constructor call sites.
// TODO: Redesign construction scheme.
if (declaration is IrConstructor) {
exprGen.buildGetLocal(/*implicit this*/ function.locals[0])
exprGen.buildInstr(WasmOp.RETURN)
}
// Add unreachable if function returns something but not as a last instruction.
if (wasmFunctionType.resultTypes.isNotEmpty() && declaration.body is IrBlockBody) {
// TODO: Add unreachable only if needed
exprGen.buildUnreachable()
}
context.defineFunction(declaration.symbol, function)
if (declaration == backendContext.startFunction)
context.setStartFunction(function)
if (declaration.isExported(backendContext)) {
context.addExport(
WasmExport.Function(
field = function,
// TODO: Add ability to specify exported name.
name = declaration.name.identifier
)
)
}
}
override fun visitClass(declaration: IrClass) {
if (declaration.isAnnotationClass) return
val symbol = declaration.symbol
if (declaration.isInterface) {
context.registerInterface(symbol)
} else {
val nameStr = declaration.fqNameWhenAvailable.toString()
val structType = WasmStructDeclaration(
name = nameStr,
fields = declaration.allFields(irBuiltIns).map {
WasmStructFieldDeclaration(
name = it.name.toString(),
type = context.transformType(it.type),
isMutable = true
)
}
)
context.defineStructType(symbol, structType)
var depth = 2
val metadata = context.getClassMetadata(symbol)
var subMetadata = metadata
while (true) {
subMetadata = subMetadata.superClass ?: break
depth++
}
val initBody = mutableListOf<WasmInstr>()
val wasmExpressionGenerator = WasmIrExpressionBuilder(initBody)
val superClass = metadata.superClass
if (superClass != null) {
val superRTT = context.referenceClassRTT(superClass.klass.symbol)
wasmExpressionGenerator.buildGetGlobal(superRTT)
} else {
wasmExpressionGenerator.buildRttCanon(WasmRefType(WasmHeapType.Simple.Eq))
}
wasmExpressionGenerator.buildRttSub(
WasmRefType(WasmHeapType.Type(WasmSymbol(structType)))
)
val rtt = WasmGlobal(
name = "rtt_of_$nameStr",
isMutable = false,
type = WasmRtt(depth, WasmHeapType.Type(WasmSymbol(structType))),
init = initBody
)
context.defineRTT(symbol, rtt)
context.registerClass(symbol)
context.generateTypeInfo(symbol, binaryDataStruct(metadata))
}
for (member in declaration.declarations) {
member.acceptVoid(this)
}
}
private fun binaryDataStruct(classMetadata: ClassMetadata): ConstantDataStruct {
val invalidIndex = -1
val superClass = classMetadata.superClass?.klass
val superClassSymbol: WasmSymbol<Int> =
superClass?.let { context.referenceClassId(it.symbol) } ?: WasmSymbol(invalidIndex)
val superTypeField =
ConstantDataIntField("Super class", superClassSymbol)
val interfacesArray = ConstantDataIntArray(
"data",
classMetadata.interfaces.map { context.referenceInterfaceId(it.symbol) }
)
val interfacesArraySize = ConstantDataIntField(
"size",
interfacesArray.value.size
)
val implementedInterfacesArrayWithSize = ConstantDataStruct(
"Implemented interfaces array",
listOf(interfacesArraySize, interfacesArray)
)
val vtableSizeField = ConstantDataIntField(
"V-table length",
classMetadata.virtualMethods.size
)
val vtableArray = ConstantDataIntArray(
"V-table",
classMetadata.virtualMethods.map {
if (it.function.modality == Modality.ABSTRACT) {
WasmSymbol(invalidIndex)
} else {
context.referenceVirtualFunctionId(it.function.symbol)
}
}
)
val signaturesArray = ConstantDataIntArray(
"Signatures",
classMetadata.virtualMethods.map {
if (it.function.modality == Modality.ABSTRACT) {
WasmSymbol(invalidIndex)
} else {
context.referenceSignatureId(it.signature)
}
}
)
return ConstantDataStruct(
"Class TypeInfo: ${classMetadata.klass.fqNameWhenAvailable} ",
listOf(
superTypeField,
vtableSizeField,
vtableArray,
signaturesArray,
implementedInterfacesArrayWithSize,
)
)
}
override fun visitField(declaration: IrField) {
// Member fields are generated as part of struct type
if (!declaration.isStatic) return
val wasmType = context.transformType(declaration.type)
val initBody = mutableListOf<WasmInstr>()
val wasmExpressionGenerator = WasmIrExpressionBuilder(initBody)
generateDefaultInitializerForType(wasmType, wasmExpressionGenerator)
val global = WasmGlobal(
name = declaration.fqNameWhenAvailable.toString(),
type = wasmType,
isMutable = true,
// All globals are currently initialized in start function
init = initBody
)
context.defineGlobal(declaration.symbol, global)
}
}
fun generateDefaultInitializerForType(type: WasmType, g: WasmExpressionBuilder) = when (type) {
WasmI32 -> g.buildConstI32(0)
WasmI1 -> g.buildConstI32(0)
WasmI64 -> g.buildConstI64(0)
WasmF32 -> g.buildConstF32(0f)
WasmF64 -> g.buildConstF64(0.0)
is WasmRefNullType -> g.buildRefNull(type.heapType)
is WasmExternRef -> g.buildRefNull(WasmHeapType.Simple.Extern)
WasmUnreachableType -> error("Unreachable type can't be initialized")
else -> error("Unknown value type ${type.name}")
}
fun IrFunction.getEffectiveValueParameters(): List<IrValueParameter> {
val implicitThis = if (this is IrConstructor) parentAsClass.thisReceiver!! else null
return listOfNotNull(implicitThis, dispatchReceiverParameter, extensionReceiverParameter) + valueParameters
}
fun IrFunction.isExported(context: WasmBackendContext): Boolean =
visibility == DescriptorVisibilities.PUBLIC && fqNameWhenAvailable in context.additionalExportedDeclarations
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.ir2wasm
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.jsAssignment
import org.jetbrains.kotlin.js.backend.ast.JsArrayLiteral
import org.jetbrains.kotlin.js.backend.ast.JsBlock
import org.jetbrains.kotlin.js.backend.ast.JsNameRef
import org.jetbrains.kotlin.js.backend.ast.JsStringLiteral
fun generateStringLiteralsSupport(literals: List<String>): String {
return JsBlock(
jsAssignment(
JsNameRef("stringLiterals", "runtime"),
JsArrayLiteral(literals.map { JsStringLiteral(it) })
).makeStmt()
).toString()
}
@@ -0,0 +1,118 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.ir2wasm
import org.jetbrains.kotlin.wasm.ir.*
import org.jetbrains.kotlin.backend.wasm.utils.hasWasmForeignAnnotation
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.util.getInlineClassUnderlyingType
import org.jetbrains.kotlin.ir.util.isInterface
class WasmTypeTransformer(
val context: WasmBaseCodegenContext,
val builtIns: IrBuiltIns
) {
fun IrType.toWasmResultType(): WasmType? =
when (this) {
builtIns.unitType,
builtIns.nothingType ->
null
else ->
toWasmValueType()
}
fun IrType.toWasmBlockResultType(): WasmType? =
when (this) {
builtIns.unitType ->
null
// TODO: Lower blocks with Nothing type?
builtIns.nothingType ->
WasmUnreachableType
else ->
toWasmValueType()
}
fun IrType.toStructType(): WasmType =
WasmRefNullType(WasmHeapType.Type(context.referenceStructType(erasedUpperBound?.symbol ?: builtIns.anyClass)))
fun IrType.toBoxedInlineClassType(): WasmType =
toStructType()
fun IrType.toWasmValueType(): WasmType =
when (this) {
builtIns.booleanType,
builtIns.byteType,
builtIns.shortType,
builtIns.intType,
builtIns.charType ->
WasmI32
builtIns.longType ->
WasmI64
builtIns.floatType ->
WasmF32
builtIns.doubleType ->
WasmF64
builtIns.stringType ->
WasmExternRef
builtIns.nothingNType ->
WasmExternRef
// Value will not be created. Just using a random Wasm type.
builtIns.nothingType ->
WasmExternRef
else -> {
val klass = this.getClass()
val ic = context.backendContext.inlineClassesUtils.getInlinedClass(this)
if (klass != null && klass.hasWasmForeignAnnotation()) {
WasmExternRef
} else if (ic != null) {
getInlineClassUnderlyingType(ic).toWasmValueType()
} else {
this.toStructType()
}
}
}
}
// Return null if upper bound is Any
val IrTypeParameter.erasedUpperBound: IrClass?
get() {
// Pick the (necessarily unique) non-interface upper bound if it exists
for (type in superTypes) {
return type.erasedUpperBound ?: continue
}
return null
}
val IrType.erasedUpperBound: IrClass?
get() = when (val classifier = classifierOrNull) {
is IrClassSymbol -> classifier.owner
is IrTypeParameterSymbol -> classifier.owner.erasedUpperBound
else -> throw IllegalStateException()
}.let {
if (it?.isInterface == true) null
else it
}
@@ -0,0 +1,45 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.ir2wasm
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.wasm.ir.*
import org.jetbrains.kotlin.backend.wasm.lower.WasmSignature
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrType
interface WasmBaseCodegenContext {
val backendContext: WasmBackendContext
fun referenceFunction(irFunction: IrFunctionSymbol): WasmSymbol<WasmFunction>
fun referenceGlobal(irField: IrFieldSymbol): WasmSymbol<WasmGlobal>
fun referenceStructType(irClass: IrClassSymbol): WasmSymbol<WasmStructDeclaration>
fun referenceFunctionType(irFunction: IrFunctionSymbol): WasmSymbol<WasmFunctionType>
fun referenceClassId(irClass: IrClassSymbol): WasmSymbol<Int>
fun referenceInterfaceId(irInterface: IrClassSymbol): WasmSymbol<Int>
fun referenceVirtualFunctionId(irFunction: IrSimpleFunctionSymbol): WasmSymbol<Int>
fun referenceClassRTT(irClass: IrClassSymbol): WasmSymbol<WasmGlobal>
fun referenceSignatureId(signature: WasmSignature): WasmSymbol<Int>
fun referenceStringLiteral(string: String): WasmSymbol<Int>
fun transformType(irType: IrType): WasmType
fun transformBoxedType(irType: IrType): WasmType
fun transformValueParameterType(irValueParameter: IrValueParameter): WasmType
fun transformResultType(irType: IrType): WasmType?
fun transformBlockResultType(irType: IrType): WasmType?
fun getStructFieldRef(field: IrField): WasmSymbol<Int>
fun getClassMetadata(irClass: IrClassSymbol): ClassMetadata
}
@@ -0,0 +1,207 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.ir2wasm
import org.jetbrains.kotlin.wasm.ir.*
import org.jetbrains.kotlin.backend.wasm.lower.WasmSignature
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
import org.jetbrains.kotlin.ir.declarations.IrExternalPackageFragment
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.ir.util.getPackageFragment
class WasmCompiledModuleFragment {
val functions =
ReferencableAndDefinable<IrFunctionSymbol, WasmFunction>()
val globals =
ReferencableAndDefinable<IrFieldSymbol, WasmGlobal>()
val functionTypes =
ReferencableAndDefinable<IrFunctionSymbol, WasmFunctionType>()
val structTypes =
ReferencableAndDefinable<IrClassSymbol, WasmStructDeclaration>()
val classIds =
ReferencableElements<IrClassSymbol, Int>()
val interfaceId =
ReferencableElements<IrClassSymbol, Int>()
val virtualFunctionId =
ReferencableElements<IrFunctionSymbol, Int>()
val signatureId =
ReferencableElements<WasmSignature, Int>()
val stringLiteralId =
ReferencableElements<String, Int>()
val runtimeTypes =
ReferencableAndDefinable<IrClassSymbol, WasmGlobal>()
val classes = mutableListOf<IrClassSymbol>()
val interfaces = mutableListOf<IrClassSymbol>()
val virtualFunctions = mutableListOf<IrSimpleFunctionSymbol>()
val signatures = LinkedHashSet<WasmSignature>()
val stringLiterals = mutableListOf<String>()
val typeInfo =
ReferencableAndDefinable<IrClassSymbol, ConstantDataElement>()
val exports = mutableListOf<WasmExport<*>>()
//
var startFunction: WasmFunction? = null
open class ReferencableElements<Ir, Wasm : Any> {
val unbound = mutableMapOf<Ir, WasmSymbol<Wasm>>()
fun reference(ir: Ir): WasmSymbol<Wasm> {
val declaration = (ir as? IrSymbol)?.owner as? IrDeclarationWithName
if (declaration != null) {
val packageFragment = declaration.getPackageFragment()
?: error("Referencing declaration without package fragment ${declaration.fqNameWhenAvailable}")
if (packageFragment is IrExternalPackageFragment) {
error("Referencing declaration without package fragment ${declaration.fqNameWhenAvailable}")
}
}
return unbound.getOrPut(ir) { WasmSymbol() }
}
}
class ReferencableAndDefinable<Ir, Wasm : Any> : ReferencableElements<Ir, Wasm>() {
fun define(ir: Ir, wasm: Wasm) {
if (ir in defined)
error("Trying to redefine element: IR: $ir Wasm: $wasm")
elements += wasm
defined[ir] = wasm
wasmToIr[wasm] = ir
}
val defined = LinkedHashMap<Ir, Wasm>()
val elements = mutableListOf<Wasm>()
val wasmToIr = mutableMapOf<Wasm, Ir>()
}
@OptIn(ExperimentalUnsignedTypes::class)
fun linkWasmCompiledFragments(): WasmModule {
bind(functions.unbound, functions.defined)
bind(globals.unbound, globals.defined)
bind(functionTypes.unbound, functionTypes.defined)
bind(structTypes.unbound, structTypes.defined)
bind(runtimeTypes.unbound, runtimeTypes.defined)
val klassIds = mutableMapOf<IrClassSymbol, Int>()
var classId = 0
for (typeInfoElement in typeInfo.elements) {
val ir = typeInfo.wasmToIr.getValue(typeInfoElement)
klassIds[ir] = classId
classId += typeInfoElement.sizeInBytes
}
bind(classIds.unbound, klassIds)
bindIndices(virtualFunctionId.unbound, virtualFunctions)
bindIndices(signatureId.unbound, signatures.toList())
bindIndices(interfaceId.unbound, interfaces)
bindIndices(stringLiteralId.unbound, stringLiterals)
val data = typeInfo.elements.map {
val ir = typeInfo.wasmToIr.getValue(it)
val id = klassIds.getValue(ir)
val offset = mutableListOf<WasmInstr>()
WasmIrExpressionBuilder(offset).buildConstI32(id)
WasmData(WasmDataMode.Active(0, offset), it.toBytes())
}
val logTypeInfo = false
if (logTypeInfo) {
println("Signatures: ")
for ((index, signature: WasmSignature) in signatures.withIndex()) {
println(" -- $index $signature")
}
println("Interfaces: ")
for ((index, iface: IrClassSymbol) in interfaces.withIndex()) {
println(" -- $index ${iface.owner.fqNameWhenAvailable}")
}
println("Virtual functions: ")
for ((index, vf: IrSimpleFunctionSymbol) in virtualFunctions.withIndex()) {
println(" -- $index ${vf.owner.fqNameWhenAvailable}")
}
println(
ConstantDataStruct("typeInfo", typeInfo.elements).dump("", 0)
)
}
val table = WasmTable(
limits = WasmLimits(virtualFunctions.size.toUInt(), virtualFunctions.size.toUInt()),
elementType = WasmFuncRef,
)
val offsetExpr = mutableListOf<WasmInstr>()
WasmIrExpressionBuilder(offsetExpr).buildConstI32(0)
val elements = WasmElement(
WasmFuncRef,
values = virtualFunctions.map {
WasmTable.Value.Function(functions.defined.getValue(it))
},
WasmElement.Mode.Active(table, offsetExpr)
)
val typeInfoSize = classId
val memorySizeInPages = (typeInfoSize / 65_536) + 1
val memory = WasmMemory(WasmLimits(memorySizeInPages.toUInt(), memorySizeInPages.toUInt()))
val importedFunctions = functions.elements.filterIsInstance<WasmFunction.Imported>()
// Sorting by depth for a valid init order
val sortedRttGlobals = runtimeTypes.elements.sortedBy { (it.type as WasmRtt).depth }
val module = WasmModule(
functionTypes = functionTypes.elements,
structs = structTypes.elements,
importsInOrder = importedFunctions,
importedFunctions = importedFunctions,
definedFunctions = functions.elements.filterIsInstance<WasmFunction.Defined>(),
tables = listOf(table),
memories = listOf(memory),
globals = globals.elements + sortedRttGlobals,
exports = exports,
startFunction = startFunction!!,
elements = listOf(elements),
data = data
)
module.calculateIds()
return module
}
}
fun <IrSymbolType, WasmDeclarationType : Any, WasmSymbolType : WasmSymbol<WasmDeclarationType>> bind(
unbound: Map<IrSymbolType, WasmSymbolType>,
defined: Map<IrSymbolType, WasmDeclarationType>
) {
unbound.forEach { (irSymbol, wasmSymbol) ->
if (irSymbol !in defined)
error("Can't link symbol ${irSymbolDebugDump(irSymbol)}")
wasmSymbol.bind(defined.getValue(irSymbol))
}
}
private fun irSymbolDebugDump(symbol: Any?): String =
when (symbol) {
is IrFunctionSymbol -> "function ${symbol.owner.fqNameWhenAvailable}"
is IrClassSymbol -> "class ${symbol.owner.fqNameWhenAvailable}"
else -> symbol.toString()
}
fun <IrSymbolType> bindIndices(
unbound: Map<IrSymbolType, WasmSymbol<Int>>,
ordered: List<IrSymbolType>
) {
unbound.forEach { (irSymbol, wasmSymbol) ->
val index = ordered.indexOf(irSymbol)
if (index == -1)
error("Can't link symbol with indices ${irSymbolDebugDump(irSymbol)}")
wasmSymbol.bind(index)
}
}
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.ir2wasm
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.IrLoop
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.wasm.ir.WasmExpressionBuilder
import org.jetbrains.kotlin.wasm.ir.WasmInstr
import org.jetbrains.kotlin.wasm.ir.WasmLocal
enum class LoopLabelType { BREAK, CONTINUE }
interface WasmFunctionCodegenContext : WasmBaseCodegenContext {
val irFunction: IrFunction
fun defineLocal(irValueDeclaration: IrValueSymbol)
fun referenceLocal(irValueDeclaration: IrValueSymbol): WasmLocal
fun referenceLocal(index: Int): WasmLocal
fun defineLoopLevel(irLoop: IrLoop, labelType: LoopLabelType, level: Int)
fun referenceLoopLevel(irLoop: IrLoop, labelType: LoopLabelType): Int
val bodyGen: WasmExpressionBuilder
}
@@ -0,0 +1,61 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.ir2wasm
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.expressions.IrLoop
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.wasm.ir.*
class WasmFunctionCodegenContextImpl(
override val irFunction: IrFunction,
private val wasmFunction: WasmFunction.Defined,
override val backendContext: WasmBackendContext,
private val referencing: WasmBaseCodegenContext
) : WasmBaseCodegenContext by referencing,
WasmFunctionCodegenContext {
override val bodyGen: WasmExpressionBuilder =
WasmIrExpressionBuilder(wasmFunction.instructions)
private val wasmLocals = LinkedHashMap<IrValueSymbol, WasmLocal>()
private val loopLevels = LinkedHashMap<Pair<IrLoop, LoopLabelType>, Int>()
override fun defineLocal(irValueDeclaration: IrValueSymbol) {
assert(irValueDeclaration !in wasmLocals) { "Redefinition of local" }
val owner = irValueDeclaration.owner
val wasmLocal = WasmLocal(
wasmFunction.locals.size,
owner.name.asString(),
if (owner is IrValueParameter) transformValueParameterType(owner) else transformType(owner.type),
isParameter = irValueDeclaration is IrValueParameterSymbol
)
wasmLocals[irValueDeclaration] = wasmLocal
wasmFunction.locals += wasmLocal
}
override fun referenceLocal(irValueDeclaration: IrValueSymbol): WasmLocal {
return wasmLocals.getValue(irValueDeclaration)
}
override fun referenceLocal(index: Int): WasmLocal {
return wasmFunction.locals[index]
}
override fun defineLoopLevel(irLoop: IrLoop, labelType: LoopLabelType, level: Int) {
val loopKey = Pair(irLoop, labelType)
assert(loopKey !in loopLevels) { "Redefinition of loop" }
loopLevels[loopKey] = level
}
override fun referenceLoopLevel(irLoop: IrLoop, labelType: LoopLabelType): Int {
return loopLevels.getValue(Pair(irLoop, labelType))
}
}
@@ -0,0 +1,34 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.ir2wasm
import org.jetbrains.kotlin.wasm.ir.*
import org.jetbrains.kotlin.backend.wasm.ir2wasm.ConstantDataElement
import org.jetbrains.kotlin.backend.wasm.ir2wasm.WasmBaseCodegenContext
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
/**
* Interface for generating WebAssembly module.
*/
interface WasmModuleCodegenContext : WasmBaseCodegenContext {
fun defineFunction(irFunction: IrFunctionSymbol, wasmFunction: WasmFunction)
fun defineGlobal(irField: IrFieldSymbol, wasmGlobal: WasmGlobal)
fun defineStructType(irClass: IrClassSymbol, wasmStruct: WasmStructDeclaration)
fun defineRTT(irClass: IrClassSymbol, wasmGlobal: WasmGlobal)
fun defineFunctionType(irFunction: IrFunctionSymbol, wasmFunctionType: WasmFunctionType)
fun setStartFunction(wasmFunction: WasmFunction)
fun addExport(wasmExport: WasmExport<*>)
fun registerVirtualFunction(irFunction: IrSimpleFunctionSymbol)
fun registerInterface(irInterface: IrClassSymbol)
fun registerClass(irClass: IrClassSymbol)
fun generateTypeInfo(irClass: IrClassSymbol, typeInfo: ConstantDataElement)
}
@@ -0,0 +1,169 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.ir2wasm
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.wasm.ir.*
import org.jetbrains.kotlin.backend.wasm.lower.WasmSignature
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.types.isNothing
import org.jetbrains.kotlin.ir.util.isFunction
import org.jetbrains.kotlin.ir.util.parentAsClass
class WasmModuleCodegenContextImpl(
override val backendContext: WasmBackendContext,
private val wasmFragment: WasmCompiledModuleFragment
) : WasmModuleCodegenContext {
private val typeTransformer =
WasmTypeTransformer(this, backendContext.irBuiltIns)
override fun transformType(irType: IrType): WasmType {
return with(typeTransformer) { irType.toWasmValueType() }
}
override fun transformBoxedType(irType: IrType): WasmType {
return with(typeTransformer) { irType.toBoxedInlineClassType() }
}
override fun transformValueParameterType(irValueParameter: IrValueParameter): WasmType {
return with(typeTransformer) {
if (context.backendContext.inlineClassesUtils.shouldValueParameterBeBoxed(irValueParameter)) {
irValueParameter.type.toBoxedInlineClassType()
} else {
irValueParameter.type.toWasmValueType()
}
}
}
override fun transformResultType(irType: IrType): WasmType? {
return with(typeTransformer) { irType.toWasmResultType() }
}
override fun transformBlockResultType(irType: IrType): WasmType? {
return with(typeTransformer) { irType.toWasmBlockResultType() }
}
override fun referenceStringLiteral(string: String): WasmSymbol<Int> {
wasmFragment.stringLiterals.add(string)
return wasmFragment.stringLiteralId.reference(string)
}
override fun generateTypeInfo(irClass: IrClassSymbol, typeInfo: ConstantDataElement) {
wasmFragment.typeInfo.define(irClass, typeInfo)
}
override fun setStartFunction(wasmFunction: WasmFunction) {
wasmFragment.startFunction = wasmFunction
}
override fun addExport(wasmExport: WasmExport<*>) {
wasmFragment.exports += wasmExport
}
override fun registerVirtualFunction(irFunction: IrSimpleFunctionSymbol) {
wasmFragment.virtualFunctions += irFunction
}
override fun registerInterface(irInterface: IrClassSymbol) {
wasmFragment.interfaces += irInterface
}
override fun registerClass(irClass: IrClassSymbol) {
wasmFragment.classes += irClass
}
override fun defineFunction(irFunction: IrFunctionSymbol, wasmFunction: WasmFunction) {
wasmFragment.functions.define(irFunction, wasmFunction)
}
override fun defineGlobal(irField: IrFieldSymbol, wasmGlobal: WasmGlobal) {
wasmFragment.globals.define(irField, wasmGlobal)
}
override fun defineStructType(irClass: IrClassSymbol, wasmStruct: WasmStructDeclaration) {
wasmFragment.structTypes.define(irClass, wasmStruct)
}
override fun defineRTT(irClass: IrClassSymbol, wasmGlobal: WasmGlobal) {
wasmFragment.runtimeTypes.define(irClass, wasmGlobal)
}
override fun defineFunctionType(irFunction: IrFunctionSymbol, wasmFunctionType: WasmFunctionType) {
wasmFragment.functionTypes.define(irFunction, wasmFunctionType)
}
private val classMetadataCache = mutableMapOf<IrClassSymbol, ClassMetadata>()
override fun getClassMetadata(irClass: IrClassSymbol): ClassMetadata =
classMetadataCache.getOrPut(irClass) {
val superClass = irClass.owner.getSuperClass(backendContext.irBuiltIns)
val superClassMetadata = superClass?.let { getClassMetadata(it.symbol) }
ClassMetadata(
irClass.owner,
superClassMetadata,
backendContext.irBuiltIns
)
}
override fun referenceFunction(irFunction: IrFunctionSymbol): WasmSymbol<WasmFunction> =
wasmFragment.functions.reference(irFunction)
override fun referenceGlobal(irField: IrFieldSymbol): WasmSymbol<WasmGlobal> =
wasmFragment.globals.reference(irField)
override fun referenceStructType(irClass: IrClassSymbol): WasmSymbol<WasmStructDeclaration> {
val type = irClass.defaultType
require(!type.isNothing()) {
"Can't reference Nothing type"
}
return wasmFragment.structTypes.reference(irClass)
}
override fun referenceClassRTT(irClass: IrClassSymbol): WasmSymbol<WasmGlobal> =
wasmFragment.runtimeTypes.reference(irClass)
override fun referenceFunctionType(irFunction: IrFunctionSymbol): WasmSymbol<WasmFunctionType> =
wasmFragment.functionTypes.reference(irFunction)
override fun referenceClassId(irClass: IrClassSymbol): WasmSymbol<Int> =
wasmFragment.classIds.reference(irClass)
override fun referenceInterfaceId(irInterface: IrClassSymbol): WasmSymbol<Int> {
// HACK to substitute kotlin.Function5 with kotlin.wasm.internal.Function5
val defaultType = irInterface.defaultType
if (defaultType.isFunction()) {
val n = irInterface.owner.typeParameters.size - 1
return wasmFragment.interfaceId.reference(backendContext.wasmSymbols.functionN(n))
}
return wasmFragment.interfaceId.reference(irInterface)
}
override fun referenceVirtualFunctionId(irFunction: IrSimpleFunctionSymbol): WasmSymbol<Int> {
if (irFunction.owner.modality == Modality.ABSTRACT)
error("Abstract functions are not stored in table")
return wasmFragment.virtualFunctionId.reference(irFunction)
}
override fun referenceSignatureId(signature: WasmSignature): WasmSymbol<Int> {
wasmFragment.signatures.add(signature)
return wasmFragment.signatureId.reference(signature)
}
override fun getStructFieldRef(field: IrField): WasmSymbol<Int> {
val klass = field.parentAsClass
val metadata = getClassMetadata(klass.symbol)
val fieldId = metadata.fields.indexOf(field)
return WasmSymbol(fieldId)
}
}
@@ -0,0 +1,41 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.ir2wasm
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.IrPackageFragment
import org.jetbrains.kotlin.ir.visitors.acceptVoid
class WasmModuleFragmentGenerator(
backendContext: WasmBackendContext,
wasmModuleFragment: WasmCompiledModuleFragment
) {
private val declarationGenerator =
DeclarationGenerator(
WasmModuleCodegenContextImpl(
backendContext,
wasmModuleFragment
)
)
fun generateModule(irModuleFragment: IrModuleFragment) {
for (irFile in irModuleFragment.files) {
generatePackageFragment(irFile)
}
}
fun generatePackageFragment(irPackageFragment: IrPackageFragment) {
for (irDeclaration in irPackageFragment.declarations) {
generateDeclaration(irDeclaration)
}
}
fun generateDeclaration(irDeclaration: IrDeclaration) {
irDeclaration.acceptVoid(declarationGenerator)
}
}
@@ -1,14 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.lower
import org.jetbrains.kotlin.ir.backend.js.lower.AbstractBlockDecomposerLowering
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.ir.expressions.IrExpression
class WasmBlockDecomposerLowering(val context: WasmBackendContext) : AbstractBlockDecomposerLowering(context) {
override fun unreachableExpression(): IrExpression = TODO()
}
@@ -6,40 +6,139 @@
package org.jetbrains.kotlin.backend.wasm.lower package org.jetbrains.kotlin.backend.wasm.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.ir.backend.js.utils.isEqualsInheritedFromAny
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irComposite
import org.jetbrains.kotlin.ir.builders.irInt
import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.ir.util.functions
import org.jetbrains.kotlin.ir.util.irCall import org.jetbrains.kotlin.ir.util.irCall
import org.jetbrains.kotlin.ir.util.render import org.jetbrains.kotlin.ir.util.isFunction
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.util.isNullConst
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.util.OperatorNameConventions
class BuiltInsLowering(val context: WasmBackendContext) : FileLoweringPass { class BuiltInsLowering(val context: WasmBackendContext) : FileLoweringPass {
private val irBuiltins = context.irBuiltIns private val irBuiltins = context.irBuiltIns
private val symbols = context.wasmSymbols private val symbols = context.wasmSymbols
fun transformCall(call: IrCall): IrExpression { private fun IrType.findEqualsMethod(): IrSimpleFunction {
val klass = getClass() ?: irBuiltins.anyClass.owner
return klass.functions.single { it.isEqualsInheritedFromAny() }
}
fun transformCall(
call: IrCall,
builder: DeclarationIrBuilder
): IrExpression {
when (val symbol = call.symbol) { when (val symbol = call.symbol) {
irBuiltins.eqeqSymbol, irBuiltins.eqeqeqSymbol, in irBuiltins.ieee754equalsFunByOperandType.values -> { irBuiltins.ieee754equalsFunByOperandType[irBuiltins.floatClass] -> {
if (call.getValueArgument(0)!!.type.isNullable() || call.getValueArgument(1)!!.type.isNullable()) {
return irCall(call, symbols.nullableFloatIeee754Equals)
}
return irCall(call, symbols.floatEqualityFunctions.getValue(irBuiltins.floatType))
}
irBuiltins.ieee754equalsFunByOperandType[irBuiltins.doubleClass] -> {
if (call.getValueArgument(0)!!.type.isNullable() || call.getValueArgument(1)!!.type.isNullable()) {
return irCall(call, symbols.nullableDoubleIeee754Equals)
}
return irCall(call, symbols.floatEqualityFunctions.getValue(irBuiltins.doubleType))
}
irBuiltins.eqeqSymbol -> {
val lhs = call.getValueArgument(0)!!
val rhs = call.getValueArgument(1)!!
val lhsType = lhs.type
val rhsType = rhs.type
if (lhsType == rhsType) {
val newSymbol = symbols.equalityFunctions[lhsType]
if (newSymbol != null) {
return irCall(call, newSymbol)
}
}
if (lhs.isNullConst()) {
return builder.irCall(symbols.refIsNull).apply { putValueArgument(0, rhs) }
}
if (rhs.isNullConst()) {
return builder.irCall(symbols.refIsNull).apply { putValueArgument(0, lhs) }
}
if (!lhsType.isNullable()) {
return irCall(call, lhsType.findEqualsMethod().symbol, argumentsAsReceivers = true)
}
return irCall(call, symbols.nullableEquals)
}
irBuiltins.eqeqeqSymbol -> {
val type = call.getValueArgument(0)!!.type val type = call.getValueArgument(0)!!.type
val newSymbol = symbols.equalityFunctions[type] val newSymbol = symbols.equalityFunctions[type] ?: symbols.floatEqualityFunctions[type] ?: symbols.refEq
?: error("Unsupported equality operator with type: ${type.render()}")
return irCall(call, newSymbol) return irCall(call, newSymbol)
} }
in symbols.irBuiltInsToWasmIntrinsics.keys -> {
val newSymbol = symbols.irBuiltInsToWasmIntrinsics[symbol]!! irBuiltins.checkNotNullSymbol -> {
return irCall(call, symbols.ensureNotNull).also {
it.putTypeArgument(0, call.type)
}
}
in symbols.comparisonBuiltInsToWasmIntrinsics.keys -> {
val newSymbol = symbols.comparisonBuiltInsToWasmIntrinsics[symbol]!!
return irCall(call, newSymbol) return irCall(call, newSymbol)
} }
// TODO: Implement
irBuiltins.noWhenBranchMatchedExceptionSymbol ->
return builder.irCall(symbols.wasmUnreachable, irBuiltins.nothingType)
// TODO: Implement
irBuiltins.illegalArgumentExceptionSymbol ->
return builder.irCall(symbols.wasmUnreachable, irBuiltins.nothingType)
irBuiltins.dataClassArrayMemberHashCodeSymbol -> {
// TODO: Implement
return builder.irComposite {
+call.getValueArgument(0)!!
+irInt(7777)
}
}
irBuiltins.dataClassArrayMemberToStringSymbol -> {
// TODO: Implement
return builder.irCall(symbols.anyNtoString).apply {
putValueArgument(0, call.getValueArgument(0))
}
}
} }
val nativeInvokeArity = getKotlinFunctionInvokeArity(call)
if (nativeInvokeArity != null) {
return irCall(call, symbols.functionNInvokeMethods[nativeInvokeArity])
}
return call return call
} }
private fun getKotlinFunctionInvokeArity(call: IrCall): Int? {
val simpleFunction = call.symbol.owner as? IrSimpleFunction ?: return null
val receiverType = simpleFunction.dispatchReceiverParameter?.type ?: return null
if (simpleFunction.isSuspend) return null
if (simpleFunction.name == OperatorNameConventions.INVOKE && receiverType.isFunction()) {
return simpleFunction.valueParameters.size
}
return null
}
override fun lower(irFile: IrFile) { override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(object : IrElementTransformerVoid() { val builder = context.createIrBuilder(irFile.symbol)
irFile.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
override fun visitCall(expression: IrCall): IrExpression { override fun visitCall(expression: IrCall): IrExpression {
val newExpression = transformCall(expression) val newExpression = transformCall(expression, builder)
newExpression.transformChildrenVoid(this) newExpression.transformChildrenVoid(this)
return newExpression return newExpression
} }
@@ -0,0 +1,91 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.lower
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.ir.isOverridableOrOverrides
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irImplicitCast
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.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrGetValue
import org.jetbrains.kotlin.ir.types.isAny
import org.jetbrains.kotlin.ir.util.isInterface
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
/**
* This lowering erases dispatch receiver types of virtual functions down to Any.
*
* WebAssembly function types are contravariant on their parameter types.
* But since child classes are not supertypes of parents, in order for virtual method
* reference to share the same v-table slot as parent's method, it's dispatch receiver type
* has to be erased at least down to type of the parent class.
*
* Current implementation is rather conservative:
* - Always erases parameter type down to Any
* - Inserts casts back to original type in every usage of dispatch receiver.
*
* Possible optimisations:
* - Instead of erasing type to Any, erase type down to least concrete supertype containing virtual method
* - Don't erase type at all if bridge will be needed anyway
* Cast receiver in bridge. This would keep precise type for direct calls
* - Cast `this` and assign it to local variable if dispatch receiver is used often
* - Don't cast if usages of `this` don't require precise type
* - Always use bridge + Wasm tail call
*
* Related issue: [https://github.com/WebAssembly/gc/issues/29]
*/
class EraseVirtualDispatchReceiverParametersTypes(val context: CommonBackendContext) : FileLoweringPass {
override fun lower(file: IrFile) {
file.acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitFunction(declaration: IrFunction) {
lower(declaration)
super.visitFunction(declaration)
}
})
}
fun lower(irFunction: IrFunction) {
// Lower only functions that override other functions
if (irFunction !is IrSimpleFunction) return
if (!irFunction.isOverridableOrOverrides) return
val oldReceiver = irFunction.dispatchReceiverParameter!!
val originalReceiverType = oldReceiver.type
// Interfaces in Wasm are erased to Any, so they already have appropriate type
if (originalReceiverType.isInterface() || originalReceiverType.isAny()) return
val builder = context.createIrBuilder(irFunction.symbol)
val newReceiver = oldReceiver.copyTo(irFunction, type = context.irBuiltIns.anyType)
irFunction.dispatchReceiverParameter = newReceiver
// Cast receiver usages back to original type
irFunction.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
if (expression.symbol == oldReceiver.symbol) {
return with(builder) {
irImplicitCast(irGet(newReceiver), originalReceiverType)
}
}
return expression
}
})
}
}
@@ -8,102 +8,22 @@ package org.jetbrains.kotlin.backend.wasm.lower
import org.jetbrains.kotlin.backend.common.ir.addChild import org.jetbrains.kotlin.backend.common.ir.addChild
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.backend.wasm.utils.hasExcludedFromCodegenAnnotation import org.jetbrains.kotlin.backend.wasm.utils.hasExcludedFromCodegenAnnotation
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
private val BODILESS_BUILTIN_CLASSES = listOf(
"kotlin.Nothing",
"kotlin.Array",
"kotlin.Any",
"kotlin.ByteArray",
"kotlin.CharArray",
"kotlin.ShortArray",
"kotlin.IntArray",
"kotlin.LongArray",
"kotlin.FloatArray",
"kotlin.DoubleArray",
"kotlin.BooleanArray",
"kotlin.Boolean",
"kotlin.Function",
"kotlin.Throwable",
"kotlin.Suppress",
"kotlin.SinceKotlin",
"kotlin.Deprecated",
"kotlin.ReplaceWith",
"kotlin.DeprecationLevel",
"kotlin.UnsafeVariance",
"kotlin.reflect.KType",
"kotlin.reflect.KTypeProjection",
"kotlin.reflect.Companion",
"kotlin.reflect.KTypeParameter",
"kotlin.reflect.KDeclarationContainer",
"kotlin.reflect.KProperty",
"kotlin.reflect.KProperty0",
"kotlin.reflect.KProperty1",
"kotlin.reflect.KProperty2",
"kotlin.reflect.KMutableProperty0",
"kotlin.reflect.KMutableProperty",
"kotlin.reflect.KMutableProperty1",
"kotlin.reflect.KMutableProperty2",
"kotlin.reflect.Accessor",
"kotlin.reflect.Getter",
"kotlin.reflect.KFunction",
"kotlin.reflect.KVariance",
"kotlin.reflect.KVisibility",
"kotlin.reflect.KClass",
"kotlin.reflect.KCallable",
"kotlin.reflect.KClassifier",
"kotlin.reflect.KParameter",
"kotlin.reflect.Kind",
"kotlin.reflect.KAnnotatedElement",
"kotlin.annotation.Target",
"kotlin.annotation.AnnotationTarget",
"kotlin.annotation.Retention",
"kotlin.annotation.AnnotationRetention",
"kotlin.annotation.MustBeDocumented",
"kotlin.Unit",
"kotlin.collections.BooleanIterator",
"kotlin.collections.CharIterator",
"kotlin.collections.ByteIterator",
"kotlin.collections.ShortIterator",
"kotlin.collections.IntIterator",
"kotlin.collections.FloatIterator",
"kotlin.collections.LongIterator",
"kotlin.collections.DoubleIterator",
"kotlin.internal.PlatformDependent",
"kotlin.CharSequence",
"kotlin.Annotation",
"kotlin.Comparable",
"kotlin.collections.Collection",
"kotlin.collections.Iterable",
"kotlin.collections.List",
"kotlin.collections.Map",
"kotlin.collections.Set",
"kotlin.collections.MutableCollection",
"kotlin.collections.MutableIterable",
"kotlin.collections.MutableSet",
"kotlin.collections.MutableList",
"kotlin.collections.MutableMap",
"kotlin.collections.Entry",
"kotlin.collections.MutableEntry",
"kotlin.Number",
"kotlin.Enum",
"kotlin.collections.Iterator",
"kotlin.collections.ListIterator",
"kotlin.collections.MutableIterator",
"kotlin.collections.MutableListIterator"
).map { FqName(it) }.toSet()
/**
* Move intrinsics marked with @ExcludedFromCodegen to special excluded files.
* All references to these declarations must be lowered or treated in a special way in a codegen.
*/
fun excludeDeclarationsFromCodegen(context: WasmBackendContext, module: IrModuleFragment) { fun excludeDeclarationsFromCodegen(context: WasmBackendContext, module: IrModuleFragment) {
fun isExcluded(declaration: IrDeclaration): Boolean { fun isExcluded(declaration: IrDeclaration): Boolean {
if (declaration is IrDeclarationWithName && declaration.fqNameWhenAvailable in BODILESS_BUILTIN_CLASSES) // Annotation can be applied to top-level declarations ...
return true
if (declaration.hasExcludedFromCodegenAnnotation()) if (declaration.hasExcludedFromCodegenAnnotation())
return true return true
// ... or files as a whole
val parentFile = declaration.parent as? IrFile val parentFile = declaration.parent as? IrFile
if (parentFile?.hasExcludedFromCodegenAnnotation() == true) if (parentFile?.hasExcludedFromCodegenAnnotation() == true)
return true return true
@@ -117,7 +37,8 @@ fun excludeDeclarationsFromCodegen(context: WasmBackendContext, module: IrModule
val d = it.next() as? IrDeclarationWithName ?: continue val d = it.next() as? IrDeclarationWithName ?: continue
if (isExcluded(d)) { if (isExcluded(d)) {
it.remove() it.remove()
context.excludedDeclarations.addChild(d) // Move to "excluded" package fragment preserving fq-name
context.getExcludedPackageFragment(file.fqName).addChild(d)
} }
} }
} }
@@ -0,0 +1,53 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.lower.at
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.builders.irSetField
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
/**
* Move initialization of global fields to start function.
*
* WebAssembly allows only constant expressions to be used directly in
* field initializers.
*
* TODO: Don't move constant expression initializers
* TODO: Make field initialization lazy. Needs design.
*/
class FieldInitializersLowering(val context: WasmBackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) {
val builder = context.createIrBuilder(context.startFunction.symbol)
val startFunctionBody = context.startFunction.body as IrBlockBody
irFile.acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitField(declaration: IrField) {
super.visitField(declaration)
if (!declaration.isStatic) return
val initValue: IrExpression = declaration.initializer?.expression ?: return
startFunctionBody.statements.add(
builder.at(initValue).irSetField(null, declaration, initValue)
)
// Replace initializer with default one
declaration.initializer = null
}
})
}
}
@@ -0,0 +1,86 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irComposite
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.backend.wasm.ir2wasm.erasedUpperBound
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
import org.jetbrains.kotlin.ir.builders.irImplicitCast
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.irCall
import org.jetbrains.kotlin.ir.util.isTypeParameter
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
/**
* This lowering adds implicit casts in places where erased generic function return type
* differs from expected type on the call site.
*/
class GenericReturnTypeLowering(val context: WasmBackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
override fun visitCall(expression: IrCall): IrExpression =
transformGenericCall(
super.visitCall(expression) as IrCall,
currentScope!!.scope.scopeOwnerSymbol
)
})
}
private fun IrType.eraseUpperBoundType(): IrType {
val type = erasedUpperBound?.defaultType ?: return context.irBuiltIns.anyNType
return if (this.isNullable())
type.makeNullable()
else
type
}
private fun transformGenericCall(call: IrCall, scopeOwnerSymbol: IrSymbol): IrExpression {
val function: IrSimpleFunction =
call.symbol.owner as? IrSimpleFunction ?: return call
if (!function.realOverrideTarget.returnType.isTypeParameter())
return call
val erasedReturnType: IrType =
function.realOverrideTarget.returnType.eraseUpperBoundType()
val callType = call.type
if (erasedReturnType != call.type) {
if (callType.isNothing()) return call
if (erasedReturnType.isSubtypeOf(callType, context.irBuiltIns)) return call
// Erase type parameter from call return type
val newCall = irCall(
call,
function.symbol,
newReturnType = erasedReturnType,
newSuperQualifierSymbol = call.superQualifierSymbol
)
context.createIrBuilder(scopeOwnerSymbol).apply {
if (call.type.isUnit()) {
return irComposite(call) {
+newCall
}
}
return irImplicitCast(newCall, call.type)
}
}
return call
}
}
@@ -0,0 +1,91 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
import org.jetbrains.kotlin.backend.common.lower.at
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irString
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.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrStringConcatenation
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.ir.types.isString
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
/**
* Replace string concatenation with a chain of String.plus calls.
* TODO: Reuse common StringConcatenationLowering which uses string builder
*/
class SimpleStringConcatenationLowering(val context: WasmBackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(StringConcatenationTransformer(this))
}
}
private class StringConcatenationTransformer(val lower: SimpleStringConcatenationLowering) : IrElementTransformerVoidWithContext() {
private val context = lower.context
private val irBuiltIns = context.irBuiltIns
private val stringPlus = irBuiltIns.stringClass.owner.declarations.filterIsInstance<IrSimpleFunction>().find {
it.name == Name.identifier("plus")
}!!
private val anyToString = irBuiltIns.anyClass.owner.declarations.filterIsInstance<IrSimpleFunction>().find {
it.name == Name.identifier("toString")
}!!
private val anyNToString = context.wasmSymbols.anyNtoString
override fun visitStringConcatenation(expression: IrStringConcatenation): IrExpression {
val transformed = super.visitStringConcatenation(expression) as IrStringConcatenation
val builder: DeclarationIrBuilder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol).at(expression)
return transformed.arguments.fold<IrExpression, IrExpression>(
builder.irString("")
) { acc, el ->
builder.irCall(stringPlus).apply {
dispatchReceiver = acc
putValueArgument(0, expressionToString(el, builder))
}
}
}
private fun expressionToString(expression: IrExpression, builder: DeclarationIrBuilder): IrExpression {
if (expression.type.isString()) return expression
builder.at(expression)
val klass = expression.type.getClass()
if (expression.type.isNullable()) {
return builder.irCall(anyNToString).apply {
putValueArgument(0, expression)
}
}
val toStringMethod: IrSimpleFunction = if (klass != null) {
klass.declarations.filterIsInstance<IrSimpleFunction>().find { it.isToStringInheritedFromAny() }!!
} else {
anyToString
}
return builder.irCall(toStringMethod).apply {
dispatchReceiver = expression
}
}
}
fun IrFunction.isToStringInheritedFromAny() =
name == Name.identifier("toString") &&
dispatchReceiverParameter != null &&
extensionReceiverParameter == null &&
valueParameters.isEmpty()
@@ -0,0 +1,68 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.lower
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.ir.isOverridable
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlock
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
import org.jetbrains.kotlin.ir.builders.createTmpVariable
import org.jetbrains.kotlin.ir.builders.irGet
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.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrGetValue
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
/**
* During Wasm code generation, dispatch receiver can be used multiple times.
* Move it to temporary variable if it is complex or can have side effects.
*/
class VirtualDispatchReceiverExtraction(val context: CommonBackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitFunction(declaration: IrFunction) {
lower(declaration)
super.visitFunction(declaration)
}
})
}
fun lower(irFunction: IrFunction) {
irFunction.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitCall(expression: IrCall): IrExpression {
expression.transformChildrenVoid(this)
val function = expression.symbol.owner.realOverrideTarget
val receiver = expression.dispatchReceiver
if (receiver == null || !function.isOverridable)
return expression
// TODO: Keep other simple receivers without side effects
// receiver.isPure(true) ?
if (receiver is IrGetValue)
return expression
return with(context.createIrBuilder(irFunction.symbol)) {
irBlock(expression) {
val tmp = createTmpVariable(receiver)
expression.dispatchReceiver = irGet(tmp)
+expression
}
}
}
})
}
}
@@ -0,0 +1,55 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.lower
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.wasm.ir2wasm.erasedUpperBound
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
import org.jetbrains.kotlin.ir.backend.js.lower.BridgesConstruction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.name.Name
class WasmBridgesConstruction(context: JsCommonBackendContext) : BridgesConstruction(context) {
override fun getFunctionSignature(function: IrSimpleFunction): WasmSignature =
function.wasmSignature(context.irBuiltIns)
// Dispatch receiver type must be casted when types are different.
override val shouldCastDispatchReceiver: Boolean = true
}
data class WasmSignature(
val name: Name,
val extensionReceiverType: IrType?,
val valueParametersType: List<IrType>,
val returnType: IrType
) {
override fun toString(): String {
val er = extensionReceiverType?.let { "(er: ${it.render()}) " } ?: ""
val parameters = valueParametersType.joinToString(", ") { it.render() }
return "[$er$name($parameters) -> ${returnType.render()}]"
}
}
fun IrSimpleFunction.wasmSignature(irBuiltIns: IrBuiltIns): WasmSignature =
WasmSignature(
name,
extensionReceiverParameter?.type?.eraseGenerics(irBuiltIns),
valueParameters.map { it.type.eraseGenerics(irBuiltIns) },
returnType.eraseGenerics(irBuiltIns)
)
private fun IrType.eraseGenerics(irBuiltIns: IrBuiltIns): IrType {
val defaultType = this.erasedUpperBound?.defaultType ?: irBuiltIns.anyType
if (!this.isNullable()) return defaultType
return defaultType.makeNullable()
}
@@ -0,0 +1,314 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
import org.jetbrains.kotlin.backend.common.ir.isSuspend
import org.jetbrains.kotlin.backend.common.ir.moveBodyTo
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.addConstructor
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.builders.declarations.buildClass
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
/**
* TODO: Temporary lowering stub. Needs to be redone.
* This is a copy of JVM lowering, but parts that don't compile are commented out.
* Turns out this works decently as a stub in most tests.
*/
val IrStatementOrigin?.isLambda: Boolean
get() = this == IrStatementOrigin.LAMBDA || this == IrStatementOrigin.ANONYMOUS_FUNCTION
// Originally copied from K/Native
internal class WasmCallableReferenceLowering(private val context: WasmBackendContext) : FileLoweringPass, IrElementTransformerVoidWithContext() {
// This pass ignores suspend function references and function references used in inline arguments to inline functions.
private val ignoredFunctionReferences = mutableSetOf<IrFunctionReference>()
private val IrFunctionReference.isIgnored: Boolean
get() = (!type.isFunctionOrKFunction() || ignoredFunctionReferences.contains(this)) && !isSuspendCallableReference()
// TODO: Currently, origin of callable references is null. Do we need to create one?
private fun IrFunctionReference.isSuspendCallableReference(): Boolean = isSuspend && origin == null
override fun lower(irFile: IrFile) {
// ignoredFunctionReferences.addAll(IrInlineReferenceLocator.scan(context, irFile))
irFile.transformChildrenVoid(this)
}
override fun visitBlock(expression: IrBlock): IrExpression {
if (!expression.origin.isLambda)
return super.visitBlock(expression)
val reference = expression.statements.last() as IrFunctionReference
if (reference.isIgnored)
return super.visitBlock(expression)
expression.statements.dropLast(1).forEach { it.transform(this, null) }
reference.transformChildrenVoid(this)
return FunctionReferenceBuilder(reference).build()
}
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
expression.transformChildrenVoid(this)
return if (expression.isIgnored) expression else FunctionReferenceBuilder(expression).build()
}
// Handle SAM conversions which wrap a function reference:
// class sam$n(private val receiver: R) : Interface { override fun method(...) = receiver.target(...) }
//
// This avoids materializing an invokable KFunction representing, thus producing one less class.
// This is actually very common, as `Interface { something }` is a local function + a SAM-conversion
// of a reference to it into an implementation.
override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression {
if (expression.operator == IrTypeOperator.SAM_CONVERSION) {
val invokable = expression.argument
val reference = if (invokable is IrFunctionReference) {
invokable
} else if (invokable is IrBlock && invokable.origin.isLambda && invokable.statements.last() is IrFunctionReference) {
invokable.statements.dropLast(1).forEach { it.transform(this, null) }
invokable.statements.last() as IrFunctionReference
} else {
return super.visitTypeOperator(expression)
}
reference.transformChildrenVoid()
return FunctionReferenceBuilder(reference, expression.typeOperand).build()
}
return super.visitTypeOperator(expression)
}
private inner class FunctionReferenceBuilder(val irFunctionReference: IrFunctionReference, val samSuperType: IrType? = null) {
private val isLambda = irFunctionReference.origin.isLambda
private val callee = irFunctionReference.symbol.owner
// Only function references can bind a receiver and even then we can only bind either an extension or a dispatch receiver.
// However, when we bind a value of an inline class type as a receiver, the receiver will turn into an argument of
// the function in question. Yet we still need to record it as the "receiver" in CallableReference in order for reflection
// to work correctly.
private val boundReceiver: Pair<IrValueParameter, IrExpression>? = irFunctionReference.getArgumentsWithIr().singleOrNull()
// The type of the reference is KFunction<in A1, ..., in An, out R>
private val parameterTypes = (irFunctionReference.type as IrSimpleType).arguments.map { (it as IrTypeProjection).type }
private val argumentTypes = parameterTypes.dropLast(1)
private val typeArgumentsMap = irFunctionReference.typeSubstitutionMap
private val functionSuperClass =
samSuperType?.classOrNull
?: if (irFunctionReference.isSuspend)
context.ir.symbols.suspendFunctionN(argumentTypes.size)
else
context.ir.symbols.functionN(argumentTypes.size)
private val superMethod =
functionSuperClass.functions.single { it.owner.modality == Modality.ABSTRACT }
// TODO(WASM)
// private val superType =
// samSuperType ?: (if (isLambda) context.ir.symbols.lambdaClass else context.ir.symbols.functionReference).defaultType
private val functionReferenceClass = context.irFactory.buildClass {
setSourceRange(irFunctionReference)
visibility = DescriptorVisibilities.LOCAL
// A callable reference results in a synthetic class, while a lambda is not synthetic.
// We don't produce GENERATED_SAM_IMPLEMENTATION, which is always synthetic.
// TODO(WASM)
// origin = if (isLambda) JvmLoweredDeclarationOrigin.LAMBDA_IMPL else JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL
name = SpecialNames.NO_NAME_PROVIDED
}.apply {
parent = currentDeclarationParent!!
// TODO(WASM)
// superTypes += superType
if (samSuperType == null)
superTypes += functionSuperClass.typeWith(parameterTypes)
// TODO(WASM)
// if (irFunctionReference.isSuspend) superTypes += context.ir.symbols.suspendFunctionInterface.defaultType
createImplicitParameterDeclarationWithWrappedDescriptor()
copyAttributes(irFunctionReference)
if (isLambda) {
this.metadata = irFunctionReference.symbol.owner.metadata
}
}
// WASM(TODO)
// private val receiverFieldFromSuper = context.ir.symbols.functionReferenceReceiverField.owner
//
// val fakeOverrideReceiverField = functionReferenceClass.addField {
// name = receiverFieldFromSuper.name
// origin = IrDeclarationOrigin.FAKE_OVERRIDE
// type = receiverFieldFromSuper.type
// isFinal = receiverFieldFromSuper.isFinal
// isStatic = receiverFieldFromSuper.isStatic
// visibility = receiverFieldFromSuper.visibility
// }
fun build(): IrExpression = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol).run {
irBlock {
val constructor = createConstructor()
createInvokeMethod(
if (samSuperType != null && boundReceiver != null) {
irTemporary(boundReceiver.second)
} else null
)
// WASM(TODO)
// if (!isLambda && samSuperType == null) {
// createGetSignatureMethod(this@run.irSymbols.functionReferenceGetSignature.owner)
// createGetNameMethod(this@run.irSymbols.functionReferenceGetName.owner)
// createGetOwnerMethod(this@run.irSymbols.functionReferenceGetOwner.owner)
// }
+functionReferenceClass
+irCall(constructor.symbol).apply {
if (valueArgumentsCount > 0) putValueArgument(0, boundReceiver!!.second)
}
}
}
private fun createConstructor(): IrConstructor =
functionReferenceClass.addConstructor {
// origin = JvmLoweredDeclarationOrigin.GENERATED_MEMBER_IN_CALLABLE_REFERENCE
returnType = functionReferenceClass.defaultType
isPrimary = true
}.apply {
// Add receiver parameter for bound function references
if (samSuperType == null) {
boundReceiver?.first?.let { param ->
valueParameters += param.copyTo(
irFunction = this,
index = 0,
type = param.type.substitute(typeArgumentsMap)
)
}
}
// Super constructor:
// - For SAM references, the super class is Any
// - For function references with bound receivers, accepts arity and receiver
// - For lambdas and function references without bound receivers, accepts arity
// WASM_TODO
// val constructor = if (samSuperType != null) {
// context.irBuiltIns.anyClass.owner.constructors.single()
// } else {
// superType.getClass()!!.constructors.single {
// it.valueParameters.size == if (boundReceiver != null) 2 else 1
// }
// }
val constructor = context.irBuiltIns.anyClass.owner.constructors.single()
body = context.createIrBuilder(symbol).irBlockBody(startOffset, endOffset) {
+irDelegatingConstructorCall(constructor).apply {
// WASM_TODO
// if (samSuperType == null) {
// putValueArgument(0, irInt(argumentTypes.size + if (irFunctionReference.isSuspend) 1 else 0))
// if (boundReceiver != null)
// putValueArgument(1, irGet(valueParameters.first()))
// }
}
+IrInstanceInitializerCallImpl(startOffset, endOffset, functionReferenceClass.symbol, context.irBuiltIns.unitType)
}
}
private fun createInvokeMethod(receiverVar: IrValueDeclaration?): IrSimpleFunction =
functionReferenceClass.addFunction {
setSourceRange(if (isLambda) callee else irFunctionReference)
name = superMethod.owner.name
returnType = callee.returnType
isSuspend = callee.isSuspend
}.apply {
overriddenSymbols += superMethod
dispatchReceiverParameter = parentAsClass.thisReceiver!!.copyTo(this)
if (isLambda) createLambdaInvokeMethod() else createFunctionReferenceInvokeMethod(receiverVar)
}
// Inline the body of an anonymous function into the generated lambda subclass.
private fun IrSimpleFunction.createLambdaInvokeMethod() {
annotations += callee.annotations
val valueParameterMap = callee.explicitParameters.withIndex().associate { (index, param) ->
param to param.copyTo(this, index = index)
}
valueParameters += valueParameterMap.values
body = callee.moveBodyTo(this, valueParameterMap)
}
private fun IrSimpleFunction.createFunctionReferenceInvokeMethod(receiver: IrValueDeclaration?) {
for ((index, argumentType) in argumentTypes.withIndex()) {
addValueParameter {
name = Name.identifier("p$index")
type = argumentType
}
}
body = context.createIrBuilder(symbol).run {
var unboundIndex = 0
irExprBody(irCall(callee).apply {
for ((typeParameter, typeArgument) in typeArgumentsMap) {
putTypeArgument(typeParameter.owner.index, typeArgument)
}
for (parameter in callee.explicitParameters) {
when {
boundReceiver?.first == parameter ->
// Bound receiver parameter. For function references, this is stored in a field of the superclass.
// For sam references, we just capture the value in a local variable and LocalDeclarationsLowering
// will put it into a field.
// if (samSuperType == null)
// irImplicitCast(
// irGetField(irGet(dispatchReceiverParameter!!), fakeOverrideReceiverField),
// boundReceiver.second.type
// )
// else
irGet(receiver ?: error("Binding receivers is not supported yet"))
// If a vararg parameter corresponds to exactly one KFunction argument, which is an array, that array
// is forwarded as is.
//
// fun f(x: (Int, Array<String>) -> String) = x(0, arrayOf("OK", "FAIL"))
// fun h(i: Int, vararg xs: String) = xs[i]
// f(::h)
//
parameter.isVararg && unboundIndex < argumentTypes.size && parameter.type == valueParameters[unboundIndex].type ->
irGet(valueParameters[unboundIndex++])
// In all other cases, excess arguments are packed into a new array.
//
// fun g(x: (Int, String, String) -> String) = x(0, "OK", "FAIL")
// f(::h) == g(::h)
//
parameter.isVararg && (unboundIndex < argumentTypes.size || !parameter.hasDefaultValue()) ->
TODO()
unboundIndex >= argumentTypes.size ->
// Default value argument (this pass doesn't handle suspend functions, otherwise
// it could also be the continuation argument)
null
else ->
irGet(valueParameters[unboundIndex++])
}?.let { putArgument(callee, parameter, it) }
}
})
}
}
}
}
@@ -0,0 +1,34 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.lower
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.lower.AbstractValueUsageLowering
import org.jetbrains.kotlin.ir.expressions.IrConstKind
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.isNothing
import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.ir.types.makeNotNull
/**
* Replace null constants of type Nothing? with null constants of a concrete class types.
*
* Wasm GC doesn't have a nullref type anymore.
*/
class WasmNullCoercingLowering(context: JsCommonBackendContext) : AbstractValueUsageLowering(context) {
override fun IrExpression.useExpressionAsType(actualType: IrType, expectedType: IrType): IrExpression =
if (actualType.makeNotNull().isNothing() && actualType.isNullable() && !expectedType.makeNotNull().isNothing())
JsIrBuilder.buildComposite(
type,
listOf(this, IrConstImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, expectedType, IrConstKind.Null, null))
)
else
this
}
@@ -0,0 +1,220 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.lower
import org.jetbrains.kotlin.backend.common.ir.SharedVariablesManager
import org.jetbrains.kotlin.backend.common.lower.InnerClassesSupport
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.builders.declarations.buildClass
import org.jetbrains.kotlin.ir.builders.declarations.buildValueParameter
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
import org.jetbrains.kotlin.ir.descriptors.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrClassSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrConstructorSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.name.Name
/**
* This is a copy of an old version of JS lowering, because JS did platform-specific optimization incompatible with Wasm.
* TODO: Revisit
*/
@OptIn(ObsoleteDescriptorBasedAPI::class)
class WasmSharedVariablesManager(val context: JsCommonBackendContext, val builtIns: IrBuiltIns, val implicitDeclarationsFile: IrPackageFragment) : SharedVariablesManager {
override fun declareSharedVariable(originalDeclaration: IrVariable): IrVariable {
val initializer = originalDeclaration.initializer ?: IrConstImpl.constNull(
originalDeclaration.startOffset,
originalDeclaration.endOffset,
builtIns.nothingNType
)
val constructorSymbol = closureBoxConstructorDeclaration.symbol
val irCall =
IrConstructorCallImpl.fromSymbolDescriptor(initializer.startOffset, initializer.endOffset, closureBoxType, constructorSymbol)
.apply {
putValueArgument(0, initializer)
}
val descriptor = WrappedVariableDescriptor()
return IrVariableImpl(
originalDeclaration.startOffset,
originalDeclaration.endOffset,
originalDeclaration.origin,
IrVariableSymbolImpl(descriptor),
originalDeclaration.name,
irCall.type,
false,
false,
false
).also {
descriptor.bind(it)
it.parent = originalDeclaration.parent
it.initializer = irCall
}
}
override fun defineSharedValue(originalDeclaration: IrVariable, sharedVariableDeclaration: IrVariable) = sharedVariableDeclaration
override fun getSharedValue(sharedVariableSymbol: IrVariableSymbol, originalGet: IrGetValue): IrExpression {
val getField = IrGetFieldImpl(
originalGet.startOffset, originalGet.endOffset,
closureBoxFieldDeclaration.symbol,
closureBoxFieldDeclaration.type,
IrGetValueImpl(
originalGet.startOffset,
originalGet.endOffset,
closureBoxType,
sharedVariableSymbol,
originalGet.origin
),
originalGet.origin
)
return IrTypeOperatorCallImpl(
originalGet.startOffset,
originalGet.endOffset,
originalGet.type,
IrTypeOperator.IMPLICIT_CAST,
originalGet.type,
getField
)
}
override fun setSharedValue(sharedVariableSymbol: IrVariableSymbol, originalSet: IrSetValue): IrExpression =
IrSetFieldImpl(
originalSet.startOffset,
originalSet.endOffset,
closureBoxFieldDeclaration.symbol,
IrGetValueImpl(
originalSet.startOffset,
originalSet.endOffset,
closureBoxType,
sharedVariableSymbol,
originalSet.origin
),
originalSet.value,
originalSet.type,
originalSet.origin
)
private val boxTypeName = "\$closureBox\$"
private val closureBoxClassDeclaration by lazy {
createClosureBoxClassDeclaration()
}
private val closureBoxConstructorDeclaration by lazy {
createClosureBoxConstructorDeclaration()
}
private val closureBoxFieldDeclaration by lazy {
closureBoxPropertyDeclaration
}
private val closureBoxPropertyDeclaration by lazy {
createClosureBoxPropertyDeclaration()
}
private lateinit var closureBoxType: IrType
private fun createClosureBoxClassDeclaration(): IrClass {
val declaration = context.irFactory.buildClass {
origin = JsLoweredDeclarationOrigin.JS_CLOSURE_BOX_CLASS_DECLARATION
name = Name.identifier(boxTypeName)
visibility = DescriptorVisibilities.PUBLIC
modality = Modality.FINAL
isCompanion = false
isInner = false
isData = false
isExternal = false
isInline = false
isExpect = false
isFun = false
}
declaration.parent = implicitDeclarationsFile
// TODO: substitute
closureBoxType = IrSimpleTypeImpl(declaration.symbol, false, emptyList(), emptyList())
declaration.thisReceiver = buildValueParameter(declaration) {
name = Name.identifier("_this_")
index = -1
type = closureBoxType
}
implicitDeclarationsFile.declarations += declaration
return declaration
}
private fun createClosureBoxPropertyDeclaration(): IrField {
val descriptor = WrappedFieldDescriptor()
val symbol = IrFieldSymbolImpl(descriptor)
val fieldName = Name.identifier("v")
return context.irFactory.createField(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
InnerClassesSupport.FIELD_FOR_OUTER_THIS,
symbol,
fieldName,
builtIns.anyNType,
DescriptorVisibilities.PUBLIC,
isFinal = false,
isExternal = false,
isStatic = false,
).also {
descriptor.bind(it)
it.parent = closureBoxClassDeclaration
closureBoxClassDeclaration.declarations += it
}
}
private fun createClosureBoxConstructorDeclaration(): IrConstructor {
val descriptor = WrappedClassConstructorDescriptor()
val symbol = IrConstructorSymbolImpl(descriptor)
val declaration = context.irFactory.createConstructor(
UNDEFINED_OFFSET, UNDEFINED_OFFSET, JsLoweredDeclarationOrigin.JS_CLOSURE_BOX_CLASS_DECLARATION, symbol,
Name.special("<init>"), DescriptorVisibilities.PUBLIC, closureBoxClassDeclaration.defaultType,
isInline = false, isExternal = false, isPrimary = true, isExpect = false
)
descriptor.bind(declaration)
declaration.parent = closureBoxClassDeclaration
val parameterDeclaration = createClosureBoxConstructorParameterDeclaration(declaration)
declaration.valueParameters += parameterDeclaration
val receiver = JsIrBuilder.buildGetValue(closureBoxClassDeclaration.thisReceiver!!.symbol)
val value = JsIrBuilder.buildGetValue(parameterDeclaration.symbol)
val setField = JsIrBuilder.buildSetField(closureBoxFieldDeclaration.symbol, receiver, value, builtIns.unitType)
declaration.body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET, listOf(setField))
closureBoxClassDeclaration.declarations += declaration
return declaration
}
private fun createClosureBoxConstructorParameterDeclaration(irConstructor: IrConstructor): IrValueParameter {
return JsIrBuilder.buildValueParameter(irConstructor,"p", 0, closureBoxPropertyDeclaration.type)
}
}
@@ -0,0 +1,37 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrThrow
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
/**
* Replace throw expressions with a runtime function call.
* TODO: Remove when full-blown exception handling is implemented
*/
internal class WasmThrowDebugLowering(
private val context: WasmBackendContext
) : FileLoweringPass, IrElementTransformerVoidWithContext() {
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(this)
}
override fun visitThrow(expression: IrThrow): IrExpression {
expression.transformChildrenVoid(this)
val builder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol)
return builder.irCall(context.wasmSymbols.wasmThrow).apply {
this.putValueArgument(0, expression.value)
}
}
}
@@ -0,0 +1,287 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
import org.jetbrains.kotlin.backend.common.lower.at
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irNot
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.backend.wasm.ir2wasm.erasedUpperBound
import org.jetbrains.kotlin.ir.backend.js.utils.isPure
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
class WasmTypeOperatorLowering(val context: WasmBackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(WasmBaseTypeOperatorTransformer(context))
}
}
class WasmBaseTypeOperatorTransformer(val context: WasmBackendContext) : IrElementTransformerVoidWithContext() {
private val symbols = context.wasmSymbols
private val builtIns = context.irBuiltIns
private lateinit var builder: DeclarationIrBuilder
override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression {
super.visitTypeOperator(expression)
builder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol).at(expression)
return when (expression.operator) {
IrTypeOperator.IMPLICIT_CAST -> lowerImplicitCast(expression)
IrTypeOperator.IMPLICIT_DYNAMIC_CAST -> error("Dynamic casts are not supported in Wasm backend")
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> expression.argument
IrTypeOperator.IMPLICIT_INTEGER_COERCION -> lowerIntegerCoercion(expression)
IrTypeOperator.IMPLICIT_NOTNULL -> lowerImplicitCast(expression)
IrTypeOperator.INSTANCEOF -> lowerInstanceOf(expression, inverted = false)
IrTypeOperator.NOT_INSTANCEOF -> lowerInstanceOf(expression, inverted = true)
IrTypeOperator.CAST -> lowerCast(expression, isSafe = false)
IrTypeOperator.SAFE_CAST -> lowerCast(expression, isSafe = true)
IrTypeOperator.SAM_CONVERSION -> TODO("SAM conversion: ${expression.render()}")
IrTypeOperator.REINTERPRET_CAST -> expression
}
}
private fun lowerInstanceOf(
expression: IrTypeOperatorCall,
inverted: Boolean
): IrExpression {
return builder.irComposite(resultType = builtIns.booleanType) {
val argument = cacheValue(expression.argument)
val check = generateTypeCheck(argument, expression.typeOperand)
if (inverted) {
+builder.irNot(check)
} else {
+check
}
}
}
private fun IrBlockBuilder.cacheValue(value: IrExpression): () -> IrExpressionWithCopy {
if (value.isPure(true) && value is IrExpressionWithCopy) {
return { value.deepCopyWithSymbols() }
}
val tmpVal = createTmpVariable(value)
return { builder.irGet(tmpVal) }
}
private fun IrType.isInlined(): Boolean =
context.inlineClassesUtils.isTypeInlined(this)
private val IrType.erasedType: IrType
get() = this.erasedUpperBound?.defaultType ?: builtIns.anyType
private fun generateTypeCheck(
valueProvider: () -> IrExpressionWithCopy,
toType: IrType
): IrExpression {
val toNotNullable = toType.makeNotNull()
val valueInstance: IrExpressionWithCopy = valueProvider()
val fromType = (valueInstance as IrExpression).type
// Inlined values have no type information on runtime.
// But since they are final we can compute type checks on compile time.
if (fromType.isInlined()) {
val result = fromType.erasedType.isSubtypeOf(toType.erasedType, builtIns)
return builder.irBoolean(result)
}
val instanceCheck = generateTypeCheckNonNull(valueInstance, toNotNullable)
val isFromNullable = valueInstance.type.isNullable()
val isToNullable = toType.isNullable()
return when {
!isFromNullable -> instanceCheck
else ->
builder.irIfThenElse(
type = builtIns.booleanType,
condition = builder.irEqualsNull(valueProvider() as IrExpression),
thenPart = builder.irBoolean(isToNullable),
elsePart = instanceCheck
)
}
}
private fun lowerIntegerCoercion(expression: IrTypeOperatorCall): IrExpression =
when (expression.typeOperand) {
builtIns.byteType,
builtIns.shortType ->
expression.argument
builtIns.longType ->
builder.irCall(symbols.intToLong).apply {
putValueArgument(0, expression.argument)
}
else -> error("Unreachable execution (coercion to non-Integer type")
}
private fun generateTypeCheckNonNull(argument: IrExpressionWithCopy, toType: IrType): IrExpression {
assert(!toType.isMarkedNullable())
return when {
toType.isNothing() -> builder.irComposite(resultType = builtIns.booleanType) {
+(argument as IrExpression)
+builder.irFalse()
}
toType.isTypeParameter() -> generateTypeCheckWithTypeParameter(argument, toType)
toType.isInterface() -> generateIsInterface(argument as IrExpression, toType)
else -> generateIsSubClass(argument as IrExpression, toType)
}
}
private fun narrowType(fromType: IrType, toType: IrType, value: IrExpression): IrExpression {
if (fromType == toType) return value
if (toType == builtIns.nothingNType) {
return builder.irComposite(resultType = builtIns.nothingNType) {
+value
+builder.irNull()
}
}
// Handled by autoboxing transformer
if (toType.isInlined() && !fromType.isInlined()) {
return builder.irCall(
symbols.unboxIntrinsic,
toType,
typeArguments = listOf(fromType, toType)
).also {
it.putValueArgument(0, value)
}
}
if (!toType.isInlined() && fromType.isInlined()) {
return builder.irCall(
symbols.boxIntrinsic,
toType,
typeArguments = listOf(fromType, toType)
).also {
it.putValueArgument(0, value)
}
}
if (fromType.erasedType.isSubtypeOf(toType.erasedType, context.irBuiltIns)) {
return value
}
if (toType.isNothing()) {
return value
}
// Ref casts traps on null (https://github.com/WebAssembly/gc/issues/152)
// Handling null manually
if (toType.isNullable() && fromType.isNullable()) {
return builder.irComposite {
val value = cacheValue(value)
+builder.irIfNull(
type = toType,
subject = value() as IrExpression,
thenPart = builder.irNull(toType),
elsePart = builder.irCall(symbols.wasmRefCast, type = toType).apply {
putTypeArgument(0, fromType)
putTypeArgument(1, toType)
putValueArgument(0, value() as IrExpression)
}
)
}
}
return builder.irCall(symbols.wasmRefCast, type = toType).apply {
putTypeArgument(0, fromType)
putTypeArgument(1, toType)
putValueArgument(0, value)
}
}
private fun lowerCast(
expression: IrTypeOperatorCall,
isSafe: Boolean
): IrExpression {
val toType = expression.typeOperand
val fromType = expression.argument.type
if (fromType.erasedType.isSubtypeOf(expression.type.erasedType, context.irBuiltIns)) {
return narrowType(fromType, expression.type, expression.argument)
}
val failResult = if (isSafe) {
builder.irNull()
} else {
builder.irCall(context.ir.symbols.throwTypeCastException)
}
return builder.irComposite(resultType = expression.type) {
val argument = cacheValue(expression.argument)
val narrowArg = narrowType(fromType, expression.type, argument() as IrExpression)
val check = generateTypeCheck(argument, toType)
if (check is IrConst<*>) {
val value = check.value as Boolean
if (value) {
+narrowArg
} else {
+failResult
}
} else {
+builder.irIfThenElse(
type = expression.type,
condition = check,
thenPart = narrowArg,
elsePart = failResult
)
}
}
}
private fun lowerImplicitCast(expression: IrTypeOperatorCall): IrExpression =
narrowType(
fromType = expression.argument.type,
toType = expression.typeOperand,
value = expression.argument
)
private fun generateTypeCheckWithTypeParameter(argument: IrExpressionWithCopy, toType: IrType): IrExpression {
val typeParameter = toType.classifierOrNull?.owner as? IrTypeParameter
?: error("expected type parameter, but got $toType")
return typeParameter.superTypes.fold(builder.irTrue() as IrExpression) { r, t ->
val check = generateTypeCheckNonNull(argument.copy() as IrExpressionWithCopy, t.makeNotNull())
builder.irCall(symbols.booleanAnd).apply {
putValueArgument(0, r)
putValueArgument(1, check)
}
}
}
private fun generateIsInterface(argument: IrExpression, toType: IrType): IrExpression {
val interfaceId = builder.irCall(symbols.wasmInterfaceId).apply {
putTypeArgument(0, toType)
}
return builder.irCall(symbols.isInterface).apply {
putValueArgument(0, argument)
putValueArgument(1, interfaceId)
}
}
private fun generateIsSubClass(argument: IrExpression, toType: IrType): IrExpression {
val classId = builder.irCall(symbols.wasmClassId).apply {
putTypeArgument(0, toType)
}
return builder.irCall(symbols.isSubClass).apply {
putValueArgument(0, argument)
putValueArgument(1, classId)
}
}
}
@@ -0,0 +1,100 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irComposite
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irInt
import org.jetbrains.kotlin.ir.builders.irTemporary
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrVararg
import org.jetbrains.kotlin.ir.expressions.IrVarargElement
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.util.primaryConstructor
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
internal class WasmVarargExpressionLowering(
private val context: WasmBackendContext
) : FileLoweringPass, IrElementTransformerVoidWithContext() {
val symbols = context.wasmSymbols
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(this)
}
override fun visitVararg(expression: IrVararg): IrExpression {
val irVararg = super.visitVararg(expression) as IrVararg
val builder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol)
val arrayClass = irVararg.type.classOrNull!!.owner
val primaryConstructor = arrayClass.primaryConstructor!!
val setMethod = arrayClass.declarations.filterIsInstance<IrSimpleFunction>().find {
it.name == Name.identifier("set")
}!!
return builder.irComposite(irVararg) {
val arrayTempVariable = irTemporary(
value = irCall(primaryConstructor).apply {
putValueArgument(0, irInt(irVararg.elements.size))
if (primaryConstructor.typeParameters.isNotEmpty()) {
check(primaryConstructor.typeParameters.size == 1)
putTypeArgument(0, irVararg.varargElementType)
}
},
nameHint = "array_tmp"
)
for ((index: Int, element: IrVarargElement) in irVararg.elements.withIndex()) {
check(element is IrExpression) {
"TODO: Support $element as vararg elements"
}
+irCall(setMethod).apply {
dispatchReceiver = irGet(arrayTempVariable)
putValueArgument(0, irInt(index))
putValueArgument(1, element)
}
}
+irGet(arrayTempVariable)
}
}
override fun visitFunctionAccess(expression: IrFunctionAccessExpression) =
transformFunctionAccessExpression(expression)
private fun transformFunctionAccessExpression(expression: IrFunctionAccessExpression): IrExpression {
expression.transformChildrenVoid()
val builder by lazy { context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol) }
// Replace empty vararg arguments with empty array construction
for (argumentIdx in 0 until expression.valueArgumentsCount) {
val argument = expression.getValueArgument(argumentIdx)
val parameter = expression.symbol.owner.valueParameters[argumentIdx]
val varargElementType = parameter.varargElementType
if (argument == null && varargElementType != null) {
val arrayClass = parameter.type.classOrNull!!.owner
val primaryConstructor = arrayClass.primaryConstructor!!
val emptyArrayCall = with(builder) {
irCall(primaryConstructor).apply {
putValueArgument(0, irInt(0))
if (primaryConstructor.typeParameters.isNotEmpty()) {
check(primaryConstructor.typeParameters.size == 1)
putTypeArgument(0, parameter.varargElementType)
}
}
}
expression.putValueArgument(argumentIdx, emptyArrayCall)
}
}
return expression
}
}
@@ -11,19 +11,27 @@ import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.util.getAnnotation import org.jetbrains.kotlin.ir.util.getAnnotation
import org.jetbrains.kotlin.ir.util.hasAnnotation import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.wasm.ir.WasmImportPair
fun IrAnnotationContainer.hasExcludedFromCodegenAnnotation(): Boolean = fun IrAnnotationContainer.hasExcludedFromCodegenAnnotation(): Boolean =
hasAnnotation(FqName("kotlin.wasm.internal.ExcludedFromCodegen")) hasAnnotation(FqName("kotlin.wasm.internal.ExcludedFromCodegen"))
fun IrAnnotationContainer.getWasmInstructionAnnotation(): String? = fun IrAnnotationContainer.getWasmOpAnnotation(): String? =
getAnnotation(FqName("kotlin.wasm.internal.WasmInstruction"))?.getSingleConstStringArgument() getAnnotation(FqName("kotlin.wasm.internal.WasmOp"))?.getSingleConstStringArgument()
fun IrAnnotationContainer.hasWasmReinterpretAnnotation(): Boolean =
hasAnnotation(FqName("kotlin.wasm.internal.WasmReinterpret"))
fun IrAnnotationContainer.hasWasmForeignAnnotation(): Boolean =
hasAnnotation(FqName("kotlin.wasm.internal.WasmForeign"))
fun IrAnnotationContainer.hasWasmPrimitiveAnnotation(): Boolean =
hasAnnotation(FqName("kotlin.wasm.internal.WasmPrimitive"))
class WasmImportPair(val module: String, val name: String)
@Suppress("UNCHECKED_CAST")
fun IrAnnotationContainer.getWasmImportAnnotation(): WasmImportPair? = fun IrAnnotationContainer.getWasmImportAnnotation(): WasmImportPair? =
getAnnotation(FqName("kotlin.wasm.internal.WasmImport"))?.let { getAnnotation(FqName("kotlin.wasm.internal.WasmImport"))?.let {
WasmImportPair( WasmImportPair(
(it.getValueArgument(0) as IrConst<String>).value, (it.getValueArgument(0) as IrConst<*>).value as String,
(it.getValueArgument(1) as IrConst<String>).value (it.getValueArgument(1) as IrConst<*>).value as String
) )
} }
@@ -0,0 +1,43 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.utils
import org.jetbrains.kotlin.backend.wasm.WasmSymbols
import org.jetbrains.kotlin.ir.backend.js.InlineClassesUtils
import org.jetbrains.kotlin.ir.backend.js.utils.erase
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.isNullable
class WasmInlineClassesUtils(private val wasmSymbols: WasmSymbols) : InlineClassesUtils {
override fun isTypeInlined(type: IrType): Boolean {
return getInlinedClass(type) != null
}
override fun getInlinedClass(type: IrType): IrClass? {
if (type is IrSimpleType) {
// TODO: Make inlining less strict
if (type.isNullable()) return null
val erased = erase(type) ?: return null
if (isClassInlineLike(erased)) {
return erased
}
}
return null
}
override fun isClassInlineLike(klass: IrClass): Boolean {
return klass.isInline || klass.hasWasmPrimitiveAnnotation()
}
override val boxIntrinsic: IrSimpleFunctionSymbol
get() = wasmSymbols.boxIntrinsic
override val unboxIntrinsic: IrSimpleFunctionSymbol
get() = wasmSymbols.unboxIntrinsic
}
@@ -404,14 +404,16 @@ fun irCall(
newFunction: IrSimpleFunction, newFunction: IrSimpleFunction,
receiversAsArguments: Boolean = false, receiversAsArguments: Boolean = false,
argumentsAsReceivers: Boolean = false, argumentsAsReceivers: Boolean = false,
newSuperQualifierSymbol: IrClassSymbol? = null newSuperQualifierSymbol: IrClassSymbol? = null,
newReturnType: IrType? = null
): IrCall = ): IrCall =
irCall( irCall(
call, call,
newFunction.symbol, newFunction.symbol,
receiversAsArguments, receiversAsArguments,
argumentsAsReceivers, argumentsAsReceivers,
newSuperQualifierSymbol newSuperQualifierSymbol,
newReturnType
) )
fun irCall( fun irCall(
@@ -419,13 +421,14 @@ fun irCall(
newSymbol: IrSimpleFunctionSymbol, newSymbol: IrSimpleFunctionSymbol,
receiversAsArguments: Boolean = false, receiversAsArguments: Boolean = false,
argumentsAsReceivers: Boolean = false, argumentsAsReceivers: Boolean = false,
newSuperQualifierSymbol: IrClassSymbol? = null newSuperQualifierSymbol: IrClassSymbol? = null,
newReturnType: IrType? = null
): IrCall = ): IrCall =
call.run { call.run {
IrCallImpl( IrCallImpl(
startOffset, startOffset,
endOffset, endOffset,
type, newReturnType ?: type,
newSymbol, newSymbol,
typeArgumentsCount, typeArgumentsCount,
valueArgumentsCount = newSymbol.owner.valueParameters.size, valueArgumentsCount = newSymbol.owner.valueParameters.size,
@@ -0,0 +1,9 @@
class C(val x: String)
fun <T1 : C, T2 : T1> foo(x: T2): String =
x.x
fun box(): String {
return foo(C("OK"))
}
@@ -13815,6 +13815,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/functions/recursiveIncrementCall.kt"); runTest("compiler/testData/codegen/box/functions/recursiveIncrementCall.kt");
} }
@TestMetadata("typeParameterAsUpperBound.kt")
public void testTypeParameterAsUpperBound() throws Exception {
runTest("compiler/testData/codegen/box/functions/typeParameterAsUpperBound.kt");
}
@TestMetadata("typeParametersInLocalFunction.kt") @TestMetadata("typeParametersInLocalFunction.kt")
public void testTypeParametersInLocalFunction() throws Exception { public void testTypeParametersInLocalFunction() throws Exception {
runTest("compiler/testData/codegen/box/functions/typeParametersInLocalFunction.kt"); runTest("compiler/testData/codegen/box/functions/typeParametersInLocalFunction.kt");
@@ -13815,6 +13815,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/functions/recursiveIncrementCall.kt"); runTest("compiler/testData/codegen/box/functions/recursiveIncrementCall.kt");
} }
@TestMetadata("typeParameterAsUpperBound.kt")
public void testTypeParameterAsUpperBound() throws Exception {
runTest("compiler/testData/codegen/box/functions/typeParameterAsUpperBound.kt");
}
@TestMetadata("typeParametersInLocalFunction.kt") @TestMetadata("typeParametersInLocalFunction.kt")
public void testTypeParametersInLocalFunction() throws Exception { public void testTypeParametersInLocalFunction() throws Exception {
runTest("compiler/testData/codegen/box/functions/typeParametersInLocalFunction.kt"); runTest("compiler/testData/codegen/box/functions/typeParametersInLocalFunction.kt");
@@ -12415,6 +12415,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/functions/recursiveIncrementCall.kt"); runTest("compiler/testData/codegen/box/functions/recursiveIncrementCall.kt");
} }
@TestMetadata("typeParameterAsUpperBound.kt")
public void testTypeParameterAsUpperBound() throws Exception {
runTest("compiler/testData/codegen/box/functions/typeParameterAsUpperBound.kt");
}
@TestMetadata("typeParametersInLocalFunction.kt") @TestMetadata("typeParametersInLocalFunction.kt")
public void testTypeParametersInLocalFunction() throws Exception { public void testTypeParametersInLocalFunction() throws Exception {
runTest("compiler/testData/codegen/box/functions/typeParametersInLocalFunction.kt"); runTest("compiler/testData/codegen/box/functions/typeParametersInLocalFunction.kt");
+36 -3
View File
@@ -232,7 +232,7 @@ val currentOsType = run {
OsType(osName, osArch) OsType(osName, osArch)
} }
val jsShellDirectory = "https://archive.mozilla.org/pub/firefox/nightly/2019/08/2019-08-11-09-56-40-mozilla-central" val jsShellDirectory = "https://archive.mozilla.org/pub/firefox/nightly/2020/06/2020-06-29-15-46-04-mozilla-central"
val jsShellSuffix = when (currentOsType) { val jsShellSuffix = when (currentOsType) {
OsType(OsName.LINUX, OsArch.X86_32) -> "linux-i686" OsType(OsName.LINUX, OsArch.X86_32) -> "linux-i686"
OsType(OsName.LINUX, OsArch.X86_64) -> "linux-x86_64" OsType(OsName.LINUX, OsArch.X86_64) -> "linux-x86_64"
@@ -248,6 +248,7 @@ val downloadedTools = File(buildDir, "tools")
val downloadJsShell by task<Download> { val downloadJsShell by task<Download> {
src(jsShellLocation) src(jsShellLocation)
dest(File(downloadedTools, "jsshell-$jsShellSuffix.zip")) dest(File(downloadedTools, "jsshell-$jsShellSuffix.zip"))
overwrite(false)
} }
val unzipJsShell by task<Copy> { val unzipJsShell by task<Copy> {
@@ -257,13 +258,45 @@ val unzipJsShell by task<Copy> {
into(unpackedDir) into(unpackedDir)
} }
val v8osString = when (currentOsType) {
OsType(OsName.LINUX, OsArch.X86_32) -> "linux32"
OsType(OsName.LINUX, OsArch.X86_64) -> "linux64"
OsType(OsName.MAC, OsArch.X86_64) -> "mac64"
OsType(OsName.WINDOWS, OsArch.X86_32) -> "win32"
OsType(OsName.WINDOWS, OsArch.X86_64) -> "win64"
else -> error("unsupported os type $currentOsType")
}
val v8edition = "rel" // rel or dbg
val v8version = "8.8.104"
val v8fileName = "v8-${v8osString}-${v8edition}-${v8version}"
val v8url = "https://storage.googleapis.com/chromium-v8/official/canary/$v8fileName.zip"
val downloadV8 by task<Download> {
src(v8url)
dest(File(downloadedTools, "$v8fileName.zip"))
overwrite(false)
}
val unzipV8 by task<Copy> {
dependsOn(downloadV8)
from(zipTree(downloadV8.get().dest))
val unpackedDir = File(downloadedTools, v8fileName)
into(unpackedDir)
}
projectTest("wasmTest", true) { projectTest("wasmTest", true) {
dependsOn(unzipJsShell) dependsOn(unzipJsShell)
dependsOn(unzipV8)
include("org/jetbrains/kotlin/js/test/wasm/semantics/*") include("org/jetbrains/kotlin/js/test/wasm/semantics/*")
val jsShellExecutablePath = File(unzipJsShell.get().destinationDir, "js").absolutePath val jsShellExecutablePath = File(unzipJsShell.get().destinationDir, "js").absolutePath
systemProperty("javascript.engine.path.SpiderMonkey", jsShellExecutablePath) val v8ExecutablePath = File(unzipV8.get().destinationDir, "d8").absolutePath
println(v8ExecutablePath)
dependsOn(":kotlin-stdlib-js-ir:compileKotlinJs") systemProperty("javascript.engine.path.SpiderMonkey", jsShellExecutablePath)
systemProperty("javascript.engine.path.V8", v8ExecutablePath)
dependsOn(":kotlin-stdlib-wasm:compileKotlinJs")
systemProperty("kotlin.wasm.stdlib.path", "libraries/stdlib/wasm/build/classes/kotlin/js/main") systemProperty("kotlin.wasm.stdlib.path", "libraries/stdlib/wasm/build/classes/kotlin/js/main")
setUpBoxTests() setUpBoxTests()
@@ -956,3 +956,14 @@ abstract class BasicBoxTest(
else ScriptEngineV8Lazy(KotlinTestUtils.tmpDirForReusableFolder("j2v8_library_path").path) else ScriptEngineV8Lazy(KotlinTestUtils.tmpDirForReusableFolder("j2v8_library_path").path)
} }
} }
fun KotlinTestWithEnvironment.createPsiFile(fileName: String): KtFile {
val psiManager = PsiManager.getInstance(project)
val fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL)
val file = fileSystem.findFileByPath(fileName) ?: error("File not found: $fileName")
return psiManager.findFile(file) as KtFile
}
fun KotlinTestWithEnvironment.createPsiFiles(fileNames: List<String>): List<KtFile> = fileNames.map(this::createPsiFile)
@@ -9,18 +9,20 @@ import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.StandardFileSystems import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiManager import com.intellij.psi.PsiManager
import junit.framework.TestCase
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.common.phaser.toPhaseMap import org.jetbrains.kotlin.backend.common.phaser.toPhaseMap
import org.jetbrains.kotlin.backend.wasm.compileWasm import org.jetbrains.kotlin.backend.wasm.compileWasm
import org.jetbrains.kotlin.backend.wasm.wasmPhases import org.jetbrains.kotlin.backend.wasm.wasmPhases
import org.jetbrains.kotlin.checkers.parseLanguageVersionSettings
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.ir.backend.js.loadKlib import org.jetbrains.kotlin.ir.backend.js.loadKlib
import org.jetbrains.kotlin.js.config.JsConfig import org.jetbrains.kotlin.js.config.JsConfig
import org.jetbrains.kotlin.js.facade.TranslationUnit import org.jetbrains.kotlin.js.facade.TranslationUnit
import org.jetbrains.kotlin.js.test.engines.ExternalTool
import org.jetbrains.kotlin.js.test.engines.SpiderMonkeyEngine import org.jetbrains.kotlin.js.test.engines.SpiderMonkeyEngine
import org.jetbrains.kotlin.library.resolver.impl.KotlinLibraryResolverResultImpl import org.jetbrains.kotlin.library.resolver.impl.KotlinLibraryResolverResultImpl
import org.jetbrains.kotlin.library.resolver.impl.KotlinResolvedLibraryImpl import org.jetbrains.kotlin.library.resolver.impl.KotlinResolvedLibraryImpl
@@ -28,10 +30,11 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.Directives import org.jetbrains.kotlin.test.Directives
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.KotlinTestWithEnvironment import org.jetbrains.kotlin.test.KotlinTestWithEnvironment
import org.jetbrains.kotlin.test.TestFiles import org.jetbrains.kotlin.test.*
import java.io.Closeable import java.io.Closeable
import java.io.File import java.io.File
import java.lang.Boolean.getBoolean import java.lang.Boolean.getBoolean
@@ -48,8 +51,13 @@ abstract class BasicWasmBoxTest(
private val spiderMonkey by lazy { SpiderMonkeyEngine() } private val spiderMonkey by lazy { SpiderMonkeyEngine() }
fun doTestWithCoroutinesPackageReplacement(filePath: String, coroutinesPackage: String) {
TODO("TestWithCoroutinesPackageReplacement are not supported")
}
fun doTest(filePath: String) { fun doTest(filePath: String) {
val file = File(filePath) val file = File(filePath)
val outputDir = getOutputDir(file) val outputDir = getOutputDir(file)
val fileContent = KotlinTestUtils.doLoadFile(file) val fileContent = KotlinTestUtils.doLoadFile(file)
@@ -58,21 +66,31 @@ abstract class BasicWasmBoxTest(
val testPackage = testFactory.testPackage val testPackage = testFactory.testPackage
val outputFileBase = outputDir.absolutePath + "/" + getTestName(true) val outputFileBase = outputDir.absolutePath + "/" + getTestName(true)
val outputWatFile = outputFileBase + ".wat" val outputWatFile = outputFileBase + ".wat"
val outputWasmFile = outputFileBase + ".wasm"
val outputJsFile = outputFileBase + ".js" val outputJsFile = outputFileBase + ".js"
val languageVersionSettings = inputFiles.mapNotNull { it.languageVersionSettings }.firstOrNull()
val kotlinFiles = inputFiles.filter { it.fileName.endsWith(".kt") } val kotlinFiles = inputFiles.filter { it.fileName.endsWith(".kt") }
val psiFiles = createPsiFiles(kotlinFiles.map { File(it.fileName).canonicalPath }.sorted()) val psiFiles = createPsiFiles(kotlinFiles.map { File(it.fileName).canonicalPath }.sorted())
val config = createConfig() val config = createConfig(languageVersionSettings)
translateFiles( translateFiles(
file,
psiFiles.map(TranslationUnit::SourceFile), psiFiles.map(TranslationUnit::SourceFile),
File(outputWatFile), File(outputWatFile),
File(outputWasmFile),
File(outputJsFile), File(outputJsFile),
config, config,
testPackage, testPackage,
TEST_FUNCTION TEST_FUNCTION
) )
spiderMonkey.runFile(outputJsFile) ExternalTool(System.getProperty("javascript.engine.path.V8"))
.run(
"--experimental-wasm-typed-funcref",
"--experimental-wasm-gc",
outputJsFile
)
} }
} }
@@ -86,26 +104,32 @@ abstract class BasicWasmBoxTest(
} }
private fun translateFiles( private fun translateFiles(
testFile: File,
units: List<TranslationUnit>, units: List<TranslationUnit>,
outputWatFile: File, outputWatFile: File,
outputWasmFile: File,
outputJsFile: File, outputJsFile: File,
config: JsConfig, config: JsConfig,
testPackage: String?, testPackage: String?,
testFunction: String testFunction: String
) { ) {
val filesToCompile = units.map { (it as TranslationUnit.SourceFile).file } val filesToCompile = units.map { (it as TranslationUnit.SourceFile).file }
val debugMode = getBoolean("kotlin.js.debugMode") val debugMode =getBoolean("kotlin.js.debugMode")
val phaseConfig = if (debugMode) { val phaseConfig = if (debugMode) {
val allPhasesSet = wasmPhases.toPhaseMap().values.toSet() val allPhasesSet = wasmPhases.toPhaseMap().values.toSet()
val dumpOutputDir = File(outputWatFile.parent, outputWatFile.nameWithoutExtension + "-irdump") val dumpOutputDir = File(outputWatFile.parent, outputWatFile.nameWithoutExtension + "-irdump")
println("\n ------ Dumping phases to file://$dumpOutputDir") println("\n ------ Dumping phases to file://$dumpOutputDir")
println("\n ------ KT file://${testFile.absolutePath}")
println("\n ------ WAT file://$outputWatFile")
println("\n ------ WASM file://$outputWasmFile")
println(" ------ JS file://$outputJsFile")
PhaseConfig( PhaseConfig(
wasmPhases, wasmPhases,
dumpToDirectory = dumpOutputDir.path, dumpToDirectory = dumpOutputDir.path,
toDumpStateAfter = allPhasesSet, // toDumpStateAfter = allPhasesSet,
toValidateStateAfter = allPhasesSet, // toValidateStateAfter = allPhasesSet,
dumpOnlyFqName = null // dumpOnlyFqName = null
) )
} else { } else {
PhaseConfig(wasmPhases) PhaseConfig(wasmPhases)
@@ -124,12 +148,12 @@ abstract class BasicWasmBoxTest(
) )
outputWatFile.write(compilerResult.wat) outputWatFile.write(compilerResult.wat)
outputWasmFile.writeBytes(compilerResult.wasm)
val runtime = File("libraries/stdlib/wasm/runtime/runtime.js").readText() val runtime = File("libraries/stdlib/wasm/runtime/runtime.js").readText()
val testRunner = """ val testRunner = """
const wat = read(String.raw`${outputWatFile.absoluteFile}`); const wasmBinary = read(String.raw`${outputWasmFile.absoluteFile}`, 'binary');
const wasmBinary = wasmTextToBinary(wat);
const wasmModule = new WebAssembly.Module(wasmBinary); const wasmModule = new WebAssembly.Module(wasmBinary);
const wasmInstance = new WebAssembly.Instance(wasmModule, { runtime }); const wasmInstance = new WebAssembly.Instance(wasmModule, { runtime });
@@ -141,20 +165,11 @@ abstract class BasicWasmBoxTest(
outputJsFile.write(runtime + "\n" + compilerResult.js + "\n" + testRunner) outputJsFile.write(runtime + "\n" + compilerResult.js + "\n" + testRunner)
} }
private fun createPsiFile(fileName: String): KtFile {
val psiManager = PsiManager.getInstance(project)
val fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL)
val file = fileSystem.findFileByPath(fileName) ?: error("File not found: $fileName") private fun createConfig(languageVersionSettings: LanguageVersionSettings?): JsConfig {
return psiManager.findFile(file) as KtFile
}
private fun createPsiFiles(fileNames: List<String>): List<KtFile> = fileNames.map(this::createPsiFile)
private fun createConfig(): JsConfig {
val configuration = environment.configuration.copy() val configuration = environment.configuration.copy()
configuration.put(CommonConfigurationKeys.MODULE_NAME, TEST_MODULE) configuration.put(CommonConfigurationKeys.MODULE_NAME, TEST_MODULE)
configuration.languageVersionSettings = languageVersionSettings ?: LanguageVersionSettingsImpl(LanguageVersion.LATEST_STABLE, ApiVersion.LATEST_STABLE)
return JsConfig(project, configuration, null, null) return JsConfig(project, configuration, null, null)
} }
@@ -169,11 +184,13 @@ abstract class BasicWasmBoxTest(
} }
} }
val languageVersionSettings = parseLanguageVersionSettings(directives)
val temporaryFile = File(tmpDir, "WASM_TEST/$fileName") val temporaryFile = File(tmpDir, "WASM_TEST/$fileName")
KotlinTestUtils.mkdirs(temporaryFile.parentFile) KotlinTestUtils.mkdirs(temporaryFile.parentFile)
temporaryFile.writeText(text, Charsets.UTF_8) temporaryFile.writeText(text, Charsets.UTF_8)
return TestFile(temporaryFile.absolutePath) return TestFile(temporaryFile.absolutePath, languageVersionSettings)
} }
var testPackage: String? = null var testPackage: String? = null
@@ -184,7 +201,7 @@ abstract class BasicWasmBoxTest(
} }
} }
private class TestFile(val fileName: String) private class TestFile(val fileName: String, val languageVersionSettings: LanguageVersionSettings?)
override fun createEnvironment() = override fun createEnvironment() =
KotlinCoreEnvironment.createForTests(testRootDisposable, CompilerConfiguration(), EnvironmentConfigFiles.JS_CONFIG_FILES) KotlinCoreEnvironment.createForTests(testRootDisposable, CompilerConfiguration(), EnvironmentConfigFiles.JS_CONFIG_FILES)
@@ -39,6 +39,6 @@ class SpiderMonkeyEngine(
private val jsShell = ExternalTool(jsShellPath) private val jsShell = ExternalTool(jsShellPath)
fun runFile(file: String) { fun runFile(file: String) {
jsShell.run(file) jsShell.run("--wasm-gc", file)
} }
} }
@@ -10655,6 +10655,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
runTest("compiler/testData/codegen/box/functions/recursiveIncrementCall.kt"); runTest("compiler/testData/codegen/box/functions/recursiveIncrementCall.kt");
} }
@TestMetadata("typeParameterAsUpperBound.kt")
public void testTypeParameterAsUpperBound() throws Exception {
runTest("compiler/testData/codegen/box/functions/typeParameterAsUpperBound.kt");
}
@TestMetadata("typeParametersInLocalFunction.kt") @TestMetadata("typeParametersInLocalFunction.kt")
public void testTypeParametersInLocalFunction() throws Exception { public void testTypeParametersInLocalFunction() throws Exception {
runTest("compiler/testData/codegen/box/functions/typeParametersInLocalFunction.kt"); runTest("compiler/testData/codegen/box/functions/typeParametersInLocalFunction.kt");
@@ -10655,6 +10655,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/functions/recursiveIncrementCall.kt"); runTest("compiler/testData/codegen/box/functions/recursiveIncrementCall.kt");
} }
@TestMetadata("typeParameterAsUpperBound.kt")
public void testTypeParameterAsUpperBound() throws Exception {
runTest("compiler/testData/codegen/box/functions/typeParameterAsUpperBound.kt");
}
@TestMetadata("typeParametersInLocalFunction.kt") @TestMetadata("typeParametersInLocalFunction.kt")
public void testTypeParametersInLocalFunction() throws Exception { public void testTypeParametersInLocalFunction() throws Exception {
runTest("compiler/testData/codegen/box/functions/typeParametersInLocalFunction.kt"); runTest("compiler/testData/codegen/box/functions/typeParametersInLocalFunction.kt");
@@ -10655,6 +10655,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/functions/recursiveIncrementCall.kt"); runTest("compiler/testData/codegen/box/functions/recursiveIncrementCall.kt");
} }
@TestMetadata("typeParameterAsUpperBound.kt")
public void testTypeParameterAsUpperBound() throws Exception {
runTest("compiler/testData/codegen/box/functions/typeParameterAsUpperBound.kt");
}
@TestMetadata("typeParametersInLocalFunction.kt") @TestMetadata("typeParametersInLocalFunction.kt")
public void testTypeParametersInLocalFunction() throws Exception { public void testTypeParametersInLocalFunction() throws Exception {
runTest("compiler/testData/codegen/box/functions/typeParametersInLocalFunction.kt"); runTest("compiler/testData/codegen/box/functions/typeParametersInLocalFunction.kt");
+54 -5
View File
@@ -1,19 +1,65 @@
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.plugin.KotlinJsCompilerType.IR
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin
plugins { plugins {
kotlin("multiplatform") kotlin("multiplatform")
} }
val unimplementedNativeBuiltIns =
(file("$rootDir/core/builtins/native/kotlin/").list().toSortedSet() - file("$rootDir/libraries/stdlib/wasm/builtins/kotlin/").list())
.map { "core/builtins/native/kotlin/$it" }
val builtInsSources by task<Sync> {
val sources = listOf(
"core/builtins/src/kotlin/"
) + unimplementedNativeBuiltIns
val excluded = listOf(
// JS-specific optimized version of emptyArray() already defined
"core/builtins/src/kotlin/ArrayIntrinsics.kt"
)
sources.forEach { path ->
from("$rootDir/$path") {
into(path.dropLastWhile { it != '/' })
excluded.filter { it.startsWith(path) }.forEach {
exclude(it.substring(path.length))
}
}
}
into("$buildDir/builtInsSources")
}
val commonMainSources by task<Sync> {
val sources = listOf(
"libraries/stdlib/common/src/",
"libraries/stdlib/src/kotlin/",
"libraries/stdlib/unsigned/"
)
sources.forEach { path ->
from("$rootDir/$path") {
into(path.dropLastWhile { it != '/' })
}
}
into("$buildDir/commonMainSources")
}
kotlin { kotlin {
js(IR) { js(IR) {
nodejs() nodejs()
} }
sourceSets { sourceSets {
val jsMain by getting { val jsMain by getting {
kotlin.srcDirs("builtins", "internal", "runtime") kotlin.srcDirs("builtins", "internal", "runtime", "src", "stubs")
kotlin.srcDirs(files(builtInsSources.map { it.destinationDir }))
}
val commonMain by getting {
kotlin.srcDirs(files(commonMainSources.map { it.destinationDir }))
} }
} }
} }
@@ -28,10 +74,13 @@ tasks.withType<KotlinCompile<*>>().configureEach {
"-Xinline-classes", "-Xinline-classes",
"-Xopt-in=kotlin.RequiresOptIn", "-Xopt-in=kotlin.RequiresOptIn",
"-Xopt-in=kotlin.ExperimentalUnsignedTypes", "-Xopt-in=kotlin.ExperimentalUnsignedTypes",
"-Xopt-in=kotlin.ExperimentalStdlibApi" "-Xopt-in=kotlin.ExperimentalStdlibApi",
"-Xexplicit-api=warning"
) )
} }
tasks.named("compileKotlinJs") { tasks.named("compileKotlinJs") {
(this as KotlinCompile<*>).kotlinOptions.freeCompilerArgs += "-Xir-module-name=kotlin" (this as KotlinCompile<*>).kotlinOptions.freeCompilerArgs += "-Xir-module-name=kotlin"
dependsOn(commonMainSources)
dependsOn(builtInsSources)
} }
-6
View File
@@ -1,6 +0,0 @@
This directory is a modified copy of `core/builtins` adapted for current
needs of WASM backend.
This is a temporary solution for a development convenience. Most of files
from `core/builtins` will be reused once compiler implementation and
stdlib become stable.
@@ -3,20 +3,19 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@file:Suppress(
"NON_ABSTRACT_FUNCTION_WITH_NO_BODY",
"MUST_BE_INITIALIZED_OR_BE_ABSTRACT",
"EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE",
"PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED",
"WRONG_MODIFIER_TARGET"
)
package kotlin package kotlin
import kotlin.wasm.internal.*
/** /**
* The root of the Kotlin class hierarchy. Every Kotlin class has [Any] as a superclass. * The root of the Kotlin class hierarchy. Every Kotlin class has [Any] as a superclass.
*/ */
public open class Any { public open class Any {
// Pointer to runtime type info
// Initialized by a compiler
@Suppress("MUST_BE_INITIALIZED_OR_BE_ABSTRACT")
internal var typeInfo: Int
/** /**
* Indicates whether some other object is "equal to" this one. Implementations must fulfil the following * Indicates whether some other object is "equal to" this one. Implementations must fulfil the following
* requirements: * requirements:
@@ -29,7 +28,8 @@ public open class Any {
* *
* Read more about [equality](https://kotlinlang.org/docs/reference/equality.html) in Kotlin. * Read more about [equality](https://kotlinlang.org/docs/reference/equality.html) in Kotlin.
*/ */
public open operator fun equals(other: Any?): Boolean public open operator fun equals(other: Any?): Boolean =
wasm_ref_eq(this, other)
/** /**
* Returns a hash code value for the object. The general contract of `hashCode` is: * Returns a hash code value for the object. The general contract of `hashCode` is:
@@ -37,10 +37,12 @@ public open class Any {
* * Whenever it is invoked on the same object more than once, the `hashCode` method must consistently return the same integer, provided no information used in `equals` comparisons on the object is modified. * * Whenever it is invoked on the same object more than once, the `hashCode` method must consistently return the same integer, provided no information used in `equals` comparisons on the object is modified.
* * If two objects are equal according to the `equals()` method, then calling the `hashCode` method on each of the two objects must produce the same integer result. * * If two objects are equal according to the `equals()` method, then calling the `hashCode` method on each of the two objects must produce the same integer result.
*/ */
public open fun hashCode(): Int // TODO: Implement
public open fun hashCode(): Int = 100
/** /**
* Returns a string representation of the object. * Returns a string representation of the object.
*/ */
public open fun toString(): String // TODO: Implement
public open fun toString(): String = "[Object object]"
} }
@@ -3,16 +3,10 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@file:Suppress(
"NON_ABSTRACT_FUNCTION_WITH_NO_BODY",
"MUST_BE_INITIALIZED_OR_BE_ABSTRACT",
"EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE",
"PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED",
"WRONG_MODIFIER_TARGET"
)
package kotlin package kotlin
import kotlin.wasm.internal.*
/** /**
* Represents an array (specifically, a Java array when targeting the JVM platform). * Represents an array (specifically, a Java array when targeting the JVM platform).
* Array instances can be created using the [arrayOf], [arrayOfNulls] and [emptyArray] * Array instances can be created using the [arrayOf], [arrayOfNulls] and [emptyArray]
@@ -20,7 +14,9 @@ package kotlin
* See [Kotlin language documentation](https://kotlinlang.org/docs/reference/basic-types.html#arrays) * See [Kotlin language documentation](https://kotlinlang.org/docs/reference/basic-types.html#arrays)
* for more information on arrays. * for more information on arrays.
*/ */
public class Array<T> { public class Array<T> constructor(size: Int) {
private var jsArray: WasmExternRef = JsArray_new(size)
/** /**
* Creates a new array with the specified [size], where each element is calculated by calling the specified * Creates a new array with the specified [size], where each element is calculated by calling the specified
* [init] function. * [init] function.
@@ -28,7 +24,10 @@ public class Array<T> {
* The function [init] is called for each array element sequentially starting from the first one. * The function [init] is called for each array element sequentially starting from the first one.
* It should return the value for an array element given its index. * It should return the value for an array element given its index.
*/ */
public inline constructor(size: Int, init: (Int) -> T) @Suppress("TYPE_PARAMETER_AS_REIFIED")
public constructor(size: Int, init: (Int) -> T) : this(size) {
JsArray_fill_T(jsArray, size, init)
}
/** /**
* Returns the array element at the specified [index]. This method can be called using the * Returns the array element at the specified [index]. This method can be called using the
@@ -40,7 +39,9 @@ public class Array<T> {
* If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException] except in Kotlin/JS * If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException] except in Kotlin/JS
* where the behavior is unspecified. * where the behavior is unspecified.
*/ */
public operator fun get(index: Int): T @Suppress("UNCHECKED_CAST")
public operator fun get(index: Int): T =
WasmExternRefToAny(JsArray_get_WasmExternRef(jsArray, index)) as T
/** /**
* Sets the array element at the specified [index] to the specified [value]. This method can * Sets the array element at the specified [index] to the specified [value]. This method can
@@ -52,15 +53,25 @@ public class Array<T> {
* If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException] except in Kotlin/JS * If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException] except in Kotlin/JS
* where the behavior is unspecified. * where the behavior is unspecified.
*/ */
public operator fun set(index: Int, value: T): Unit public operator fun set(index: Int, value: T) {
JsArray_set_WasmExternRef(jsArray, index, value.toWasmExternRef())
}
/** /**
* Returns the number of elements in the array. * Returns the number of elements in the array.
*/ */
public val size: Int public val size: Int
get() = JsArray_getSize(jsArray)
/** /**
* Creates an iterator for iterating over the elements of the array. * Creates an iterator for iterating over the elements of the array.
*/ */
public operator fun iterator(): Iterator<T> public operator fun iterator(): Iterator<T> = arrayIterator(this)
} }
internal fun <T> arrayIterator(array: Array<T>) = object : Iterator<T> {
var index = 0
override fun hasNext() = index != array.size
override fun next() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
}
@@ -0,0 +1,265 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin
import kotlin.wasm.internal.*
public class ByteArray(size: Int) {
private var jsArray: WasmExternRef = JsArray_new(size)
init {
JsArray_fill_Byte(jsArray, size) { 0 }
}
public constructor(size: Int, init: (Int) -> Byte) : this(size) {
jsArray = JsArray_new(size)
JsArray_fill_Byte(jsArray, size, init)
}
public operator fun get(index: Int): Byte =
JsArray_get_Byte(jsArray, index)
public operator fun set(index: Int, value: Byte) {
JsArray_set_Byte(jsArray, index, value)
}
public val size: Int
get() = JsArray_getSize(jsArray)
public operator fun iterator(): ByteIterator = byteArrayIterator(this)
}
internal fun byteArrayIterator(array: ByteArray) = object : ByteIterator() {
var index = 0
override fun hasNext() = index != array.size
override fun nextByte() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
}
public class CharArray(size: Int) {
private var jsArray: WasmExternRef = JsArray_new(size)
init {
JsArray_fill_Char(jsArray, size) { 0.toChar() }
}
public constructor(size: Int, init: (Int) -> Char) : this(size) {
jsArray = JsArray_new(size)
JsArray_fill_Char(jsArray, size, init)
}
public operator fun get(index: Int): Char =
JsArray_get_Char(jsArray, index)
public operator fun set(index: Int, value: Char) {
JsArray_set_Char(jsArray, index, value)
}
public val size: Int
get() = JsArray_getSize(jsArray)
public operator fun iterator(): CharIterator = charArrayIterator(this)
}
internal fun charArrayIterator(array: CharArray) = object : CharIterator() {
var index = 0
override fun hasNext() = index != array.size
override fun nextChar() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
}
public class ShortArray(size: Int) {
private var jsArray: WasmExternRef = JsArray_new(size)
init {
JsArray_fill_Short(jsArray, size) { 0 }
}
public constructor(size: Int, init: (Int) -> Short) : this(size) {
JsArray_fill_Short(jsArray, size, init)
}
public operator fun get(index: Int): Short =
JsArray_get_Short(jsArray, index)
public operator fun set(index: Int, value: Short) {
JsArray_set_Short(jsArray, index, value)
}
public val size: Int
get() = JsArray_getSize(jsArray)
public operator fun iterator(): ShortIterator = shortArrayIterator(this)
}
internal fun shortArrayIterator(array: ShortArray) = object : ShortIterator() {
var index = 0
override fun hasNext() = index != array.size
override fun nextShort() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
}
public class IntArray(size: Int) {
private var jsArray: WasmExternRef = JsArray_new(size)
init {
JsArray_fill_Int(jsArray, size) { 0 }
}
public constructor(size: Int, init: (Int) -> Int) : this(size) {
JsArray_fill_Int(jsArray, size, init)
}
public operator fun get(index: Int): Int =
JsArray_get_Int(jsArray, index)
public operator fun set(index: Int, value: Int) {
JsArray_set_Int(jsArray, index, value)
}
public val size: Int
get() = JsArray_getSize(jsArray)
public operator fun iterator(): IntIterator = intArrayIterator(this)
}
internal fun intArrayIterator(array: IntArray) = object : IntIterator() {
var index = 0
override fun hasNext() = index != array.size
override fun nextInt() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
}
public class LongArray(size: Int) {
private var jsArray: WasmExternRef = JsArray_new(size)
init {
JsArray_fill_Long(jsArray, size) { 0L }
}
public constructor(size: Int, init: (Int) -> Long) : this(size) {
JsArray_fill_Long(jsArray, size, init)
}
public operator fun get(index: Int): Long =
JsArray_get_Long(jsArray, index)
public operator fun set(index: Int, value: Long) {
JsArray_set_Long(jsArray, index, value)
}
public val size: Int
get() = JsArray_getSize(jsArray)
public operator fun iterator(): LongIterator = longArrayIterator(this)
}
internal fun longArrayIterator(array: LongArray) = object : LongIterator() {
var index = 0
override fun hasNext() = index != array.size
override fun nextLong() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
}
public class FloatArray(size: Int) {
private var jsArray: WasmExternRef = JsArray_new(size)
init {
JsArray_fill_Float(jsArray, size) { 0.0f }
}
public constructor(size: Int, init: (Int) -> Float) : this(size) {
JsArray_fill_Float(jsArray, size, init)
}
public operator fun get(index: Int): Float =
JsArray_get_Float(jsArray, index)
public operator fun set(index: Int, value: Float) {
JsArray_set_Float(jsArray, index, value)
}
public val size: Int
get() = JsArray_getSize(jsArray)
public operator fun iterator(): FloatIterator = floatArrayIterator(this)
}
internal fun floatArrayIterator(array: FloatArray) = object : FloatIterator() {
var index = 0
override fun hasNext() = index != array.size
override fun nextFloat() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
}
public class DoubleArray(size: Int) {
private var jsArray: WasmExternRef = JsArray_new(size)
init {
JsArray_fill_Double(jsArray, size) { 0.0 }
}
public constructor(size: Int, init: (Int) -> Double) : this(size) {
JsArray_fill_Double(jsArray, size, init)
}
public operator fun get(index: Int): Double =
JsArray_get_Double(jsArray, index)
public operator fun set(index: Int, value: Double) {
JsArray_set_Double(jsArray, index, value)
}
public val size: Int
get() = JsArray_getSize(jsArray)
public operator fun iterator(): DoubleIterator = doubleArrayIterator(this)
}
internal fun doubleArrayIterator(array: DoubleArray) = object : DoubleIterator() {
var index = 0
override fun hasNext() = index != array.size
override fun nextDouble() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
}
public class BooleanArray(size: Int) {
private var jsArray: WasmExternRef = JsArray_new(size)
init {
JsArray_fill_Boolean(jsArray, size) { false }
}
public constructor(size: Int, init: (Int) -> Boolean) : this(size) {
JsArray_fill_Boolean(jsArray, size, init)
}
public operator fun get(index: Int): Boolean =
JsArray_get_Boolean(jsArray, index)
public operator fun set(index: Int, value: Boolean) {
JsArray_set_Boolean(jsArray, index, value)
}
public val size: Int
get() = JsArray_getSize(jsArray)
public operator fun iterator(): BooleanIterator = booleanArrayIterator(this)
}
internal fun booleanArrayIterator(array: BooleanArray) = object : BooleanIterator() {
var index = 0
override fun hasNext() = index != array.size
override fun nextBoolean() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
}
@@ -3,56 +3,67 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@file:Suppress(
"NON_ABSTRACT_FUNCTION_WITH_NO_BODY",
"MUST_BE_INITIALIZED_OR_BE_ABSTRACT",
"EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE",
"PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED",
"WRONG_MODIFIER_TARGET"
)
package kotlin package kotlin
import kotlin.wasm.internal.WasmInstruction import kotlin.wasm.internal.*
import kotlin.wasm.internal.wasm_i32_compareTo
/** /**
* Represents a value which is either `true` or `false`. On the JVM, non-nullable values of this type are * Represents a value which is either `true` or `false`. On the JVM, non-nullable values of this type are
* represented as values of the primitive type `boolean`. * represented as values of the primitive type `boolean`.
*/ */
public class Boolean private constructor() : Comparable<Boolean> { @WasmPrimitive
public class Boolean private constructor(private val value: Boolean) : Comparable<Boolean> {
/** /**
* Returns the inverse of this boolean. * Returns the inverse of this boolean.
*/ */
@WasmInstruction(WasmInstruction.I32_EQZ) @WasmOp(WasmOp.I32_EQZ)
public operator fun not(): Boolean public operator fun not(): Boolean =
implementedAsIntrinsic
/** /**
* Performs a logical `and` operation between this Boolean and the [other] one. Unlike the `&&` operator, * Performs a logical `and` operation between this Boolean and the [other] one. Unlike the `&&` operator,
* this function does not perform short-circuit evaluation. Both `this` and [other] will always be evaluated. * this function does not perform short-circuit evaluation. Both `this` and [other] will always be evaluated.
*/ */
@WasmInstruction(WasmInstruction.I32_AND) @WasmOp(WasmOp.I32_AND)
public infix fun and(other: Boolean): Boolean public infix fun and(other: Boolean): Boolean =
implementedAsIntrinsic
/** /**
* Performs a logical `or` operation between this Boolean and the [other] one. Unlike the `||` operator, * Performs a logical `or` operation between this Boolean and the [other] one. Unlike the `||` operator,
* this function does not perform short-circuit evaluation. Both `this` and [other] will always be evaluated. * this function does not perform short-circuit evaluation. Both `this` and [other] will always be evaluated.
*/ */
@WasmInstruction(WasmInstruction.I32_OR) @WasmOp(WasmOp.I32_OR)
public infix fun or(other: Boolean): Boolean public infix fun or(other: Boolean): Boolean =
implementedAsIntrinsic
/** /**
* Performs a logical `xor` operation between this Boolean and the [other] one. * Performs a logical `xor` operation between this Boolean and the [other] one.
*/ */
@WasmInstruction(WasmInstruction.I32_XOR) @WasmOp(WasmOp.I32_XOR)
public infix fun xor(other: Boolean): Boolean public infix fun xor(other: Boolean): Boolean =
implementedAsIntrinsic
public override fun compareTo(other: Boolean): Int = public override fun compareTo(other: Boolean): Int =
wasm_i32_compareTo(this.asInt(), other.asInt()) wasm_i32_compareTo(this.toInt(), other.toInt())
@WasmInstruction(WasmInstruction.NOP) override fun toString(): String =
internal fun asInt(): Int if (this) "true" else "false"
override fun hashCode(): Int =
toInt()
override fun equals(other: Any?): Boolean {
return if (other !is Boolean) {
false
} else {
this === (other as Boolean)
}
}
@WasmReinterpret
internal fun toInt(): Int =
implementedAsIntrinsic
@SinceKotlin("1.3") @SinceKotlin("1.3")
companion object {} public companion object
} }
@@ -3,30 +3,34 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@file:Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
package kotlin package kotlin
import kotlin.wasm.internal.ExcludedFromCodegen import kotlin.wasm.internal.*
import kotlin.wasm.internal.WasmInstruction
import kotlin.wasm.internal.implementedAsIntrinsic
import kotlin.wasm.internal.wasm_i32_compareTo
/** /**
* Represents a 16-bit Unicode character. * Represents a 16-bit Unicode character.
* *
* On the JVM, non-nullable values of this type are represented as values of the primitive type `char`. * On the JVM, non-nullable values of this type are represented as values of the primitive type `char`.
*/ */
public class Char private constructor() : Comparable<Char> { @WasmPrimitive
@Suppress("NOTHING_TO_INLINE")
public class Char private constructor(public val value: Char) : Comparable<Char> {
/** /**
* Compares this value with the specified value for order. * Compares this value with the specified value for order.
* *
* Returns zero if this value is equal to the specified other value, a negative number if it's less than other, * Returns zero if this value is equal to the specified other value, a negative number if it's less than other,
* or a positive number if it's greater than other. * or a positive number if it's greater than other.
*/ */
public override inline fun compareTo(other: Char): Int = public override fun compareTo(other: Char): Int =
wasm_i32_compareTo(this.toInt(), other.toInt()) wasm_i32_compareTo(this.toInt(), other.toInt())
public override fun equals(other: Any?): Boolean {
if (other is Char)
return this === (other as Char)
return false
}
/** Adds the other Int value to this value resulting a Char. */ /** Adds the other Int value to this value resulting a Char. */
public inline operator fun plus(other: Int): Char = public inline operator fun plus(other: Int): Char =
(this.toInt() + other).toChar() (this.toInt() + other).toChar()
@@ -47,34 +51,46 @@ public class Char private constructor() : Comparable<Char> {
public inline operator fun dec(): Char = public inline operator fun dec(): Char =
(this.toInt() - 1).toChar() (this.toInt() - 1).toChar()
// /** Creates a range from this value to the specified [other] value. */ /** Creates a range from this value to the specified [other] value. */
// public operator fun rangeTo(other: Char): CharRange public operator fun rangeTo(other: Char): CharRange =
CharRange(this, other)
/** Returns the value of this character as a `Byte`. */ /** Returns the value of this character as a `Byte`. */
public inline fun toByte(): Byte = public inline fun toByte(): Byte =
this.toInt().toByte() this.toInt().toByte()
/** Returns the value of this character as a `Char`. */ /** Returns the value of this character as a `Char`. */
public inline fun toChar(): Char = public inline fun toChar(): Char =
this this
/** Returns the value of this character as a `Short`. */ /** Returns the value of this character as a `Short`. */
public inline fun toShort(): Short = public inline fun toShort(): Short =
this.toInt().toShort() this.toInt().toShort()
/** Returns the value of this character as a `Int`. */ /** Returns the value of this character as a `Int`. */
@WasmInstruction(WasmInstruction.NOP) @WasmReinterpret
public fun toInt(): Int = public fun toInt(): Int =
implementedAsIntrinsic implementedAsIntrinsic
/** Returns the value of this character as a `Long`. */ /** Returns the value of this character as a `Long`. */
public inline fun toLong(): Long = public inline fun toLong(): Long =
this.toInt().toLong() this.toInt().toLong()
/** Returns the value of this character as a `Float`. */ /** Returns the value of this character as a `Float`. */
public inline fun toFloat(): Float = public inline fun toFloat(): Float =
this.toInt().toFloat() this.toInt().toFloat()
/** Returns the value of this character as a `Double`. */ /** Returns the value of this character as a `Double`. */
public inline fun toDouble(): Double = public inline fun toDouble(): Double =
this.toInt().toDouble() this.toInt().toDouble()
@ExcludedFromCodegen override fun toString(): String =
companion object { charToString(this)
override fun hashCode(): Int =
this.toInt().hashCode()
public companion object {
/** /**
* The minimum value of a character code unit. * The minimum value of a character code unit.
*/ */
@@ -129,6 +145,8 @@ public class Char private constructor() : Comparable<Char> {
@SinceKotlin("1.3") @SinceKotlin("1.3")
public const val SIZE_BITS: Int = 16 public const val SIZE_BITS: Int = 16
} }
} }
@WasmImport("runtime", "Char_toString")
private fun charToString(c: Char): String = implementedAsIntrinsic
@@ -0,0 +1,20 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin
public abstract class Enum<E : Enum<E>>(
public val name: String,
public val ordinal: Int
) : Comparable<E> {
override fun compareTo(other: E): Int =
ordinal.compareTo(other.ordinal)
override fun toString(): String =
name
public companion object
}
@@ -2,86 +2,90 @@
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@file:Suppress(
"NON_MEMBER_FUNCTION_NO_BODY", @file:Suppress("NOTHING_TO_INLINE")
"REIFIED_TYPE_PARAMETER_NO_INLINE"
)
@file:kotlin.wasm.internal.ExcludedFromCodegen
package kotlin package kotlin
import kotlin.internal.PureReifiable import kotlin.internal.PureReifiable
public inline fun <T> emptyArray(): Array<T> = arrayOf()
/** /**
* Returns a string representation of the object. Can be called with a null receiver, in which case * Returns a string representation of the object. Can be called with a null receiver, in which case
* it returns the string "null". * it returns the string "null".
*/ */
public fun Any?.toString(): String public fun Any?.toString(): String = this?.toString() ?: "null"
/** /**
* Concatenates this string with the string representation of the given [other] object. If either the receiver * Concatenates this string with the string representation of the given [other] object. If either the receiver
* or the [other] object are null, they are represented as the string "null". * or the [other] object are null, they are represented as the string "null".
*/ */
public operator fun String?.plus(other: Any?): String public operator fun String?.plus(other: Any?): String = (this ?: "null") + other.toString()
/** /**
* Returns an array of objects of the given type with the given [size], initialized with null values. * Returns an array of objects of the given type with the given [size], initialized with null values.
*/ */
public fun <reified @PureReifiable T> arrayOfNulls(size: Int): Array<T?> // TODO: Should T be reified?
@Suppress("REIFIED_TYPE_PARAMETER_NO_INLINE")
public fun <@PureReifiable reified T> arrayOfNulls(size: Int): Array<T?> = Array(size) { null }
/** /**
* Returns an array containing the specified elements. * Returns an array containing the specified elements.
*/ */
public inline fun <reified @PureReifiable T> arrayOf(vararg elements: T): Array<T> @Suppress("UNCHECKED_CAST")
public inline fun <T> arrayOf(vararg elements: T): Array<T> = elements as Array<T>
/** /**
* Returns an array containing the specified [Double] numbers. * Returns an array containing the specified [Double] numbers.
*/ */
public fun doubleArrayOf(vararg elements: Double): DoubleArray public inline fun doubleArrayOf(vararg elements: Double): DoubleArray = elements
/** /**
* Returns an array containing the specified [Float] numbers. * Returns an array containing the specified [Float] numbers.
*/ */
public fun floatArrayOf(vararg elements: Float): FloatArray public inline fun floatArrayOf(vararg elements: Float): FloatArray = elements
/** /**
* Returns an array containing the specified [Long] numbers. * Returns an array containing the specified [Long] numbers.
*/ */
public fun longArrayOf(vararg elements: Long): LongArray public inline fun longArrayOf(vararg elements: Long): LongArray = elements
/** /**
* Returns an array containing the specified [Int] numbers. * Returns an array containing the specified [Int] numbers.
*/ */
public fun intArrayOf(vararg elements: Int): IntArray public inline fun intArrayOf(vararg elements: Int): IntArray = elements
/** /**
* Returns an array containing the specified characters. * Returns an array containing the specified characters.
*/ */
public fun charArrayOf(vararg elements: Char): CharArray public inline fun charArrayOf(vararg elements: Char): CharArray = elements
/** /**
* Returns an array containing the specified [Short] numbers. * Returns an array containing the specified [Short] numbers.
*/ */
public fun shortArrayOf(vararg elements: Short): ShortArray public inline fun shortArrayOf(vararg elements: Short): ShortArray = elements
/** /**
* Returns an array containing the specified [Byte] numbers. * Returns an array containing the specified [Byte] numbers.
*/ */
public fun byteArrayOf(vararg elements: Byte): ByteArray public inline fun byteArrayOf(vararg elements: Byte): ByteArray = elements
/** /**
* Returns an array containing the specified boolean values. * Returns an array containing the specified boolean values.
*/ */
public fun booleanArrayOf(vararg elements: Boolean): BooleanArray public inline fun booleanArrayOf(vararg elements: Boolean): BooleanArray = elements
/** /**
* Returns an array containing enum T entries. * Returns an array containing enum T entries.
*/ */
@SinceKotlin("1.1") @SinceKotlin("1.1")
@Suppress("NON_MEMBER_FUNCTION_NO_BODY")
public inline fun <reified T : Enum<T>> enumValues(): Array<T> public inline fun <reified T : Enum<T>> enumValues(): Array<T>
/** /**
* Returns an enum entry with specified name. * Returns an enum entry with specified name.
*/ */
@SinceKotlin("1.1") @SinceKotlin("1.1")
@Suppress("NON_MEMBER_FUNCTION_NO_BODY")
public inline fun <reified T : Enum<T>> enumValueOf(name: String): T public inline fun <reified T : Enum<T>> enumValueOf(name: String): T
@@ -16,8 +16,11 @@
package kotlin package kotlin
import kotlin.wasm.internal.ExcludedFromCodegen
/** /**
* Nothing has no instances. You can use Nothing to represent "a value that never exists": for example, * Nothing has no instances. You can use Nothing to represent "a value that never exists": for example,
* if a function has the return type of Nothing, it means that it never returns (always throws an exception). * if a function has the return type of Nothing, it means that it never returns (always throws an exception).
*/ */
@ExcludedFromCodegen
public class Nothing private constructor() public class Nothing private constructor()
@@ -2,7 +2,11 @@
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@file:Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") @file:Suppress(
"OVERRIDE_BY_INLINE",
"NOTHING_TO_INLINE",
"unused"
)
package kotlin package kotlin
@@ -11,9 +15,9 @@ import kotlin.wasm.internal.*
/** /**
* Represents a 8-bit signed integer. * Represents a 8-bit signed integer.
*/ */
public class Byte private constructor() : Number(), Comparable<Byte> { @WasmPrimitive
@ExcludedFromCodegen public class Byte private constructor(public val value: Byte) : Number(), Comparable<Byte> {
companion object { public companion object {
/** /**
* A constant holding the minimum value an instance of Byte can have. * A constant holding the minimum value an instance of Byte can have.
*/ */
@@ -233,8 +237,7 @@ public class Byte private constructor() : Number(), Comparable<Byte> {
* The least significant 8 bits of the resulting `Char` code are the same as the bits of this `Byte` value, * The least significant 8 bits of the resulting `Char` code are the same as the bits of this `Byte` value,
* whereas the most significant 8 bits are filled with the sign bit of this value. * whereas the most significant 8 bits are filled with the sign bit of this value.
*/ */
@WasmInstruction(WasmInstruction.NOP) public override fun toChar(): Char = reinterpretAsInt().reinterpretAsChar()
public override fun toChar(): Char = implementedAsIntrinsic
/** /**
* Converts this [Byte] value to [Short]. * Converts this [Byte] value to [Short].
@@ -244,8 +247,7 @@ public class Byte private constructor() : Number(), Comparable<Byte> {
* The least significant 8 bits of the resulting `Short` value are the same as the bits of this `Byte` value, * The least significant 8 bits of the resulting `Short` value are the same as the bits of this `Byte` value,
* whereas the most significant 8 bits are filled with the sign bit of this value. * whereas the most significant 8 bits are filled with the sign bit of this value.
*/ */
@WasmInstruction(WasmInstruction.NOP) public override fun toShort(): Short = reinterpretAsInt().reinterpretAsShort()
public override fun toShort(): Short = implementedAsIntrinsic
/** /**
* Converts this [Byte] value to [Int]. * Converts this [Byte] value to [Int].
@@ -255,8 +257,7 @@ public class Byte private constructor() : Number(), Comparable<Byte> {
* The least significant 8 bits of the resulting `Int` value are the same as the bits of this `Byte` value, * The least significant 8 bits of the resulting `Int` value are the same as the bits of this `Byte` value,
* whereas the most significant 24 bits are filled with the sign bit of this value. * whereas the most significant 24 bits are filled with the sign bit of this value.
*/ */
@WasmInstruction(WasmInstruction.NOP) public override fun toInt(): Int = reinterpretAsInt()
public override fun toInt(): Int = implementedAsIntrinsic
/** /**
* Converts this [Byte] value to [Long]. * Converts this [Byte] value to [Long].
@@ -266,62 +267,93 @@ public class Byte private constructor() : Number(), Comparable<Byte> {
* The least significant 8 bits of the resulting `Long` value are the same as the bits of this `Byte` value, * The least significant 8 bits of the resulting `Long` value are the same as the bits of this `Byte` value,
* whereas the most significant 56 bits are filled with the sign bit of this value. * whereas the most significant 56 bits are filled with the sign bit of this value.
*/ */
@WasmInstruction(WasmInstruction.I64_EXTEND_I32_S) public override fun toLong(): Long = wasm_i64_extend_i32_s(this.toInt())
public override fun toLong(): Long = implementedAsIntrinsic
/** /**
* Converts this [Byte] value to [Float]. * Converts this [Byte] value to [Float].
* *
* The resulting `Float` value represents the same numerical value as this `Byte`. * The resulting `Float` value represents the same numerical value as this `Byte`.
*/ */
@WasmInstruction(WasmInstruction.F32_CONVERT_I32_S) public override fun toFloat(): Float = wasm_f32_convert_i32_s(this.toInt())
public override fun toFloat(): Float = implementedAsIntrinsic
/** /**
* Converts this [Byte] value to [Double]. * Converts this [Byte] value to [Double].
* *
* The resulting `Double` value represents the same numerical value as this `Byte`. * The resulting `Double` value represents the same numerical value as this `Byte`.
*/ */
@WasmInstruction(WasmInstruction.F64_CONVERT_I32_S) public override fun toDouble(): Double = wasm_f64_convert_i32_s(this.toInt())
public override fun toDouble(): Double = implementedAsIntrinsic
// /** Creates a range from this value to the specified [other] value. */ /** Creates a range from this value to the specified [other] value. */
// public operator fun rangeTo(other: Byte): IntRange { public operator fun rangeTo(other: Byte): IntRange {
// return IntRange(this.toInt(), other.toInt()) return IntRange(this.toInt(), other.toInt())
// } }
// /** Creates a range from this value to the specified [other] value. */
// public operator fun rangeTo(other: Short): IntRange {
// return IntRange(this.toInt(), other.toInt())
// }
// /** Creates a range from this value to the specified [other] value. */
// public operator fun rangeTo(other: Int): IntRange {
// return IntRange(this.toInt(), other.toInt())
// }
// /** Creates a range from this value to the specified [other] value. */
// public operator fun rangeTo(other: Long): LongRange {
// return LongRange(this.toLong(), other.toLong())
// }
// TODO: Support Any? and type operators /** Creates a range from this value to the specified [other] value. */
// public override fun equals(other: Any?): Boolean = public operator fun rangeTo(other: Short): IntRange {
// other is Byte && wasm_i32_eq(this.toInt(), other.toInt()).reinterpretAsBoolean() return IntRange(this.toInt(), other.toInt())
}
/** Creates a range from this value to the specified [other] value. */
public operator fun rangeTo(other: Int): IntRange {
return IntRange(this.toInt(), other.toInt())
}
/** Creates a range from this value to the specified [other] value. */
public operator fun rangeTo(other: Long): LongRange {
return LongRange(this.toLong(), other.toLong())
}
/** Performs a bitwise AND operation between the two values. */
@SinceKotlin("1.1")
@WasmOp(WasmOp.I32_AND)
public infix fun and(other: Byte): Byte =
implementedAsIntrinsic
/** Performs a bitwise OR operation between the two values. */
@SinceKotlin("1.1")
@WasmOp(WasmOp.I32_OR)
public infix fun or(other: Byte): Byte =
implementedAsIntrinsic
/** Performs a bitwise XOR operation between the two values. */
@SinceKotlin("1.1")
@WasmOp(WasmOp.I32_XOR)
public infix fun xor(other: Byte): Byte =
implementedAsIntrinsic
/** Inverts the bits in this value/ */
@SinceKotlin("1.1")
public inline fun inv(): Byte = this.xor(-1)
public override fun equals(other: Any?): Boolean =
other is Byte && wasm_i32_eq(this.toInt(), other.toInt())
public inline fun equals(other: Byte): Boolean = public inline fun equals(other: Byte): Boolean =
wasm_i32_eq(this.toInt(), other.toInt()).reinterpretAsBoolean() wasm_i32_eq(this.toInt(), other.toInt())
// TODO: Implement Byte.toString() public override fun toString(): String =
// public override fun toString(): String byteToStringImpl(this)
public override inline fun hashCode(): Int = public override inline fun hashCode(): Int =
this.toInt() this.toInt()
@WasmReinterpret
@PublishedApi
internal fun reinterpretAsInt(): Int =
implementedAsIntrinsic
} }
@WasmImport("runtime", "coerceToString")
private fun byteToStringImpl(byte: Byte): String =
implementedAsIntrinsic
/** /**
* Represents a 16-bit signed integer. * Represents a 16-bit signed integer.
*/ */
public class Short private constructor() : Number(), Comparable<Short> { @WasmPrimitive
@ExcludedFromCodegen public class Short private constructor(public val value: Short) : Number(), Comparable<Short> {
companion object { public companion object {
/** /**
* A constant holding the minimum value an instance of Short can have. * A constant holding the minimum value an instance of Short can have.
*/ */
@@ -529,22 +561,48 @@ public class Short private constructor() : Number(), Comparable<Short> {
public inline operator fun unaryMinus(): Int = public inline operator fun unaryMinus(): Int =
-this.toInt() -this.toInt()
// /** Creates a range from this value to the specified [other] value. */ /** Creates a range from this value to the specified [other] value. */
// public operator fun rangeTo(other: Byte): IntRange { public operator fun rangeTo(other: Byte): IntRange {
// return IntRange(this.toInt(), other.toInt()) return IntRange(this.toInt(), other.toInt())
// } }
// /** Creates a range from this value to the specified [other] value. */
// public operator fun rangeTo(other: Short): IntRange { /** Creates a range from this value to the specified [other] value. */
// return IntRange(this.toInt(), other.toInt()) public operator fun rangeTo(other: Short): IntRange {
// } return IntRange(this.toInt(), other.toInt())
// /** Creates a range from this value to the specified [other] value. */ }
// public operator fun rangeTo(other: Int): IntRange {
// return IntRange(this.toInt(), other.toInt()) /** Creates a range from this value to the specified [other] value. */
// } public operator fun rangeTo(other: Int): IntRange {
// /** Creates a range from this value to the specified [other] value. */ return IntRange(this.toInt(), other)
// public operator fun rangeTo(other: Long): LongRange { }
// return LongRange(this.toLong(), other.toLong())
// } /** Creates a range from this value to the specified [other] value. */
public operator fun rangeTo(other: Long): LongRange {
return LongRange(this.toLong(), other)
}
/** Performs a bitwise AND operation between the two values. */
@SinceKotlin("1.1")
@WasmOp(WasmOp.I32_AND)
public infix fun and(other: Short): Short =
implementedAsIntrinsic
/** Performs a bitwise OR operation between the two values. */
@SinceKotlin("1.1")
@WasmOp(WasmOp.I32_OR)
public infix fun or(other: Short): Short =
implementedAsIntrinsic
/** Performs a bitwise XOR operation between the two values. */
@SinceKotlin("1.1")
@WasmOp(WasmOp.I32_XOR)
public infix fun xor(other: Short): Short =
implementedAsIntrinsic
/** Inverts the bits in this value */
@SinceKotlin("1.1")
public fun inv(): Short =
this.xor(-1)
/** /**
* Converts this [Short] value to [Byte]. * Converts this [Short] value to [Byte].
@@ -563,9 +621,7 @@ public class Short private constructor() : Number(), Comparable<Short> {
* The resulting `Char` code is equal to this value reinterpreted as an unsigned number, * The resulting `Char` code is equal to this value reinterpreted as an unsigned number,
* i.e. it has the same binary representation as this `Short`. * i.e. it has the same binary representation as this `Short`.
*/ */
@WasmInstruction(WasmInstruction.NOP) public override fun toChar(): Char = reinterpretAsInt().reinterpretAsChar()
public override fun toChar(): Char =
implementedAsIntrinsic
/** Returns this value. */ /** Returns this value. */
public override inline fun toShort(): Short = public override inline fun toShort(): Short =
@@ -579,9 +635,7 @@ public class Short private constructor() : Number(), Comparable<Short> {
* The least significant 16 bits of the resulting `Int` value are the same as the bits of this `Short` value, * The least significant 16 bits of the resulting `Int` value are the same as the bits of this `Short` value,
* whereas the most significant 16 bits are filled with the sign bit of this value. * whereas the most significant 16 bits are filled with the sign bit of this value.
*/ */
@WasmInstruction(WasmInstruction.NOP) public override fun toInt(): Int = reinterpretAsInt()
public override fun toInt(): Int =
implementedAsIntrinsic
/** /**
* Converts this [Short] value to [Long]. * Converts this [Short] value to [Long].
@@ -591,49 +645,50 @@ public class Short private constructor() : Number(), Comparable<Short> {
* The least significant 16 bits of the resulting `Long` value are the same as the bits of this `Short` value, * The least significant 16 bits of the resulting `Long` value are the same as the bits of this `Short` value,
* whereas the most significant 48 bits are filled with the sign bit of this value. * whereas the most significant 48 bits are filled with the sign bit of this value.
*/ */
@WasmInstruction(WasmInstruction.I64_EXTEND_I32_S) public override fun toLong(): Long = wasm_i64_extend_i32_s(this.toInt())
public override fun toLong(): Long =
implementedAsIntrinsic
/** /**
* Converts this [Short] value to [Float]. * Converts this [Short] value to [Float].
* *
* The resulting `Float` value represents the same numerical value as this `Short`. * The resulting `Float` value represents the same numerical value as this `Short`.
*/ */
@WasmInstruction(WasmInstruction.F32_CONVERT_I32_S) public override fun toFloat(): Float = wasm_f32_convert_i32_s(this.toInt())
public override fun toFloat(): Float =
implementedAsIntrinsic
/** /**
* Converts this [Short] value to [Double]. * Converts this [Short] value to [Double].
* *
* The resulting `Double` value represents the same numerical value as this `Short`. * The resulting `Double` value represents the same numerical value as this `Short`.
*/ */
@WasmInstruction(WasmInstruction.F64_CONVERT_I32_S)
public override fun toDouble(): Double = public override fun toDouble(): Double =
implementedAsIntrinsic wasm_f64_convert_i32_s(this.toInt())
public inline fun equals(other: Short): Boolean = public inline fun equals(other: Short): Boolean =
wasm_i32_eq(this.toInt(), other.toInt()).reinterpretAsBoolean() wasm_i32_eq(this.toInt(), other.toInt())
// TODO: Support Any? and type operators public override fun equals(other: Any?): Boolean =
// public override fun equals(other: Any?): Boolean = other is Short && wasm_i32_eq(this.toInt(), other.toInt())
// other is Short && wasm_i32_eq(this.toInt(), other.toInt()).reinterpretAsBoolean()
// TODO: Implement Short.toString() public override fun toString(): String = shortToStringImpl(this)
// public override fun toString(): String
public override inline fun hashCode(): Int = public override inline fun hashCode(): Int =
this.toInt() this.toInt()
@WasmReinterpret
@PublishedApi
internal fun reinterpretAsInt(): Int =
implementedAsIntrinsic
} }
@WasmImport("runtime", "coerceToString")
private fun shortToStringImpl(x: Short): String = implementedAsIntrinsic
/** /**
* Represents a 32-bit signed integer. * Represents a 32-bit signed integer.
*/ */
public class Int private constructor() : Number(), Comparable<Int> { @WasmPrimitive
public class Int private constructor(val value: Int) : Number(), Comparable<Int> {
@ExcludedFromCodegen public companion object {
companion object {
/** /**
* A constant holding the minimum value an instance of Int can have. * A constant holding the minimum value an instance of Int can have.
*/ */
@@ -714,7 +769,7 @@ public class Int private constructor() : Number(), Comparable<Int> {
this + other.toInt() this + other.toInt()
/** Adds the other value to this value. */ /** Adds the other value to this value. */
@WasmInstruction(WasmInstruction.I32_ADD) @WasmOp(WasmOp.I32_ADD)
public operator fun plus(other: Int): Int = public operator fun plus(other: Int): Int =
implementedAsIntrinsic implementedAsIntrinsic
@@ -739,7 +794,7 @@ public class Int private constructor() : Number(), Comparable<Int> {
this - other.toInt() this - other.toInt()
/** Subtracts the other value from this value. */ /** Subtracts the other value from this value. */
@WasmInstruction(WasmInstruction.I32_SUB) @WasmOp(WasmOp.I32_SUB)
public operator fun minus(other: Int): Int = public operator fun minus(other: Int): Int =
implementedAsIntrinsic implementedAsIntrinsic
@@ -764,7 +819,7 @@ public class Int private constructor() : Number(), Comparable<Int> {
this * other.toInt() this * other.toInt()
/** Multiplies this value by the other value. */ /** Multiplies this value by the other value. */
@WasmInstruction(WasmInstruction.I32_MUL) @WasmOp(WasmOp.I32_MUL)
public operator fun times(other: Int): Int = public operator fun times(other: Int): Int =
implementedAsIntrinsic implementedAsIntrinsic
@@ -789,7 +844,7 @@ public class Int private constructor() : Number(), Comparable<Int> {
this / other.toInt() this / other.toInt()
/** Divides this value by the other value. */ /** Divides this value by the other value. */
@WasmInstruction(WasmInstruction.I32_DIV_S) @WasmOp(WasmOp.I32_DIV_S)
public operator fun div(other: Int): Int = public operator fun div(other: Int): Int =
implementedAsIntrinsic implementedAsIntrinsic
@@ -814,7 +869,7 @@ public class Int private constructor() : Number(), Comparable<Int> {
this % other.toInt() this % other.toInt()
/** Calculates the remainder of dividing this value by the other value. */ /** Calculates the remainder of dividing this value by the other value. */
@WasmInstruction(WasmInstruction.I32_REM_S) @WasmOp(WasmOp.I32_REM_S)
public operator fun rem(other: Int): Int = public operator fun rem(other: Int): Int =
implementedAsIntrinsic implementedAsIntrinsic
@@ -835,7 +890,8 @@ public class Int private constructor() : Number(), Comparable<Int> {
this + 1 this + 1
/** Decrements this value. */ /** Decrements this value. */
public inline operator fun dec(): Int = // TODO: Fix test compiler/testData/codegen/box/functions/invoke/invoke.kt with inline dec
public operator fun dec(): Int =
this - 1 this - 1
/** Returns this value. */ /** Returns this value. */
@@ -850,7 +906,7 @@ public class Int private constructor() : Number(), Comparable<Int> {
* Note that only the five lowest-order bits of the [bitCount] are used as the shift distance. * Note that only the five lowest-order bits of the [bitCount] are used as the shift distance.
* The shift distance actually used is therefore always in the range `0..31`. * The shift distance actually used is therefore always in the range `0..31`.
*/ */
@WasmInstruction(WasmInstruction.I32_SHL) @WasmOp(WasmOp.I32_SHL)
public infix fun shl(bitCount: Int): Int = public infix fun shl(bitCount: Int): Int =
implementedAsIntrinsic implementedAsIntrinsic
@@ -860,7 +916,7 @@ public class Int private constructor() : Number(), Comparable<Int> {
* Note that only the five lowest-order bits of the [bitCount] are used as the shift distance. * Note that only the five lowest-order bits of the [bitCount] are used as the shift distance.
* The shift distance actually used is therefore always in the range `0..31`. * The shift distance actually used is therefore always in the range `0..31`.
*/ */
@WasmInstruction(WasmInstruction.I32_SHR_S) @WasmOp(WasmOp.I32_SHR_S)
public infix fun shr(bitCount: Int): Int = public infix fun shr(bitCount: Int): Int =
implementedAsIntrinsic implementedAsIntrinsic
@@ -870,22 +926,22 @@ public class Int private constructor() : Number(), Comparable<Int> {
* Note that only the five lowest-order bits of the [bitCount] are used as the shift distance. * Note that only the five lowest-order bits of the [bitCount] are used as the shift distance.
* The shift distance actually used is therefore always in the range `0..31`. * The shift distance actually used is therefore always in the range `0..31`.
*/ */
@WasmInstruction(WasmInstruction.I32_SHR_U) @WasmOp(WasmOp.I32_SHR_U)
public infix fun ushr(bitCount: Int): Int = public infix fun ushr(bitCount: Int): Int =
implementedAsIntrinsic implementedAsIntrinsic
/** Performs a bitwise AND operation between the two values. */ /** Performs a bitwise AND operation between the two values. */
@WasmInstruction(WasmInstruction.I32_AND) @WasmOp(WasmOp.I32_AND)
public infix fun and(other: Int): Int = public infix fun and(other: Int): Int =
implementedAsIntrinsic implementedAsIntrinsic
/** Performs a bitwise OR operation between the two values. */ /** Performs a bitwise OR operation between the two values. */
@WasmInstruction(WasmInstruction.I32_OR) @WasmOp(WasmOp.I32_OR)
public infix fun or(other: Int): Int = public infix fun or(other: Int): Int =
implementedAsIntrinsic implementedAsIntrinsic
/** Performs a bitwise XOR operation between the two values. */ /** Performs a bitwise XOR operation between the two values. */
@WasmInstruction(WasmInstruction.I32_XOR) @WasmOp(WasmOp.I32_XOR)
public infix fun xor(other: Int): Int = public infix fun xor(other: Int): Int =
implementedAsIntrinsic implementedAsIntrinsic
@@ -893,22 +949,25 @@ public class Int private constructor() : Number(), Comparable<Int> {
public inline fun inv(): Int = public inline fun inv(): Int =
this.xor(-1) this.xor(-1)
// /** Creates a range from this value to the specified [other] value. */ /** Creates a range from this value to the specified [other] value. */
// public operator fun rangeTo(other: Byte): IntRange { public operator fun rangeTo(other: Byte): IntRange {
// return IntRange(this, other.toInt()) return IntRange(this, other.toInt())
// } }
// /** Creates a range from this value to the specified [other] value. */
// public operator fun rangeTo(other: Short): IntRange { /** Creates a range from this value to the specified [other] value. */
// return IntRange(this, other.toInt()) public operator fun rangeTo(other: Short): IntRange {
// } return IntRange(this, other.toInt())
// /** Creates a range from this value to the specified [other] value. */ }
// public operator fun rangeTo(other: Int): IntRange {
// return IntRange(this, other.toInt()) /** Creates a range from this value to the specified [other] value. */
// } public operator fun rangeTo(other: Int): IntRange {
// /** Creates a range from this value to the specified [other] value. */ return IntRange(this, other.toInt())
// public operator fun rangeTo(other: Long): LongRange { }
// return LongRange(this.toLong(), other.toLong())
// } /** Creates a range from this value to the specified [other] value. */
public operator fun rangeTo(other: Long): LongRange {
return LongRange(this.toLong(), other.toLong())
}
/** /**
* Converts this [Int] value to [Byte]. * Converts this [Int] value to [Byte].
@@ -955,9 +1014,8 @@ public class Int private constructor() : Number(), Comparable<Int> {
* The least significant 32 bits of the resulting `Long` value are the same as the bits of this `Int` value, * The least significant 32 bits of the resulting `Long` value are the same as the bits of this `Int` value,
* whereas the most significant 32 bits are filled with the sign bit of this value. * whereas the most significant 32 bits are filled with the sign bit of this value.
*/ */
@WasmInstruction(WasmInstruction.I64_EXTEND_I32_S)
public override fun toLong(): Long = public override fun toLong(): Long =
implementedAsIntrinsic wasm_i64_extend_i32_s(this)
/** /**
* Converts this [Int] value to [Float]. * Converts this [Int] value to [Float].
@@ -966,60 +1024,61 @@ public class Int private constructor() : Number(), Comparable<Int> {
* In case when this `Int` value is exactly between two `Float`s, * In case when this `Int` value is exactly between two `Float`s,
* the one with zero at least significant bit of mantissa is selected. * the one with zero at least significant bit of mantissa is selected.
*/ */
@WasmInstruction(WasmInstruction.F32_CONVERT_I32_S)
public override fun toFloat(): Float = public override fun toFloat(): Float =
implementedAsIntrinsic wasm_f32_convert_i32_s(this)
/** /**
* Converts this [Int] value to [Double]. * Converts this [Int] value to [Double].
* *
* The resulting `Double` value represents the same numerical value as this `Int`. * The resulting `Double` value represents the same numerical value as this `Int`.
*/ */
@WasmInstruction(WasmInstruction.F64_CONVERT_I32_S)
public override fun toDouble(): Double = public override fun toDouble(): Double =
implementedAsIntrinsic wasm_f64_convert_i32_s(this)
public inline fun equals(other: Int): Boolean = public inline fun equals(other: Int): Boolean =
wasm_i32_eq(this, other).reinterpretAsBoolean() wasm_i32_eq(this, other)
// TODO: Support Any? and type operators public override fun equals(other: Any?): Boolean =
// public override fun equals(other: Any?): Boolean = other is Int && wasm_i32_eq(this, other)
// other is Int && wasm_i32_eq(this, other).reinterpretAsBoolean()
// TODO: Implement Int.toString() public override fun toString(): String =
// public override fun toString(): String intToStringImpl(this)
public override inline fun hashCode(): Int = public override inline fun hashCode(): Int =
this this
@WasmInstruction(WasmInstruction.NOP) @WasmReinterpret
@PublishedApi @PublishedApi
internal fun reinterpretAsBoolean(): Boolean = internal fun reinterpretAsBoolean(): Boolean =
implementedAsIntrinsic implementedAsIntrinsic
@PublishedApi @PublishedApi
@WasmInstruction(WasmInstruction.NOP) @WasmReinterpret
internal fun reinterpretAsByte(): Byte = internal fun reinterpretAsByte(): Byte =
implementedAsIntrinsic implementedAsIntrinsic
@PublishedApi @PublishedApi
@WasmInstruction(WasmInstruction.NOP) @WasmReinterpret
internal fun reinterpretAsShort(): Short = internal fun reinterpretAsShort(): Short =
implementedAsIntrinsic implementedAsIntrinsic
@PublishedApi @PublishedApi
@WasmInstruction(WasmInstruction.NOP) @WasmReinterpret
internal fun reinterpretAsChar(): Char = internal fun reinterpretAsChar(): Char =
implementedAsIntrinsic implementedAsIntrinsic
} }
@WasmImport("runtime", "coerceToString")
private fun intToStringImpl(x: Int): String =
implementedAsIntrinsic
/** /**
* Represents a 64-bit signed integer. * Represents a 64-bit signed integer.
*/ */
public class Long private constructor() : Number(), Comparable<Long> { @WasmPrimitive
public class Long private constructor(val value: Long) : Number(), Comparable<Long> {
@ExcludedFromCodegen public companion object {
companion object {
/** /**
* A constant holding the minimum value an instance of Long can have. * A constant holding the minimum value an instance of Long can have.
*/ */
@@ -1104,7 +1163,7 @@ public class Long private constructor() : Number(), Comparable<Long> {
this + other.toLong() this + other.toLong()
/** Adds the other value to this value. */ /** Adds the other value to this value. */
@WasmInstruction(WasmInstruction.I64_ADD) @WasmOp(WasmOp.I64_ADD)
public operator fun plus(other: Long): Long = public operator fun plus(other: Long): Long =
implementedAsIntrinsic implementedAsIntrinsic
@@ -1129,7 +1188,7 @@ public class Long private constructor() : Number(), Comparable<Long> {
this - other.toLong() this - other.toLong()
/** Subtracts the other value from this value. */ /** Subtracts the other value from this value. */
@WasmInstruction(WasmInstruction.I64_SUB) @WasmOp(WasmOp.I64_SUB)
public operator fun minus(other: Long): Long = public operator fun minus(other: Long): Long =
implementedAsIntrinsic implementedAsIntrinsic
@@ -1154,7 +1213,7 @@ public class Long private constructor() : Number(), Comparable<Long> {
this * other.toLong() this * other.toLong()
/** Multiplies this value by the other value. */ /** Multiplies this value by the other value. */
@WasmInstruction(WasmInstruction.I64_MUL) @WasmOp(WasmOp.I64_MUL)
public operator fun times(other: Long): Long = public operator fun times(other: Long): Long =
implementedAsIntrinsic implementedAsIntrinsic
@@ -1179,7 +1238,7 @@ public class Long private constructor() : Number(), Comparable<Long> {
this / other.toLong() this / other.toLong()
/** Divides this value by the other value. */ /** Divides this value by the other value. */
@WasmInstruction(WasmInstruction.I64_DIV_S) @WasmOp(WasmOp.I64_DIV_S)
public operator fun div(other: Long): Long = public operator fun div(other: Long): Long =
implementedAsIntrinsic implementedAsIntrinsic
@@ -1204,7 +1263,7 @@ public class Long private constructor() : Number(), Comparable<Long> {
this % other.toLong() this % other.toLong()
/** Calculates the remainder of dividing this value by the other value. */ /** Calculates the remainder of dividing this value by the other value. */
@WasmInstruction(WasmInstruction.I64_REM_S) @WasmOp(WasmOp.I64_REM_S)
public operator fun rem(other: Long): Long = public operator fun rem(other: Long): Long =
implementedAsIntrinsic implementedAsIntrinsic
@@ -1231,22 +1290,25 @@ public class Long private constructor() : Number(), Comparable<Long> {
/** Returns the negative of this value. */ /** Returns the negative of this value. */
public inline operator fun unaryMinus(): Long = 0L - this public inline operator fun unaryMinus(): Long = 0L - this
// /** Creates a range from this value to the specified [other] value. */ /** Creates a range from this value to the specified [other] value. */
// public operator fun rangeTo(other: Byte): LongRange { public operator fun rangeTo(other: Byte): LongRange {
// return LongRange(this, other.toLong()) return LongRange(this, other.toLong())
// } }
// /** Creates a range from this value to the specified [other] value. */
// public operator fun rangeTo(other: Short): LongRange { /** Creates a range from this value to the specified [other] value. */
// return LongRange(this, other.toLong()) public operator fun rangeTo(other: Short): LongRange {
// } return LongRange(this, other.toLong())
// /** Creates a range from this value to the specified [other] value. */ }
// public operator fun rangeTo(other: Int): LongRange {
// return LongRange(this, other.toLong()) /** Creates a range from this value to the specified [other] value. */
// } public operator fun rangeTo(other: Int): LongRange {
// /** Creates a range from this value to the specified [other] value. */ return LongRange(this, other.toLong())
// public operator fun rangeTo(other: Long): LongRange { }
// return LongRange(this, other.toLong())
// } /** Creates a range from this value to the specified [other] value. */
public operator fun rangeTo(other: Long): LongRange {
return LongRange(this, other.toLong())
}
/** /**
* Shifts this value left by the [bitCount] number of bits. * Shifts this value left by the [bitCount] number of bits.
@@ -1276,17 +1338,17 @@ public class Long private constructor() : Number(), Comparable<Long> {
wasm_i64_shr_u(this, bitCount.toLong()) wasm_i64_shr_u(this, bitCount.toLong())
/** Performs a bitwise AND operation between the two values. */ /** Performs a bitwise AND operation between the two values. */
@WasmInstruction(WasmInstruction.I64_AND) @WasmOp(WasmOp.I64_AND)
public infix fun and(other: Long): Long = public infix fun and(other: Long): Long =
implementedAsIntrinsic implementedAsIntrinsic
/** Performs a bitwise OR operation between the two values. */ /** Performs a bitwise OR operation between the two values. */
@WasmInstruction(WasmInstruction.I64_OR) @WasmOp(WasmOp.I64_OR)
public infix fun or(other: Long): Long = public infix fun or(other: Long): Long =
implementedAsIntrinsic implementedAsIntrinsic
/** Performs a bitwise XOR operation between the two values. */ /** Performs a bitwise XOR operation between the two values. */
@WasmInstruction(WasmInstruction.I64_XOR) @WasmOp(WasmOp.I64_XOR)
public infix fun xor(other: Long): Long = public infix fun xor(other: Long): Long =
implementedAsIntrinsic implementedAsIntrinsic
@@ -1335,9 +1397,8 @@ public class Long private constructor() : Number(), Comparable<Long> {
* *
* The resulting `Int` value is represented by the least significant 32 bits of this `Long` value. * The resulting `Int` value is represented by the least significant 32 bits of this `Long` value.
*/ */
@WasmInstruction(WasmInstruction.I32_WRAP_I64)
public override fun toInt(): Int = public override fun toInt(): Int =
implementedAsIntrinsic wasm_i32_wrap_i64(this)
/** Returns this value. */ /** Returns this value. */
public override inline fun toLong(): Long = public override inline fun toLong(): Long =
@@ -1350,9 +1411,8 @@ public class Long private constructor() : Number(), Comparable<Long> {
* In case when this `Long` value is exactly between two `Float`s, * In case when this `Long` value is exactly between two `Float`s,
* the one with zero at least significant bit of mantissa is selected. * the one with zero at least significant bit of mantissa is selected.
*/ */
@WasmInstruction(WasmInstruction.F32_CONVERT_I64_S)
public override fun toFloat(): Float = public override fun toFloat(): Float =
implementedAsIntrinsic wasm_f32_convert_i64_s(this)
/** /**
* Converts this [Long] value to [Double]. * Converts this [Long] value to [Double].
@@ -1361,19 +1421,18 @@ public class Long private constructor() : Number(), Comparable<Long> {
* In case when this `Long` value is exactly between two `Double`s, * In case when this `Long` value is exactly between two `Double`s,
* the one with zero at least significant bit of mantissa is selected. * the one with zero at least significant bit of mantissa is selected.
*/ */
@WasmInstruction(WasmInstruction.F64_CONVERT_I64_S)
public override fun toDouble(): Double = public override fun toDouble(): Double =
implementedAsIntrinsic wasm_f64_convert_i64_s(this)
public inline fun equals(other: Long): Boolean = public inline fun equals(other: Long): Boolean =
wasm_i64_eq(this, other).reinterpretAsBoolean() wasm_i64_eq(this, other)
// TODO: Support Any? and type operators public override fun equals(other: Any?): Boolean =
// public override fun equals(other: Any?): Boolean = other is Long && wasm_i64_eq(this, other)
// other is Long && wasm_i64_eq(this, other).reinterpretAsBoolean()
// TODO: Implement Long.toString() // TODO: Implement proper Long.toString
// public override fun toString(): String public override fun toString(): String =
toDouble().toString()
public override fun hashCode(): Int = public override fun hashCode(): Int =
((this ushr 32) xor this).toInt() ((this ushr 32) xor this).toInt()
@@ -1382,10 +1441,10 @@ public class Long private constructor() : Number(), Comparable<Long> {
/** /**
* Represents a single-precision 32-bit IEEE 754 floating point number. * Represents a single-precision 32-bit IEEE 754 floating point number.
*/ */
public class Float private constructor() : Number(), Comparable<Float> { @WasmPrimitive
public class Float private constructor(public val value: Float) : Number(), Comparable<Float> {
@ExcludedFromCodegen public companion object {
companion object {
/** /**
* A constant holding the smallest *positive* nonzero value of Float. * A constant holding the smallest *positive* nonzero value of Float.
*/ */
@@ -1411,7 +1470,9 @@ public class Float private constructor() : Number(), Comparable<Float> {
/** /**
* A constant holding the "not a number" value of Float. * A constant holding the "not a number" value of Float.
*/ */
public val NaN: Float = wasm_f32_const_nan() public val NaN: Float
get() =
wasm_float_nan()
} }
/** /**
@@ -1483,7 +1544,7 @@ public class Float private constructor() : Number(), Comparable<Float> {
this + other.toFloat() this + other.toFloat()
/** Adds the other value to this value. */ /** Adds the other value to this value. */
@WasmInstruction(WasmInstruction.F32_ADD) @WasmOp(WasmOp.F32_ADD)
public operator fun plus(other: Float): Float = public operator fun plus(other: Float): Float =
implementedAsIntrinsic implementedAsIntrinsic
@@ -1508,7 +1569,7 @@ public class Float private constructor() : Number(), Comparable<Float> {
this - other.toFloat() this - other.toFloat()
/** Subtracts the other value from this value. */ /** Subtracts the other value from this value. */
@WasmInstruction(WasmInstruction.F32_SUB) @WasmOp(WasmOp.F32_SUB)
public operator fun minus(other: Float): Float = public operator fun minus(other: Float): Float =
implementedAsIntrinsic implementedAsIntrinsic
@@ -1533,7 +1594,7 @@ public class Float private constructor() : Number(), Comparable<Float> {
this * other.toFloat() this * other.toFloat()
/** Multiplies this value by the other value. */ /** Multiplies this value by the other value. */
@WasmInstruction(WasmInstruction.F32_MUL) @WasmOp(WasmOp.F32_MUL)
public operator fun times(other: Float): Float = public operator fun times(other: Float): Float =
implementedAsIntrinsic implementedAsIntrinsic
@@ -1558,7 +1619,7 @@ public class Float private constructor() : Number(), Comparable<Float> {
this / other.toFloat() this / other.toFloat()
/** Divides this value by the other value. */ /** Divides this value by the other value. */
@WasmInstruction(WasmInstruction.F32_DIV) @WasmOp(WasmOp.F32_DIV)
public operator fun div(other: Float): Float = public operator fun div(other: Float): Float =
implementedAsIntrinsic implementedAsIntrinsic
@@ -1602,7 +1663,7 @@ public class Float private constructor() : Number(), Comparable<Float> {
public inline operator fun unaryPlus(): Float = this public inline operator fun unaryPlus(): Float = this
/** Returns the negative of this value. */ /** Returns the negative of this value. */
@WasmInstruction(WasmInstruction.F32_NEG) @WasmOp(WasmOp.F32_NEG)
public operator fun unaryMinus(): Float = public operator fun unaryMinus(): Float =
implementedAsIntrinsic implementedAsIntrinsic
@@ -1634,10 +1695,8 @@ public class Float private constructor() : Number(), Comparable<Float> {
* Returns zero if this `Float` value is `NaN`, [Int.MIN_VALUE] if it's less than `Int.MIN_VALUE`, * Returns zero if this `Float` value is `NaN`, [Int.MIN_VALUE] if it's less than `Int.MIN_VALUE`,
* [Int.MAX_VALUE] if it's bigger than `Int.MAX_VALUE`. * [Int.MAX_VALUE] if it's bigger than `Int.MAX_VALUE`.
*/ */
// TODO: Implement Float.toInt()
public override fun toInt(): Int { public override fun toInt(): Int {
wasm_unreachable() return wasm_i32_trunc_sat_f32_s(this)
return 0
} }
/** /**
@@ -1647,10 +1706,8 @@ public class Float private constructor() : Number(), Comparable<Float> {
* Returns zero if this `Float` value is `NaN`, [Long.MIN_VALUE] if it's less than `Long.MIN_VALUE`, * Returns zero if this `Float` value is `NaN`, [Long.MIN_VALUE] if it's less than `Long.MIN_VALUE`,
* [Long.MAX_VALUE] if it's bigger than `Long.MAX_VALUE`. * [Long.MAX_VALUE] if it's bigger than `Long.MAX_VALUE`.
*/ */
// TODO: Implement Float.toLong()
public override fun toLong(): Long { public override fun toLong(): Long {
wasm_unreachable() return wasm_i64_trunc_sat_f32_s(this)
return 0
} }
/** Returns this value. */ /** Returns this value. */
@@ -1662,35 +1719,37 @@ public class Float private constructor() : Number(), Comparable<Float> {
* *
* The resulting `Double` value represents the same numerical value as this `Float`. * The resulting `Double` value represents the same numerical value as this `Float`.
*/ */
@WasmInstruction(WasmInstruction.F64_PROMOTE_F32)
public override fun toDouble(): Double = public override fun toDouble(): Double =
implementedAsIntrinsic wasm_f64_promote_f32(this)
public inline fun equals(other: Float): Boolean = public inline fun equals(other: Float): Boolean =
bits() == other.bits() bits() == other.bits()
// TODO: Support Any? and type operators public override fun equals(other: Any?): Boolean =
// public override fun equals(other: Any?): Boolean = other is Float && this.equals(other)
// other is Float && this.equals(other)
// TODO: Implement Float.toString() public override fun toString(): String =
// public override fun toString(): String floatToStringImpl(this)
public override inline fun hashCode(): Int = public override inline fun hashCode(): Int =
bits() bits()
@PublishedApi @PublishedApi
@WasmInstruction(WasmInstruction.I32_REINTERPRET_F32) @WasmOp(WasmOp.I32_REINTERPRET_F32)
internal fun bits(): Int = implementedAsIntrinsic internal fun bits(): Int = implementedAsIntrinsic
} }
@WasmImport("runtime", "coerceToString")
private fun floatToStringImpl(x: Float): String =
implementedAsIntrinsic
/** /**
* Represents a double-precision 64-bit IEEE 754 floating point number. * Represents a double-precision 64-bit IEEE 754 floating point number.
*/ */
public class Double private constructor() : Number(), Comparable<Double> { @WasmPrimitive
public class Double private constructor(public val value: Double) : Number(), Comparable<Double> {
@ExcludedFromCodegen public companion object {
companion object {
/** /**
* A constant holding the smallest *positive* nonzero value of Double. * A constant holding the smallest *positive* nonzero value of Double.
*/ */
@@ -1716,7 +1775,10 @@ public class Double private constructor() : Number(), Comparable<Double> {
/** /**
* A constant holding the "not a number" value of Double. * A constant holding the "not a number" value of Double.
*/ */
public val NaN: Double = wasm_f64_const_nan() public val NaN: Double
get() =
wasm_double_nan()
} }
/** /**
@@ -1792,7 +1854,7 @@ public class Double private constructor() : Number(), Comparable<Double> {
this + other.toDouble() this + other.toDouble()
/** Adds the other value to this value. */ /** Adds the other value to this value. */
@WasmInstruction(WasmInstruction.F64_ADD) @WasmOp(WasmOp.F64_ADD)
public operator fun plus(other: Double): Double = public operator fun plus(other: Double): Double =
implementedAsIntrinsic implementedAsIntrinsic
@@ -1817,7 +1879,7 @@ public class Double private constructor() : Number(), Comparable<Double> {
this - other.toDouble() this - other.toDouble()
/** Subtracts the other value from this value. */ /** Subtracts the other value from this value. */
@WasmInstruction(WasmInstruction.F64_SUB) @WasmOp(WasmOp.F64_SUB)
public operator fun minus(other: Double): Double = public operator fun minus(other: Double): Double =
implementedAsIntrinsic implementedAsIntrinsic
@@ -1842,7 +1904,7 @@ public class Double private constructor() : Number(), Comparable<Double> {
this * other.toDouble() this * other.toDouble()
/** Multiplies this value by the other value. */ /** Multiplies this value by the other value. */
@WasmInstruction(WasmInstruction.F64_MUL) @WasmOp(WasmOp.F64_MUL)
public operator fun times(other: Double): Double = public operator fun times(other: Double): Double =
implementedAsIntrinsic implementedAsIntrinsic
@@ -1867,7 +1929,7 @@ public class Double private constructor() : Number(), Comparable<Double> {
this / other.toDouble() this / other.toDouble()
/** Divides this value by the other value. */ /** Divides this value by the other value. */
@WasmInstruction(WasmInstruction.F64_DIV) @WasmOp(WasmOp.F64_DIV)
public operator fun div(other: Double): Double = public operator fun div(other: Double): Double =
implementedAsIntrinsic implementedAsIntrinsic
@@ -1908,7 +1970,7 @@ public class Double private constructor() : Number(), Comparable<Double> {
this this
/** Returns the negative of this value. */ /** Returns the negative of this value. */
@WasmInstruction(WasmInstruction.F64_NEG) @WasmOp(WasmOp.F64_NEG)
public operator fun unaryMinus(): Double = public operator fun unaryMinus(): Double =
implementedAsIntrinsic implementedAsIntrinsic
@@ -1940,10 +2002,8 @@ public class Double private constructor() : Number(), Comparable<Double> {
* Returns zero if this `Double` value is `NaN`, [Int.MIN_VALUE] if it's less than `Int.MIN_VALUE`, * Returns zero if this `Double` value is `NaN`, [Int.MIN_VALUE] if it's less than `Int.MIN_VALUE`,
* [Int.MAX_VALUE] if it's bigger than `Int.MAX_VALUE`. * [Int.MAX_VALUE] if it's bigger than `Int.MAX_VALUE`.
*/ */
// TODO: Implement Double.toInt()
public override fun toInt(): Int { public override fun toInt(): Int {
wasm_unreachable() return wasm_i32_trunc_sat_f64_s(this)
return 0
} }
/** /**
@@ -1953,10 +2013,8 @@ public class Double private constructor() : Number(), Comparable<Double> {
* Returns zero if this `Double` value is `NaN`, [Long.MIN_VALUE] if it's less than `Long.MIN_VALUE`, * Returns zero if this `Double` value is `NaN`, [Long.MIN_VALUE] if it's less than `Long.MIN_VALUE`,
* [Long.MAX_VALUE] if it's bigger than `Long.MAX_VALUE`. * [Long.MAX_VALUE] if it's bigger than `Long.MAX_VALUE`.
*/ */
// TODO: Implement Double.toLong()
public override fun toLong(): Long { public override fun toLong(): Long {
wasm_unreachable() return wasm_i64_trunc_sat_f64_s(this)
return 0
} }
/** /**
@@ -1966,9 +2024,8 @@ public class Double private constructor() : Number(), Comparable<Double> {
* In case when this `Double` value is exactly between two `Float`s, * In case when this `Double` value is exactly between two `Float`s,
* the one with zero at least significant bit of mantissa is selected. * the one with zero at least significant bit of mantissa is selected.
*/ */
@WasmInstruction(WasmInstruction.F32_DEMOTE_F64)
public override fun toFloat(): Float = public override fun toFloat(): Float =
implementedAsIntrinsic wasm_f32_demote_f64(this)
/** Returns this value. */ /** Returns this value. */
public override inline fun toDouble(): Double = public override inline fun toDouble(): Double =
@@ -1977,17 +2034,20 @@ public class Double private constructor() : Number(), Comparable<Double> {
public inline fun equals(other: Double): Boolean = public inline fun equals(other: Double): Boolean =
this.bits() == other.bits() this.bits() == other.bits()
// TODO: Support Any? and type operators public override fun equals(other: Any?): Boolean =
// public override fun equals(other: Any?): Boolean = other is Double && this.bits() == other.bits()
// other is Double && this.bits() == other.bits()
// TODO: Implement Double.toString() public override fun toString(): String =
// public override fun toString(): String doubleToStringImpl(this)
public override inline fun hashCode(): Int = bits().hashCode() public override inline fun hashCode(): Int = bits().hashCode()
@PublishedApi @PublishedApi
@WasmInstruction(WasmInstruction.I64_REINTERPRET_F64) @WasmOp(WasmOp.I64_REINTERPRET_F64)
internal fun bits(): Long = internal fun bits(): Long =
implementedAsIntrinsic implementedAsIntrinsic
} }
@WasmImport("runtime", "coerceToString")
private fun doubleToStringImpl(x: Double): String =
implementedAsIntrinsic
@@ -0,0 +1,72 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin
import kotlin.wasm.internal.*
/**
* The `String` class represents character strings. All string literals in Kotlin programs, such as `"abc"`, are
* implemented as instances of this class.
*/
@WasmPrimitive
public class String constructor(public val string: String) : Comparable<String>, CharSequence {
public companion object;
/**
* Returns a string obtained by concatenating this string with the string representation of the given [other] object.
*/
public operator fun plus(other: Any?): String =
stringPlusImpl(this, other.toString())
public override val length: Int
get() = stringLengthImpl(this)
/**
* Returns the character of this string at the specified [index].
*
* If the [index] is out of bounds of this string, throws an [IndexOutOfBoundsException] except in Kotlin/JS
* where the behavior is unspecified.
*/
public override fun get(index: Int): Char =
stringGetCharImpl(this, index)
public override fun subSequence(startIndex: Int, endIndex: Int): CharSequence =
stringSubSequenceImpl(this, startIndex, endIndex)
public override fun compareTo(other: String): Int =
stringCompareToImpl(this, other)
public override fun equals(other: Any?): Boolean {
if (other is String)
return this.compareTo(other) == 0
return false
}
public override fun toString(): String = this
// TODO: Implement
public override fun hashCode(): Int = 10
}
@WasmImport("runtime", "String_plus")
private fun stringPlusImpl(it: String, other: String): String =
implementedAsIntrinsic
@WasmImport("runtime", "String_getLength")
private fun stringLengthImpl(it: String): Int =
implementedAsIntrinsic
@WasmImport("runtime", "String_getChar")
private fun stringGetCharImpl(it: String, index: Int): Char =
implementedAsIntrinsic
@WasmImport("runtime", "String_compareTo")
private fun stringCompareToImpl(it: String, other: String): Int =
implementedAsIntrinsic
@WasmImport("runtime", "String_subsequence")
private fun stringSubSequenceImpl(string: String, startIndex: Int, endIndex: Int): String =
implementedAsIntrinsic
@@ -1,13 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin
/**
* Base interface implicitly implemented by all annotation interfaces.
* See [Kotlin language documentation](https://kotlinlang.org/docs/reference/annotations.html) for more information
* on annotations.
*/
public interface Annotation
@@ -1,313 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
// Auto-generated file. DO NOT EDIT!
@file:Suppress(
"NON_ABSTRACT_FUNCTION_WITH_NO_BODY",
"MUST_BE_INITIALIZED_OR_BE_ABSTRACT",
"EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE",
"PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED",
"WRONG_MODIFIER_TARGET"
)
package kotlin
/**
* An array of bytes. When targeting the JVM, instances of this class are represented as `byte[]`.
* @constructor Creates a new array of the specified [size], with all elements initialized to zero.
*/
public class ByteArray(size: Int) {
/**
* Creates a new array of the specified [size], where each element is calculated by calling the specified
* [init] function.
*
* The function [init] is called for each array element sequentially starting from the first one.
* It should return the value for an array element given its index.
*/
public inline constructor(size: Int, init: (Int) -> Byte)
/**
* Returns the array element at the given [index]. This method can be called using the index operator.
*
* If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException] except in Kotlin/JS
* where the behavior is unspecified.
*/
public operator fun get(index: Int): Byte
/**
* Sets the element at the given [index] to the given [value]. This method can be called using the index operator.
*
* If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException] except in Kotlin/JS
* where the behavior is unspecified.
*/
public operator fun set(index: Int, value: Byte): Unit
/** Returns the number of elements in the array. */
public val size: Int
/** Creates an iterator over the elements of the array. */
public operator fun iterator(): ByteIterator
}
/**
* An array of chars. When targeting the JVM, instances of this class are represented as `char[]`.
* @constructor Creates a new array of the specified [size], with all elements initialized to null char (`\u0000').
*/
public class CharArray(size: Int) {
/**
* Creates a new array of the specified [size], where each element is calculated by calling the specified
* [init] function.
*
* The function [init] is called for each array element sequentially starting from the first one.
* It should return the value for an array element given its index.
*/
public inline constructor(size: Int, init: (Int) -> Char)
/**
* Returns the array element at the given [index]. This method can be called using the index operator.
*
* If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException] except in Kotlin/JS
* where the behavior is unspecified.
*/
public operator fun get(index: Int): Char
/**
* Sets the element at the given [index] to the given [value]. This method can be called using the index operator.
*
* If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException] except in Kotlin/JS
* where the behavior is unspecified.
*/
public operator fun set(index: Int, value: Char): Unit
/** Returns the number of elements in the array. */
public val size: Int
/** Creates an iterator over the elements of the array. */
public operator fun iterator(): CharIterator
}
/**
* An array of shorts. When targeting the JVM, instances of this class are represented as `short[]`.
* @constructor Creates a new array of the specified [size], with all elements initialized to zero.
*/
public class ShortArray(size: Int) {
/**
* Creates a new array of the specified [size], where each element is calculated by calling the specified
* [init] function.
*
* The function [init] is called for each array element sequentially starting from the first one.
* It should return the value for an array element given its index.
*/
public inline constructor(size: Int, init: (Int) -> Short)
/**
* Returns the array element at the given [index]. This method can be called using the index operator.
*
* If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException] except in Kotlin/JS
* where the behavior is unspecified.
*/
public operator fun get(index: Int): Short
/**
* Sets the element at the given [index] to the given [value]. This method can be called using the index operator.
*
* If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException] except in Kotlin/JS
* where the behavior is unspecified.
*/
public operator fun set(index: Int, value: Short): Unit
/** Returns the number of elements in the array. */
public val size: Int
/** Creates an iterator over the elements of the array. */
public operator fun iterator(): ShortIterator
}
/**
* An array of ints. When targeting the JVM, instances of this class are represented as `int[]`.
* @constructor Creates a new array of the specified [size], with all elements initialized to zero.
*/
public class IntArray(size: Int) {
/**
* Creates a new array of the specified [size], where each element is calculated by calling the specified
* [init] function.
*
* The function [init] is called for each array element sequentially starting from the first one.
* It should return the value for an array element given its index.
*/
public inline constructor(size: Int, init: (Int) -> Int)
/**
* Returns the array element at the given [index]. This method can be called using the index operator.
*
* If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException] except in Kotlin/JS
* where the behavior is unspecified.
*/
public operator fun get(index: Int): Int
/**
* Sets the element at the given [index] to the given [value]. This method can be called using the index operator.
*
* If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException] except in Kotlin/JS
* where the behavior is unspecified.
*/
public operator fun set(index: Int, value: Int): Unit
/** Returns the number of elements in the array. */
public val size: Int
/** Creates an iterator over the elements of the array. */
public operator fun iterator(): IntIterator
}
/**
* An array of longs. When targeting the JVM, instances of this class are represented as `long[]`.
* @constructor Creates a new array of the specified [size], with all elements initialized to zero.
*/
public class LongArray(size: Int) {
/**
* Creates a new array of the specified [size], where each element is calculated by calling the specified
* [init] function.
*
* The function [init] is called for each array element sequentially starting from the first one.
* It should return the value for an array element given its index.
*/
public inline constructor(size: Int, init: (Int) -> Long)
/**
* Returns the array element at the given [index]. This method can be called using the index operator.
*
* If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException] except in Kotlin/JS
* where the behavior is unspecified.
*/
public operator fun get(index: Int): Long
/**
* Sets the element at the given [index] to the given [value]. This method can be called using the index operator.
*
* If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException] except in Kotlin/JS
* where the behavior is unspecified.
*/
public operator fun set(index: Int, value: Long): Unit
/** Returns the number of elements in the array. */
public val size: Int
/** Creates an iterator over the elements of the array. */
public operator fun iterator(): LongIterator
}
/**
* An array of floats. When targeting the JVM, instances of this class are represented as `float[]`.
* @constructor Creates a new array of the specified [size], with all elements initialized to zero.
*/
public class FloatArray(size: Int) {
/**
* Creates a new array of the specified [size], where each element is calculated by calling the specified
* [init] function.
*
* The function [init] is called for each array element sequentially starting from the first one.
* It should return the value for an array element given its index.
*/
public inline constructor(size: Int, init: (Int) -> Float)
/**
* Returns the array element at the given [index]. This method can be called using the index operator.
*
* If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException] except in Kotlin/JS
* where the behavior is unspecified.
*/
public operator fun get(index: Int): Float
/**
* Sets the element at the given [index] to the given [value]. This method can be called using the index operator.
*
* If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException] except in Kotlin/JS
* where the behavior is unspecified.
*/
public operator fun set(index: Int, value: Float): Unit
/** Returns the number of elements in the array. */
public val size: Int
/** Creates an iterator over the elements of the array. */
public operator fun iterator(): FloatIterator
}
/**
* An array of doubles. When targeting the JVM, instances of this class are represented as `double[]`.
* @constructor Creates a new array of the specified [size], with all elements initialized to zero.
*/
public class DoubleArray(size: Int) {
/**
* Creates a new array of the specified [size], where each element is calculated by calling the specified
* [init] function.
*
* The function [init] is called for each array element sequentially starting from the first one.
* It should return the value for an array element given its index.
*/
public inline constructor(size: Int, init: (Int) -> Double)
/**
* Returns the array element at the given [index]. This method can be called using the index operator.
*
* If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException] except in Kotlin/JS
* where the behavior is unspecified.
*/
public operator fun get(index: Int): Double
/**
* Sets the element at the given [index] to the given [value]. This method can be called using the index operator.
*
* If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException] except in Kotlin/JS
* where the behavior is unspecified.
*/
public operator fun set(index: Int, value: Double): Unit
/** Returns the number of elements in the array. */
public val size: Int
/** Creates an iterator over the elements of the array. */
public operator fun iterator(): DoubleIterator
}
/**
* An array of booleans. When targeting the JVM, instances of this class are represented as `boolean[]`.
* @constructor Creates a new array of the specified [size], with all elements initialized to `false`.
*/
public class BooleanArray(size: Int) {
/**
* Creates a new array of the specified [size], where each element is calculated by calling the specified
* [init] function.
*
* The function [init] is called for each array element sequentially starting from the first one.
* It should return the value for an array element given its index.
*/
public inline constructor(size: Int, init: (Int) -> Boolean)
/**
* Returns the array element at the given [index]. This method can be called using the index operator.
*
* If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException] except in Kotlin/JS
* where the behavior is unspecified.
*/
public operator fun get(index: Int): Boolean
/**
* Sets the element at the given [index] to the given [value]. This method can be called using the index operator.
*
* If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException] except in Kotlin/JS
* where the behavior is unspecified.
*/
public operator fun set(index: Int, value: Boolean): Unit
/** Returns the number of elements in the array. */
public val size: Int
/** Creates an iterator over the elements of the array. */
public operator fun iterator(): BooleanIterator
}
@@ -1,46 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kotlin
/**
* Represents a readable sequence of [Char] values.
*/
public interface CharSequence {
/**
* Returns the length of this character sequence.
*/
public val length: Int
/**
* Returns the character at the specified [index] in this character sequence.
*
* @throws [IndexOutOfBoundsException] if the [index] is out of bounds of this character sequence.
*
* Note that the [String] implementation of this interface in Kotlin/JS has unspecified behavior
* if the [index] is out of its bounds.
*/
public operator fun get(index: Int): Char
/**
* Returns a new character sequence that is a subsequence of this character sequence,
* starting at the specified [startIndex] and ending right before the specified [endIndex].
*
* @param startIndex the start index (inclusive).
* @param endIndex the end index (exclusive).
*/
public fun subSequence(startIndex: Int, endIndex: Int): CharSequence
}
@@ -1,438 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin.collections
import kotlin.internal.PlatformDependent
/**
* Classes that inherit from this interface can be represented as a sequence of elements that can
* be iterated over.
* @param T the type of element being iterated over. The iterator is covariant in its element type.
*/
public interface Iterable<out T> {
/**
* Returns an iterator over the elements of this object.
*/
public operator fun iterator(): Iterator<T>
}
/**
* Classes that inherit from this interface can be represented as a sequence of elements that can
* be iterated over and that supports removing elements during iteration.
* @param T the type of element being iterated over. The mutable iterator is invariant in its element type.
*/
public interface MutableIterable<out T> : Iterable<T> {
/**
* Returns an iterator over the elements of this sequence that supports removing elements during iteration.
*/
override fun iterator(): MutableIterator<T>
}
/**
* A generic collection of elements. Methods in this interface support only read-only access to the collection;
* read/write access is supported through the [MutableCollection] interface.
* @param E the type of elements contained in the collection. The collection is covariant in its element type.
*/
public interface Collection<out E> : Iterable<E> {
// Query Operations
/**
* Returns the size of the collection.
*/
public val size: Int
/**
* Returns `true` if the collection is empty (contains no elements), `false` otherwise.
*/
public fun isEmpty(): Boolean
/**
* Checks if the specified element is contained in this collection.
*/
public operator fun contains(element: @UnsafeVariance E): Boolean
override fun iterator(): Iterator<E>
// Bulk Operations
/**
* Checks if all elements in the specified collection are contained in this collection.
*/
public fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean
}
/**
* A generic collection of elements that supports adding and removing elements.
*
* @param E the type of elements contained in the collection. The mutable collection is invariant in its element type.
*/
public interface MutableCollection<E> : Collection<E>, MutableIterable<E> {
// Query Operations
override fun iterator(): MutableIterator<E>
// Modification Operations
/**
* Adds the specified element to the collection.
*
* @return `true` if the element has been added, `false` if the collection does not support duplicates
* and the element is already contained in the collection.
*/
public fun add(element: E): Boolean
/**
* Removes a single instance of the specified element from this
* collection, if it is present.
*
* @return `true` if the element has been successfully removed; `false` if it was not present in the collection.
*/
public fun remove(element: E): Boolean
// Bulk Modification Operations
/**
* Adds all of the elements of the specified collection to this collection.
*
* @return `true` if any of the specified elements was added to the collection, `false` if the collection was not modified.
*/
public fun addAll(elements: Collection<E>): Boolean
/**
* Removes all of this collection's elements that are also contained in the specified collection.
*
* @return `true` if any of the specified elements was removed from the collection, `false` if the collection was not modified.
*/
public fun removeAll(elements: Collection<E>): Boolean
/**
* Retains only the elements in this collection that are contained in the specified collection.
*
* @return `true` if any element was removed from the collection, `false` if the collection was not modified.
*/
public fun retainAll(elements: Collection<E>): Boolean
/**
* Removes all elements from this collection.
*/
public fun clear(): Unit
}
/**
* A generic ordered collection of elements. Methods in this interface support only read-only access to the list;
* read/write access is supported through the [MutableList] interface.
* @param E the type of elements contained in the list. The list is covariant in its element type.
*/
public interface List<out E> : Collection<E> {
// Query Operations
override val size: Int
override fun isEmpty(): Boolean
override fun contains(element: @UnsafeVariance E): Boolean
override fun iterator(): Iterator<E>
// Bulk Operations
override fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean
// Positional Access Operations
/**
* Returns the element at the specified index in the list.
*/
public operator fun get(index: Int): E
// Search Operations
/**
* Returns the index of the first occurrence of the specified element in the list, or -1 if the specified
* element is not contained in the list.
*/
public fun indexOf(element: @UnsafeVariance E): Int
/**
* Returns the index of the last occurrence of the specified element in the list, or -1 if the specified
* element is not contained in the list.
*/
public fun lastIndexOf(element: @UnsafeVariance E): Int
// List Iterators
/**
* Returns a list iterator over the elements in this list (in proper sequence).
*/
public fun listIterator(): ListIterator<E>
/**
* Returns a list iterator over the elements in this list (in proper sequence), starting at the specified [index].
*/
public fun listIterator(index: Int): ListIterator<E>
// View
/**
* Returns a view of the portion of this list between the specified [fromIndex] (inclusive) and [toIndex] (exclusive).
* The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa.
*
* Structural changes in the base list make the behavior of the view undefined.
*/
public fun subList(fromIndex: Int, toIndex: Int): List<E>
}
/**
* A generic ordered collection of elements that supports adding and removing elements.
* @param E the type of elements contained in the list. The mutable list is invariant in its element type.
*/
public interface MutableList<E> : List<E>, MutableCollection<E> {
// Modification Operations
/**
* Adds the specified element to the end of this list.
*
* @return `true` because the list is always modified as the result of this operation.
*/
override fun add(element: E): Boolean
override fun remove(element: E): Boolean
// Bulk Modification Operations
/**
* Adds all of the elements of the specified collection to the end of this list.
*
* The elements are appended in the order they appear in the [elements] collection.
*
* @return `true` if the list was changed as the result of the operation.
*/
override fun addAll(elements: Collection<E>): Boolean
/**
* Inserts all of the elements of the specified collection [elements] into this list at the specified [index].
*
* @return `true` if the list was changed as the result of the operation.
*/
public fun addAll(index: Int, elements: Collection<E>): Boolean
override fun removeAll(elements: Collection<E>): Boolean
override fun retainAll(elements: Collection<E>): Boolean
override fun clear(): Unit
// Positional Access Operations
/**
* Replaces the element at the specified position in this list with the specified element.
*
* @return the element previously at the specified position.
*/
public operator fun set(index: Int, element: E): E
/**
* Inserts an element into the list at the specified [index].
*/
public fun add(index: Int, element: E): Unit
/**
* Removes an element at the specified [index] from the list.
*
* @return the element that has been removed.
*/
public fun removeAt(index: Int): E
// List Iterators
override fun listIterator(): MutableListIterator<E>
override fun listIterator(index: Int): MutableListIterator<E>
// View
override fun subList(fromIndex: Int, toIndex: Int): MutableList<E>
}
/**
* A generic unordered collection of elements that does not support duplicate elements.
* Methods in this interface support only read-only access to the set;
* read/write access is supported through the [MutableSet] interface.
* @param E the type of elements contained in the set. The set is covariant in its element type.
*/
public interface Set<out E> : Collection<E> {
// Query Operations
override val size: Int
override fun isEmpty(): Boolean
override fun contains(element: @UnsafeVariance E): Boolean
override fun iterator(): Iterator<E>
// Bulk Operations
override fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean
}
/**
* A generic unordered collection of elements that does not support duplicate elements, and supports
* adding and removing elements.
* @param E the type of elements contained in the set. The mutable set is invariant in its element type.
*/
public interface MutableSet<E> : Set<E>, MutableCollection<E> {
// Query Operations
override fun iterator(): MutableIterator<E>
// Modification Operations
/**
* Adds the specified element to the set.
*
* @return `true` if the element has been added, `false` if the element is already contained in the set.
*/
override fun add(element: E): Boolean
override fun remove(element: E): Boolean
// Bulk Modification Operations
override fun addAll(elements: Collection<E>): Boolean
override fun removeAll(elements: Collection<E>): Boolean
override fun retainAll(elements: Collection<E>): Boolean
override fun clear(): Unit
}
/**
* A collection that holds pairs of objects (keys and values) and supports efficiently retrieving
* the value corresponding to each key. Map keys are unique; the map holds only one value for each key.
* Methods in this interface support only read-only access to the map; read-write access is supported through
* the [MutableMap] interface.
* @param K the type of map keys. The map is invariant in its key type, as it
* can accept key as a parameter (of [containsKey] for example) and return it in [keys] set.
* @param V the type of map values. The map is covariant in its value type.
*/
public interface Map<K, out V> {
// Query Operations
/**
* Returns the number of key/value pairs in the map.
*/
public val size: Int
/**
* Returns `true` if the map is empty (contains no elements), `false` otherwise.
*/
public fun isEmpty(): Boolean
/**
* Returns `true` if the map contains the specified [key].
*/
public fun containsKey(key: K): Boolean
/**
* Returns `true` if the map maps one or more keys to the specified [value].
*/
public fun containsValue(value: @UnsafeVariance V): Boolean
/**
* Returns the value corresponding to the given [key], or `null` if such a key is not present in the map.
*/
public operator fun get(key: K): V?
/**
* Returns the value corresponding to the given [key], or [defaultValue] if such a key is not present in the map.
*
* @since JDK 1.8
*/
@SinceKotlin("1.1")
@PlatformDependent
public fun getOrDefault(key: K, defaultValue: @UnsafeVariance V): V {
// See default implementation in JDK sources
return null as V
}
// Views
/**
* Returns a read-only [Set] of all keys in this map.
*/
public val keys: Set<K>
/**
* Returns a read-only [Collection] of all values in this map. Note that this collection may contain duplicate values.
*/
public val values: Collection<V>
/**
* Returns a read-only [Set] of all key/value pairs in this map.
*/
public val entries: Set<Map.Entry<K, V>>
/**
* Represents a key/value pair held by a [Map].
*/
public interface Entry<out K, out V> {
/**
* Returns the key of this key/value pair.
*/
public val key: K
/**
* Returns the value of this key/value pair.
*/
public val value: V
}
}
/**
* A modifiable collection that holds pairs of objects (keys and values) and supports efficiently retrieving
* the value corresponding to each key. Map keys are unique; the map holds only one value for each key.
* @param K the type of map keys. The map is invariant in its key type.
* @param V the type of map values. The mutable map is invariant in its value type.
*/
public interface MutableMap<K, V> : Map<K, V> {
// Modification Operations
/**
* Associates the specified [value] with the specified [key] in the map.
*
* @return the previous value associated with the key, or `null` if the key was not present in the map.
*/
public fun put(key: K, value: V): V?
/**
* Removes the specified key and its corresponding value from this map.
*
* @return the previous value associated with the key, or `null` if the key was not present in the map.
*/
public fun remove(key: K): V?
/**
* Removes the entry for the specified key only if it is mapped to the specified value.
*
* @return true if entry was removed
*/
@SinceKotlin("1.1")
@PlatformDependent
public fun remove(key: K, value: V): Boolean {
// See default implementation in JDK sources
return true
}
// Bulk Modification Operations
/**
* Updates this map with key/value pairs from the specified map [from].
*/
public fun putAll(from: Map<out K, V>): Unit
/**
* Removes all elements from this map.
*/
public fun clear(): Unit
// Views
/**
* Returns a [MutableSet] of all keys in this map.
*/
override val keys: MutableSet<K>
/**
* Returns a [MutableCollection] of all values in this map. Note that this collection may contain duplicate values.
*/
override val values: MutableCollection<V>
/**
* Returns a [MutableSet] of all key/value pairs in this map.
*/
override val entries: MutableSet<MutableMap.MutableEntry<K, V>>
/**
* Represents a key/value pair held by a [MutableMap].
*/
public interface MutableEntry<K, V> : Map.Entry<K, V> {
/**
* Changes the value associated with the key of this entry.
*
* @return the previous value corresponding to the key.
*/
public fun setValue(newValue: V): V
}
}
@@ -1,29 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kotlin
/**
* Classes which inherit from this interface have a defined total ordering between their instances.
*/
public interface Comparable<in T> {
/**
* Compares this object with the specified object for order. Returns zero if this object is equal
* to the specified [other] object, a negative number if it's less than [other], or a positive number
* if it's greater than [other].
*/
public operator fun compareTo(other: T): Int
}

Some files were not shown because too many files have changed in this diff Show More