Compare commits

...

9 Commits

Author SHA1 Message Date
Svyatoslav Kuzmich
5c245b7fb9 [Wasm] backend update 2020-06-16 18:28:35 +03:00
Svyatoslav Kuzmich
fa4f892467 Fix types in JsSharedVariablesManager.kt 2020-05-16 20:40:21 +03:00
Svyatoslav Kuzmich
4e4782b927 [IR] Use IR instead of descriptors in annotation helpers 2020-05-16 20:40:21 +03:00
Svyatoslav Kuzmich
06ef7db0d1 [Wasm] Add GenerateJsIrKlib.kt (WORKAROUNDS) 2020-05-16 20:40:21 +03:00
Svyatoslav Kuzmich
02690c0d14 [Wasm] Update stdlib 2020-05-16 20:40:21 +03:00
Svyatoslav Kuzmich
ac1ef2e556 [Wasm] Add some simple wasmBox tests 2020-05-16 20:40:20 +03:00
Svyatoslav Kuzmich
ee5d1dae54 [Wasm] Update stdlib 2020-05-16 20:40:20 +03:00
Svyatoslav Kuzmich
7f66924043 [Wasm] Update tests 2020-05-16 20:40:20 +03:00
Svyatoslav Kuzmich
71f10440a5 Move isInlineClass utils to JS compiler module
Major backend update

Features:
- Non-constant property initializers
- if/when expressions
- Loops
- String concatenation
- Classes:
 - Constructors, instantiation
 - Field access
 - Virtual method calls
 - Interface method calls
 - is-checks, casts for classes and interfaces

Stdlib:
 - Missing String and Boolean methods
 - Basic println
 - assertEquals stubs for tests

Refactor:
 - Wasm AST generator
 - Wasm AST -> WAT convertor
2020-05-16 19:23:08 +03:00
1273 changed files with 31228 additions and 5321 deletions

7
.idea/dictionaries/jetbrains.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<component name="ProjectDictionaryState">
<dictionary name="jetbrains">
<words>
<w>vtable</w>
</words>
</dictionary>
</component>

View File

@@ -12,6 +12,7 @@ import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiManager
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.messages.*
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
@@ -23,6 +24,7 @@ import org.jetbrains.kotlin.resolve.multiplatform.isCommonSource
import org.jetbrains.kotlin.serialization.js.ModuleKind
import org.jetbrains.kotlin.util.Logger
import java.io.File
import kotlin.system.exitProcess
fun buildConfiguration(environment: KotlinCoreEnvironment, moduleName: String): CompilerConfiguration {
val runtimeConfiguration = environment.configuration.copy()
@@ -30,7 +32,10 @@ fun buildConfiguration(environment: KotlinCoreEnvironment, moduleName: String):
runtimeConfiguration.put(JSConfigurationKeys.MODULE_KIND, ModuleKind.PLAIN)
runtimeConfiguration.put(
CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY,
PrintingMessageCollector(System.err, MessageRenderer.PLAIN_RELATIVE_PATHS, false)
GroupingMessageCollector(
PrintingMessageCollector(System.err, MessageRenderer.GRADLE_STYLE, false),
false
)
)
runtimeConfiguration.languageVersionSettings = LanguageVersionSettingsImpl(
@@ -74,22 +79,32 @@ fun buildKLib(
commonSources: List<String>
) {
val configuration = buildConfiguration(environment, moduleName)
generateKLib(
project = environment.project,
files = sources.map { source ->
val file = createPsiFile(source)
if (source in commonSources) {
file.isCommonSource = true
}
file
},
analyzer = AnalyzerWithCompilerReport(configuration),
configuration = configuration,
allDependencies = allDependencies,
friendDependencies = emptyList(),
outputKlibPath = outputPath,
nopack = true
)
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)!!
try {
generateKLib(
project = environment.project,
files = sources.map { source ->
val file = createPsiFile(source)
if (source in commonSources) {
file.isCommonSource = true
}
file
},
analyzer = AnalyzerWithCompilerReport(configuration),
configuration = configuration,
allDependencies = allDependencies,
friendDependencies = emptyList(),
outputKlibPath = outputPath,
nopack = true
)
} catch (e: JsIrCompilationError) {
if (messageCollector.hasErrors()) {
(messageCollector as GroupingMessageCollector).flush()
exitProcess(ExitCode.COMPILATION_ERROR.code)
}
} finally {
(messageCollector as GroupingMessageCollector).flush()
}
}
private fun listOfKtFilesFrom(paths: List<String>): List<String> {

View File

@@ -109,10 +109,13 @@ class CheckIrElementVisitor(
}
var type = expression.type
while (true) {
val inlinedClass = type.getInlinedClass() ?: break
type = getInlineClassUnderlyingType(inlinedClass)
}
// while (true) {
// val inlinedClass = type.getInlinedClass() ?: break
// // TODO(Wasm): Stop using recursive inline classes
// if (getInlineClassUnderlyingType(inlinedClass) == type)
// break
// type = getInlineClassUnderlyingType(inlinedClass)
// }
expression.ensureTypesEqual(type, naturalType)
}

View File

@@ -269,6 +269,9 @@ abstract class Symbols<out T : CommonBackendContext>(val context: T, irBuiltIns:
open val ThrowKotlinNothingValueException: IrSimpleFunctionSymbol
get() = TODO("Support KotlinNothingValueException in Kotlin/Native and make this val abstract")
open val ThrowIllegalStateException: IrSimpleFunctionSymbol
get() = TODO("Support ThrowIllegalStateException")
abstract val stringBuilder: IrClassSymbol
abstract val defaultConstructorMarker: IrClassSymbol

View File

@@ -252,11 +252,15 @@ class InlineClassLowering(val context: CommonBackendContext) {
}
}
private fun Name.toInlineClassImplementationName() = when {
isSpecial -> Name.special(asString() + INLINE_CLASS_IMPL_SUFFIX)
else -> Name.identifier(asString() + INLINE_CLASS_IMPL_SUFFIX)
private fun IrFunction.toInlineClassImplementationName(): Name {
val klass = this.parentAsClass!!
val newName = klass.name.asString() + "__" + name.asString() + INLINE_CLASS_IMPL_SUFFIX
return when {
name.isSpecial -> Name.special("<" + newName)
else -> Name.identifier(newName)
}
}
private fun createStaticBodilessMethod(function: IrFunction): IrSimpleFunction =
createStaticFunctionWithReceivers(function.parent, function.name.toInlineClassImplementationName(), function)
createStaticFunctionWithReceivers(function.parent, function.toInlineClassImplementationName(), function)
}

View File

@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.ir.isMemberOfOpenClass
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.backend.js.export.isExported
import org.jetbrains.kotlin.ir.backend.js.utils.associatedObject
import org.jetbrains.kotlin.ir.backend.js.utils.getInlinedClass
import org.jetbrains.kotlin.ir.backend.js.utils.getJsName
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
import org.jetbrains.kotlin.ir.backend.js.utils.isAssociatedObjectAnnotatedAnnotation

View File

@@ -6,7 +6,32 @@
package org.jetbrains.kotlin.ir.backend.js
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 {
override val mapping: JsMapping
}
val inlineClassesUtils: InlineClassesUtils
}
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
}

View File

@@ -19,7 +19,9 @@ 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.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.JsInlineClassesUtils
import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames
import org.jetbrains.kotlin.ir.backend.js.utils.getInlinedClass
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
@@ -128,6 +130,9 @@ class JsIrBackendContext(
override val sharedVariablesManager = JsSharedVariablesManager(irBuiltIns, implicitDeclarationFile)
override val mapping = JsMapping()
override val inlineClassesUtils = JsInlineClassesUtils(this)
override val declarationFactory = JsDeclarationFactory(mapping)
companion object {
@@ -200,6 +205,9 @@ class JsIrBackendContext(
override val defaultConstructorMarker =
symbolTable.referenceClass(context.getJsInternalClass("DefaultConstructorMarker"))
override val ThrowIllegalStateException: IrSimpleFunctionSymbol =
symbolTable.referenceSimpleFunction(getFunctions(kotlinPackageFqn.child(Name.identifier("THROW_ISE"))).single())
override val stringBuilder
get() = TODO("not implemented")
override val copyRangeTo: Map<ClassDescriptor, IrSimpleFunctionSymbol>

View File

@@ -294,6 +294,12 @@ private val enumUsageLoweringPhase = makeBodyLoweringPhase(
prerequisite = setOf(enumEntryCreateGetInstancesFunsLoweringPhase)
)
private val externalEnumUsageLoweringPhase = makeBodyLoweringPhase(
::ExternalEnumUsagesLowering,
name = "ExternalEnumUsagesLowering",
description = "Replace external enum entry accesses with field accesses"
)
private val enumEntryRemovalLoweringPhase = makeDeclarationTransformerPhase(
::EnumClassRemoveEntriesLowering,
name = "EnumEntryRemovalLowering",
@@ -494,7 +500,7 @@ private val multipleCatchesLoweringPhase = makeBodyLoweringPhase(
)
private val bridgesConstructionPhase = makeDeclarationTransformerPhase(
::BridgesConstruction,
::JsBridgesConstruction,
name = "BridgesConstruction",
description = "Generate bridges",
prerequisite = setOf(suspendFunctionsLoweringPhase)
@@ -652,6 +658,7 @@ val loweringList = listOf<Lowering>(
enumEntryCreateGetInstancesFunsLoweringPhase,
enumSyntheticFunsLoweringPhase,
enumUsageLoweringPhase,
externalEnumUsageLoweringPhase,
enumEntryRemovalLoweringPhase,
suspendFunctionsLoweringPhase,
propertyReferenceLoweringPhase,

View File

@@ -18,9 +18,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
import org.jetbrains.kotlin.ir.descriptors.*
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrGetValue
import org.jetbrains.kotlin.ir.expressions.IrSetVariable
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
@@ -35,11 +33,10 @@ import org.jetbrains.kotlin.name.Name
class JsSharedVariablesManager(val builtIns: IrBuiltIns, val implicitDeclarationsFile: IrPackageFragment) : SharedVariablesManager {
override fun declareSharedVariable(originalDeclaration: IrVariable): IrVariable {
val valueType = originalDeclaration.type
val initializer = originalDeclaration.initializer ?: IrConstImpl.constNull(
originalDeclaration.startOffset,
originalDeclaration.endOffset,
valueType
builtIns.nothingNType
)
val constructorSymbol = closureBoxConstructorDeclaration.symbol
@@ -70,19 +67,30 @@ class JsSharedVariablesManager(val builtIns: IrBuiltIns, val implicitDeclaration
override fun defineSharedValue(originalDeclaration: IrVariable, sharedVariableDeclaration: IrVariable) = sharedVariableDeclaration
override fun getSharedValue(sharedVariableSymbol: IrVariableSymbol, originalGet: IrGetValue) = IrGetFieldImpl(
originalGet.startOffset, originalGet.endOffset,
closureBoxFieldDeclaration.symbol,
originalGet.type,
IrGetValueImpl(
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,
closureBoxType,
sharedVariableSymbol,
originalGet.origin
),
originalGet.origin
)
originalGet.type,
IrTypeOperator.IMPLICIT_CAST,
originalGet.type,
getField
)
}
override fun setSharedValue(sharedVariableSymbol: IrVariableSymbol, originalSet: IrSetVariable): IrExpression =
IrSetFieldImpl(
@@ -186,7 +194,7 @@ class JsSharedVariablesManager(val builtIns: IrBuiltIns, val implicitDeclaration
val receiver = JsIrBuilder.buildGetValue(closureBoxClassDeclaration.thisReceiver!!.symbol)
val value = JsIrBuilder.buildGetValue(parameterDeclaration.symbol)
val setField = JsIrBuilder.buildSetField(closureBoxFieldDeclaration.symbol, receiver, value, closureBoxFieldDeclaration.type)
val setField = JsIrBuilder.buildSetField(closureBoxFieldDeclaration.symbol, receiver, value, builtIns.unitType)
declaration.body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, listOf(setField))

View File

@@ -7,18 +7,25 @@ package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
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.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.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
class AutoboxingTransformer(val context: JsIrBackendContext) : AbstractValueUsageTransformer(context.irBuiltIns), BodyLoweringPass {
class AutoboxingTransformer(val context: JsCommonBackendContext) : AbstractValueUsageTransformer(context.irBuiltIns), BodyLoweringPass {
val icUtils = context.inlineClassesUtils
override fun lower(irBody: IrBody, container: IrDeclaration) {
// TODO workaround for callable references
@@ -55,14 +62,17 @@ class AutoboxingTransformer(val context: JsIrBackendContext) : AbstractValueUsag
is IrGetField -> this.symbol.owner.type
is IrTypeOperatorCall -> {
assert(operator == IrTypeOperator.REINTERPRET_CAST) { "Only REINTERPRET_CAST expected at this point" }
this.typeOperand
if (operator == IrTypeOperator.REINTERPRET_CAST) {
this.typeOperand
} else {
this.type
}
}
is IrGetValue -> {
val value = this.symbol.owner
if (value is IrValueParameter && value.isDispatchReceiver) {
irBuiltIns.anyNType
if (value is IrValueParameter && icUtils.shouldValueParameterBeBoxed(value)) {
irBuiltIns.anyType
} else {
this.type
}
@@ -77,6 +87,9 @@ class AutoboxingTransformer(val context: JsIrBackendContext) : AbstractValueUsag
val expectedType = type
if (expectedType.isNothing())
return this
if (actualType.isUnit() && !expectedType.isUnit()) {
// Don't materialize Unit if value is known to be proper Unit on runtime
if (!this.isGetUnit()) {
@@ -85,8 +98,8 @@ class AutoboxingTransformer(val context: JsIrBackendContext) : AbstractValueUsag
}
}
val actualInlinedClass = actualType.getInlinedClass()
val expectedInlinedClass = expectedType.getInlinedClass()
val actualInlinedClass = icUtils.getInlinedClass(actualType)
val expectedInlinedClass = icUtils.getInlinedClass(expectedType)
// Mimicking behaviour of current JS backend
// TODO: Revisit
@@ -97,8 +110,8 @@ class AutoboxingTransformer(val context: JsIrBackendContext) : AbstractValueUsag
val function = when {
actualInlinedClass == null && expectedInlinedClass == null -> return this
actualInlinedClass != null && expectedInlinedClass == null -> context.intrinsics.jsBoxIntrinsic
actualInlinedClass == null && expectedInlinedClass != null -> context.intrinsics.jsUnboxIntrinsic
actualInlinedClass != null && expectedInlinedClass == null -> icUtils.boxIntrinsic
actualInlinedClass == null && expectedInlinedClass != null -> icUtils.unboxIntrinsic
else -> return this
}
@@ -119,14 +132,14 @@ class AutoboxingTransformer(val context: JsIrBackendContext) : AbstractValueUsag
resultType: IrType,
call: (IrExpression) -> IrExpression
): IrExpression {
if (!actualType.isNullable())
if (!actualType.isNullable() || !resultType.isNullable())
return call(arg)
return JsIrBuilder.run {
// TODO: Set parent of local variables
val tmp = buildVar(actualType, parent = null, initializer = arg)
val nullCheck = buildIfElse(
type = resultType,
cond = buildCall(irBuiltIns.eqeqSymbol).apply {
cond = buildCall(irBuiltIns.eqeqeqSymbol).apply {
putValueArgument(0, buildGetValue(tmp.symbol))
putValueArgument(1, buildNull(irBuiltIns.nothingNType))
},
@@ -156,7 +169,10 @@ class AutoboxingTransformer(val context: JsIrBackendContext) : AbstractValueUsag
override fun IrExpression.useAsDispatchReceiver(expression: IrFunctionAccessExpression): IrExpression {
return this.useAsArgument(expression.target.dispatchReceiverParameter!!)
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 {
@@ -175,22 +191,10 @@ class AutoboxingTransformer(val context: JsIrBackendContext) : AbstractValueUsag
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())
if (icUtils.isTypeInlined(type) && !icUtils.isTypeInlined(expression.type) && !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
}
}

View File

@@ -11,15 +11,14 @@ import org.jetbrains.kotlin.backend.common.bridges.FunctionHandle
import org.jetbrains.kotlin.backend.common.bridges.generateBridges
import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom
import org.jetbrains.kotlin.backend.common.ir.isMethodOfAny
import org.jetbrains.kotlin.backend.common.ir.isSuspend
import org.jetbrains.kotlin.backend.common.lower.*
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
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.realOverrideTarget
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
@@ -48,10 +47,15 @@ import org.jetbrains.kotlin.ir.util.*
// fun foo(t: Any?) = foo(t as Int) // Constructed bridge
// }
//
class BridgesConstruction(val context: CommonBackendContext) : DeclarationTransformer {
abstract class BridgesConstruction(val context: CommonBackendContext) : DeclarationTransformer {
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>? {
if (declaration !is IrSimpleFunction || declaration.isStaticMethodOfClass || declaration.parent !is IrClass) return null
@@ -59,10 +63,6 @@ class BridgesConstruction(val context: CommonBackendContext) : DeclarationTransf
}
private fun generateBridges(function: IrSimpleFunction): List<IrDeclaration>? {
// equals(Any?), hashCode(), toString() never need bridges
if (function.isMethodOfAny())
return null
val (specialOverride: IrSimpleFunction?, specialOverrideInfo) =
specialBridgeMethods.findSpecialWithOverride(function) ?: Pair(null, null)
@@ -86,6 +86,11 @@ class BridgesConstruction(val context: CommonBackendContext) : DeclarationTransf
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()) {
// TODO: Revisit bridges from external properties
continue
@@ -160,7 +165,13 @@ class BridgesConstruction(val context: CommonBackendContext) : DeclarationTransf
}
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 {
call.extensionReceiver = irCastIfNeeded(irGet(it), delegateTo.extensionReceiverParameter!!.type)
}
@@ -181,6 +192,25 @@ class BridgesConstruction(val context: CommonBackendContext) : DeclarationTransf
// TODO: get rid of Unit check
private fun IrBlockBodyBuilder.irCastIfNeeded(argument: IrExpression, type: IrType): IrExpression =
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
@@ -197,23 +227,5 @@ data class IrBasedFunctionHandle(val function: IrSimpleFunction) : FunctionHandl
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()
}

View File

@@ -6,32 +6,24 @@
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.backend.common.getOrPut
import org.jetbrains.kotlin.backend.common.ir.copyParameterDeclarationsFrom
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
import org.jetbrains.kotlin.backend.common.lower.irIfThen
import org.jetbrains.kotlin.backend.common.lower.parents
import org.jetbrains.kotlin.backend.common.lower.*
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
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.utils.toJsArrayLiteral
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.buildField
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl
import org.jetbrains.kotlin.ir.descriptors.WrappedClassConstructorDescriptor
import org.jetbrains.kotlin.ir.descriptors.WrappedFieldDescriptor
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrExpressionBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrConstructorSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.util.*
@@ -40,7 +32,7 @@ import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
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
override fun lower(irBody: IrBody, container: IrDeclaration) {
@@ -48,41 +40,12 @@ class EnumUsageLowering(val context: JsIrBackendContext) : BodyLoweringPass {
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 lowerEnumEntry(enumEntry, klass)
if (klass.isExternal) return expression
return lowerEnumEntry(enumEntry, klass)
}
})
}
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 {
val descriptor = WrappedFieldDescriptor()
val symbol = IrFieldSymbolImpl(descriptor)
return entry.run {
IrFieldImpl(
startOffset, endOffset, origin, symbol, name, irClass.defaultType, Visibilities.PUBLIC,
isFinal = false, isExternal = true, isStatic = true,
isFakeOverride = entry.isFakeOverride
).also {
descriptor.bind(it)
it.parent = irClass
// TODO need a way to emerge local declarations from BodyLoweringPass
stageController.unrestrictDeclarationListsAccess {
irClass.declarations += it
}
}
}
}
private fun lowerEnumEntry(enumEntry: IrEnumEntry, klass: IrClass) =
enumEntry.getInstanceFun!!.run { JsIrBuilder.buildCall(symbol) }
}
@@ -319,7 +282,7 @@ class EnumClassConstructorBodyTransformer(val context: JsCommonBackendContext) :
private val IrClass.goodEnum: Boolean
get() = isEnumClass && !descriptor.isExpect && !isEffectivelyExternal()
class EnumEntryInstancesLowering(val context: JsIrBackendContext) : DeclarationTransformer {
class EnumEntryInstancesLowering(val context: JsCommonBackendContext) : DeclarationTransformer {
private var IrEnumEntry.correspondingField by context.mapping.enumEntryToCorrespondingField
@@ -352,7 +315,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
@@ -370,7 +333,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 IrClass.initEntryInstancesFun: IrSimpleFunction? by context.mapping.enumClassToInitEntryInstancesFun
@@ -435,7 +398,7 @@ private fun buildFunction(
returnType: IrType
) = JsIrBuilder.buildFunction(name, returnType, irClass)
class EnumEntryCreateGetInstancesFunsLowering(val context: JsIrBackendContext): DeclarationTransformer {
class EnumEntryCreateGetInstancesFunsLowering(val context: JsCommonBackendContext): DeclarationTransformer {
private var IrEnumEntry.correspondingField by context.mapping.enumEntryToCorrespondingField
private var IrClass.initEntryInstancesFun: IrSimpleFunction? by context.mapping.enumClassToInitEntryInstancesFun
@@ -474,7 +437,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
@@ -497,7 +460,7 @@ class EnumSyntheticFunctionsLowering(val context: JsIrBackendContext): Declarati
return null
}
private val throwISESymbol = context.throwISEsymbol
private val throwISESymbol = context.ir.symbols.ThrowIllegalStateException
private fun createEnumValueOfBody(valueOfFun: IrFunction, irClass: IrClass): IrBlockBody {
val nameParameter = valueOfFun.valueParameters[0]
@@ -518,21 +481,12 @@ class EnumSyntheticFunctionsLowering(val context: JsIrBackendContext): Declarati
}
}
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).apply {
putValueArgument(0, irVararg)
}
}
private fun createEnumValuesBody(valuesFun: IrFunction, irClass: IrClass): IrBlockBody {
val backendContext = context
return context.createIrBuilder(valuesFun.symbol).run {
irBlockBody {
val instances = irClass.enumEntries.map { irCall(it.getInstanceFun!!) }
+irReturn(
irClass.enumEntries.map { irCall(it.getInstanceFun!!) }
.toJsArrayLiteral(backendContext, valuesFun.returnType, irClass.defaultType)
IrVarargImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, valuesFun.returnType, irClass.defaultType, instances)
)
}
}
@@ -543,7 +497,7 @@ private val IrClass.enumEntries: List<IrEnumEntry>
get() = declarations.filterIsInstance<IrEnumEntry>()
// Should be applied recursively
class EnumClassRemoveEntriesLowering(val context: JsIrBackendContext) : DeclarationTransformer {
class EnumClassRemoveEntriesLowering(val context: JsCommonBackendContext) : DeclarationTransformer {
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
// Remove IrEnumEntry nodes from class declarations. Replace them with corresponding class declarations (if they have them).
if (declaration is IrEnumEntry && !declaration.descriptor.isExpect && !declaration.isEffectivelyExternal()) {

View File

@@ -0,0 +1,65 @@
/*
* 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.descriptors.Visibilities
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl
import org.jetbrains.kotlin.ir.descriptors.WrappedFieldDescriptor
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.symbols.impl.IrFieldSymbolImpl
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.isFakeOverride
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 {
val descriptor = WrappedFieldDescriptor()
val symbol = IrFieldSymbolImpl(descriptor)
return entry.run {
IrFieldImpl(
startOffset, endOffset, origin, symbol, name, irClass.defaultType, Visibilities.PUBLIC,
isFinal = false, isExternal = true, isStatic = true,
isFakeOverride = entry.isFakeOverride
).also {
descriptor.bind(it)
it.parent = irClass
// TODO need a way to emerge local declarations from BodyLoweringPass
stageController.unrestrictDeclarationListsAccess {
irClass.declarations += it
}
}
}
}
}

View File

@@ -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.backend.common.CommonBackendContext
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: CommonBackendContext) : BridgesConstruction(context) {
override fun getFunctionSignature(function: IrSimpleFunction): Signature =
jsFunctionSignature(function)
}

View File

@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrArithBuilder
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.getInlinedClass
import org.jetbrains.kotlin.ir.backend.js.utils.isPure
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration

View File

@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
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.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl

View File

@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
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.getInlinedClass
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.*
@@ -26,7 +27,6 @@ import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
import org.jetbrains.kotlin.ir.util.getInlinedClass
import org.jetbrains.kotlin.ir.visitors.*
class SuspendState(type: IrType) {

View File

@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.ir.isSuspend
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
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.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
@@ -18,7 +19,6 @@ import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.isUnit
import org.jetbrains.kotlin.ir.util.getInlinedClass
import org.jetbrains.kotlin.ir.visitors.*
object COROUTINE_ROOT_LOOP : IrStatementOriginImpl("COROUTINE_ROOT_LOOP")

View File

@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
import org.jetbrains.kotlin.ir.backend.js.utils.getInlinedClass
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
@@ -19,7 +20,6 @@ import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.util.getInlineClassBackingField
import org.jetbrains.kotlin.ir.util.getInlinedClass
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
import org.jetbrains.kotlin.js.backend.ast.*

View File

@@ -5,11 +5,13 @@
package org.jetbrains.kotlin.ir.backend.js.utils
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.ir.util.getInlineClassUnderlyingType
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
@@ -38,3 +40,39 @@ private fun IrClassifierSymbol.asString() = when (this) {
is IrClassSymbol -> this.owner.fqNameWhenAvailable!!.asString()
else -> error("Unexpected kind of IrClassifierSymbol: " + javaClass.typeName)
}
/**
* Returns inline class for given class or null of type is not inlined
*/
fun IrType.getInlinedClass(): IrClass? {
if (this is IrSimpleType) {
val erased = erase(this) ?: return null
if (erased.isInline) {
if (this.isMarkedNullable()) {
var fieldType: IrType
var fieldInlinedClass = erased
while (true) {
fieldType = getInlineClassUnderlyingType(fieldInlinedClass)
if (fieldType.isMarkedNullable()) {
return null
}
fieldInlinedClass = fieldType.getInlinedClass() ?: break
}
}
return erased
}
}
return null
}
tailrec fun erase(type: IrType): IrClass? {
val classifier = type.classifierOrFail
return when (classifier) {
is IrClassSymbol -> classifier.owner
is IrTypeParameterSymbol -> erase(classifier.owner.superTypes.first())
else -> error(classifier)
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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? {
if (type !is IrSimpleType) return null
val erased = erase(type) ?: return null
if (erased.isInline) {
if (type.isMarkedNullable()) {
var fieldType: IrType
var fieldInlinedClass = erased
while (true) {
fieldType = getInlineClassUnderlyingType(fieldInlinedClass)
if (fieldType.isMarkedNullable()) {
return null
}
fieldInlinedClass = fieldType.getInlinedClass() ?: break
}
}
return erased
}
return null
}
override fun isClassInlineLike(klass: IrClass): Boolean =
klass.isInline
override val boxIntrinsic: IrSimpleFunctionSymbol
get() = context.intrinsics.jsBoxIntrinsic
override val unboxIntrinsic: IrSimpleFunctionSymbol
get() = context.intrinsics.jsUnboxIntrinsic
}

View File

@@ -100,7 +100,7 @@ fun fieldSignature(field: IrField): Signature {
return BackingFieldSignature(field)
}
fun functionSignature(declaration: IrFunction): Signature {
fun jsFunctionSignature(declaration: IrFunction): Signature {
require(!declaration.isStaticMethodOfClass)
require(declaration.dispatchReceiverParameter != null)
@@ -139,7 +139,7 @@ fun functionSignature(declaration: IrFunction): Signature {
declaration.returnType.let {
// Return type is only used in signature for inline class and Unit types because
// they are binary incompatible with supertypes.
if (it.isInlined() || it.isUnit()) {
if (it.getInlinedClass() != null || it.isUnit()) {
nameBuilder.append("_ret$${it.asString()}")
}
}
@@ -280,7 +280,7 @@ class NameTables(
}
private fun generateNameForMemberFunction(declaration: IrSimpleFunction) {
when (val signature = functionSignature(declaration)) {
when (val signature = jsFunctionSignature(declaration)) {
is StableNameSignature -> memberNames.declareStableName(signature, signature.name)
is ParameterTypeBasedSignature -> memberNames.declareFreshName(signature, signature.suggestedName)
}
@@ -331,7 +331,7 @@ class NameTables(
}
fun getNameForMemberFunction(function: IrSimpleFunction): String {
val signature = functionSignature(function)
val signature = jsFunctionSignature(function)
val name = memberNames.names[signature] ?: mappedNames[mapToKey(signature)]
// TODO Add a compiler flag, which enables this behaviour

View File

@@ -75,4 +75,14 @@ fun IrExpression?.isPure(anyVariable: Boolean, checkFields: Boolean = true): Boo
}
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
}

View File

@@ -15,6 +15,7 @@ dependencies {
compile(project(":js:js.ast"))
compile(project(":js:js.frontend"))
compile(project(":compiler:backend.js"))
compile(project(":wasm:ir"))
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
}

View File

@@ -8,25 +8,31 @@ package org.jetbrains.kotlin.backend.wasm
import org.jetbrains.kotlin.backend.common.ir.Ir
import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.backend.js.JsDeclarationFactory
import org.jetbrains.kotlin.backend.wasm.utils.WasmInlineClassesUtils
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.impl.EmptyPackageFragmentDescriptor
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.JsMapping
import org.jetbrains.kotlin.ir.backend.js.JsSharedVariablesManager
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
import org.jetbrains.kotlin.ir.symbols.IrExternalPackageFragmentSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrExternalPackageFragmentSymbolImpl
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
class WasmBackendContext(
val module: ModuleDescriptor,
@@ -44,10 +50,12 @@ class WasmBackendContext(
override val extractedLocalClasses: MutableSet<IrClass> = hashSetOf()
// 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(
DescriptorlessExternalPackageFragmentSymbol(),
FqName("kotlin")
fqName
)
}
@@ -55,14 +63,40 @@ class WasmBackendContext(
override val declarationFactory = JsDeclarationFactory(mapping)
val objectToGetInstanceFunction = mutableMapOf<IrClassSymbol, IrSimpleFunction>()
override val internalPackageFqn = FqName("kotlin.wasm")
private val internalPackageFragment = IrExternalPackageFragmentImpl(
IrExternalPackageFragmentSymbolImpl(
EmptyPackageFragmentDescriptor(builtIns.builtInsModule, FqName("kotlin.wasm.internal"))
)
)
private val internalPackageFragmentDescriptor = EmptyPackageFragmentDescriptor(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 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 = internalPackageFragment.addFunction {
name = Name.identifier("startFunction")
returnType = irBuiltIns.unitType
}.apply {
body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET)
}
override val sharedVariablesManager = JsSharedVariablesManager(irBuiltIns, internalPackageFragment)
@@ -72,6 +106,8 @@ class WasmBackendContext(
override fun shouldGenerateHandlerParameterForDefaultBodyFun() = true
}
override val inlineClassesUtils = WasmInlineClassesUtils(wasmSymbols)
override fun log(message: () -> String) {
/*TODO*/
if (inVerbosePhase) print(message())

View File

@@ -5,29 +5,28 @@
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.lower
import org.jetbrains.kotlin.backend.common.lower.*
import org.jetbrains.kotlin.backend.common.lower.inline.FunctionInlining
import org.jetbrains.kotlin.backend.common.phaser.*
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
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.backend.wasm.lower.*
import org.jetbrains.kotlin.ir.backend.js.lower.*
import org.jetbrains.kotlin.ir.backend.js.lower.inline.RemoveInlineFunctionsWithReifiedTypeParametersLowering
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
private fun ClassLoweringPass.runOnFilesPostfix(moduleFragment: IrModuleFragment) = moduleFragment.files.forEach { runOnFilePostfix(it) }
private fun makeWasmModulePhase(
lowering: (WasmBackendContext) -> FileLoweringPass,
name: String,
description: String,
prerequisite: Set<AnyNamedPhase> = emptySet()
) = makeIrModulePhase<WasmBackendContext>(lowering, name, description, prerequisite, actions = setOf(validationAction, defaultDumper))
) = makeIrModulePhase(
lowering,
name,
description,
prerequisite,
actions = setOf(validationAction, defaultDumper)
)
private fun makeCustomWasmModulePhase(
op: (WasmBackendContext, IrModuleFragment) -> Unit,
@@ -71,6 +70,12 @@ private val expectDeclarationsRemovingPhase = makeWasmModulePhase(
description = "Remove expect declaration from module fragment"
)
private val stringConstructorLowering = makeWasmModulePhase(
::SimpleStringConcatenationLowering,
name = "StringConcatenation",
description = "String concatenation lowering"
)
private val lateinitNullableFieldsPhase = makeWasmModulePhase(
::NullableFieldsForLateinitCreationLowering,
name = "LateinitNullableFields",
@@ -97,12 +102,6 @@ private val provisionalFunctionExpressionPhase = makeWasmModulePhase(
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(
{ context, module ->
FunctionInlining(context).inline(module)
@@ -123,7 +122,7 @@ private val removeInlineFunctionsWithReifiedTypeParametersLoweringPhase = makeWa
private val tailrecLoweringPhase = makeWasmModulePhase(
::TailrecLowering,
name = "TailrecLowering",
description = "Replace `tailrec` callsites with equivalent loop"
description = "Replace `tailrec` call sites with equivalent loop"
)
private val enumClassConstructorLoweringPhase = makeWasmModulePhase(
@@ -132,6 +131,62 @@ private val enumClassConstructorLoweringPhase = makeWasmModulePhase(
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(
::SharedVariablesLowering,
@@ -139,6 +194,12 @@ private val sharedVariablesLoweringPhase = makeWasmModulePhase(
description = "Box captured mutable variables"
)
private val callableReferencePhase = makeWasmModulePhase(
::WasmCallableReferenceLowering,
name = "WasmCallableReferenceLowering",
description = "Handle callable references"
)
private val localDelegatedPropertiesLoweringPhase = makeWasmModulePhase(
{ LocalDelegatedPropertiesLowering() },
name = "LocalDelegatedPropertiesLowering",
@@ -194,7 +255,7 @@ private val defaultArgumentPatchOverridesPhase = makeWasmModulePhase(
private val defaultParameterInjectorPhase = makeWasmModulePhase(
{ context -> DefaultParameterInjector(context, skipExternalMethods = true) },
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)
)
@@ -204,18 +265,6 @@ private val defaultParameterCleanerPhase = makeWasmModulePhase(
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(
{ PropertiesLowering() },
name = "PropertiesLowering",
@@ -265,7 +314,7 @@ private val returnableBlockLoweringPhase = makeWasmModulePhase(
)
private val bridgesConstructionPhase = makeWasmModulePhase(
::BridgesConstruction,
::WasmBridgesConstruction,
name = "BridgesConstruction",
description = "Generate bridges"
)
@@ -282,65 +331,51 @@ private val inlineClassUsageLoweringPhase = makeWasmModulePhase(
description = "Handle inline class usages"
)
//private val autoboxingTransformerPhase = makeJsModulePhase(
// ::AutoboxingTransformer,
// name = "AutoboxingTransformer",
// description = "Insert box/unbox intrinsics"
//)
private val blockDecomposerLoweringPhase = makeCustomWasmModulePhase(
{ context, module ->
WasmBlockDecomposerLowering(context).lower(module)
module.patchDeclarationParents()
},
name = "BlockDecomposerLowering",
description = "Transform statement-like-expression nodes into pure-statement to make it easily transform into JS"
private val autoboxingTransformerPhase = makeWasmModulePhase(
{ context -> AutoboxingTransformer(context) },
name = "AutoboxingTransformer",
description = "Insert box/unbox intrinsics"
)
private val wasmVarargExpressionLoweringPhase = makeWasmModulePhase(
::WasmVarargExpressionLowering,
name = "WasmVarargExpressionLowering",
description = "Replace vararg expressions with constructor calls"
)
private val wasmThrowDebugLoweringPhase = makeWasmModulePhase(
::WasmThrowDebugLowering,
name = "WasmThrowDebugLowering",
description = "Replace exception throwing with debug runtime calls"
)
//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 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 builtInsLoweringPhase = makeWasmModulePhase(
::BuiltInsLowering,
name = "BuiltInsLowering",
description = "Lower IR buildins"
description = "Lower IR builtins"
)
private val objectDeclarationLoweringPhase = makeWasmModulePhase(
::ObjectUsageLowering,
::ObjectDeclarationLowering,
name = "ObjectDeclarationLowering",
description = "Create lazy object instance generator functions"
)
@@ -351,26 +386,52 @@ private val objectUsageLoweringPhase = makeWasmModulePhase(
description = "Transform IrGetObjectValue into instance generator call"
)
val wasmPhases = namedIrModulePhase<WasmBackendContext>(
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 = namedIrModulePhase(
name = "IrModuleLowering",
description = "IR module lowering",
lower = validateIrBeforeLowering then
excludeDeclarationsFromCodegenPhase then
expectDeclarationsRemovingPhase then
provisionalFunctionExpressionPhase then
// TODO: Need some helpers from stdlib
// arrayConstructorPhase then
functionInliningPhase then
provisionalFunctionExpressionPhase then
lateinitNullableFieldsPhase then
lateinitDeclarationLoweringPhase then
lateinitUsageLoweringPhase then
tailrecLoweringPhase then
enumClassConstructorLoweringPhase then
enumClassConstructorBodyLoweringPhase then
sharedVariablesLoweringPhase then
callableReferencePhase then
localDelegatedPropertiesLoweringPhase then
localDeclarationsLoweringPhase then
localClassExtractionPhase then
@@ -384,68 +445,51 @@ val wasmPhases = namedIrModulePhase<WasmBackendContext>(
initializersCleanupLoweringPhase then
// Common prefix ends
builtInsLoweringPhase then
// TODO: Commonize enumEntryToGetInstanceFunction
// Commonize array literal creation
// Extract external enum lowering to JS part
//
// enumClassLoweringPhase then
// enumUsageLoweringPhase then
enumEntryInstancesLoweringPhase then
enumEntryInstancesBodyLoweringPhase then
enumClassCreateInitializerLoweringPhase then
enumEntryCreateGetInstancesFunsLoweringPhase then
enumSyntheticFunsLoweringPhase then
enumUsageLoweringPhase then
enumEntryRemovalLoweringPhase then
// TODO: Requires stdlib
// suspendFunctionsLoweringPhase then
stringConstructorLowering then
returnableBlockLoweringPhase then
// TODO: Callable reference lowering is too JS specific.
// Should we reuse JVM or Native lowering?
// callableReferenceLoweringPhase then
defaultArgumentStubGeneratorPhase then
defaultArgumentPatchOverridesPhase then
defaultParameterInjectorPhase then
defaultParameterCleanerPhase then
// TODO: Investigate
// jsDefaultCallbackGeneratorPhase then
removeInlineFunctionsWithReifiedTypeParametersLoweringPhase then
// TODO: Varargs are too platform-specific. Reimplement.
// varargLoweringPhase then
// TODO: Investigate exception proposal
// TODO:
// multipleCatchesLoweringPhase then
bridgesConstructionPhase then
// TODO: Reimplement
// typeOperatorLoweringPhase then
// TODO: Reimplement
// secondaryConstructorLoweringPhase then
// secondaryFactoryInjectorLoweringPhase then
// TODO: Reimplement
// classReferenceLoweringPhase then
wasmVarargExpressionLoweringPhase then
inlineClassDeclarationLoweringPhase then
inlineClassUsageLoweringPhase then
// TODO: Commonize box/unbox intrinsics
// autoboxingTransformerPhase then
blockDecomposerLoweringPhase then
// TODO: Reimplement
// constLoweringPhase then
eraseVirtualDispatchReceiverParametersTypes then
bridgesConstructionPhase then
objectDeclarationLoweringPhase then
objectUsageLoweringPhase then
staticMembersLoweringPhase then
fieldInitializersLoweringPhase 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
validateIrAfterLowering
)

View File

@@ -7,37 +7,39 @@ package org.jetbrains.kotlin.backend.wasm
import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
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.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.SimpleType
import org.jetbrains.kotlin.util.OperatorNameConventions
class WasmSymbols(
context: WasmBackendContext,
private val symbolTable: SymbolTable
) : Symbols<WasmBackendContext>(context, context.irBuiltIns, symbolTable) {
override val ThrowNullPointerException
get() = TODO()
override val ThrowNoWhenBranchMatchedException
get() = TODO()
override val ThrowTypeCastException
get() = TODO()
override val ThrowUninitializedPropertyAccessException
get() = TODO()
private val wasmInternalPackage: PackageViewDescriptor =
context.module.getPackage(FqName("kotlin.wasm.internal"))
override val ThrowNullPointerException = getInternalFunction("THROW_NPE")
override val ThrowIllegalStateException = getInternalFunction("THROW_ISE")
override val ThrowNoWhenBranchMatchedException = ThrowIllegalStateException
override val ThrowTypeCastException = getInternalFunction("THROW_CCE")
override val ThrowUninitializedPropertyAccessException =
getInternalFunction("throwUninitializedPropertyAccessException")
override val defaultConstructorMarker =
getIrClass(FqName("kotlin.wasm.internal.DefaultConstructorMarker"))
override val ThrowKotlinNothingValueException: IrSimpleFunctionSymbol
get() = TODO()
override val defaultConstructorMarker
get() = TODO()
override val stringBuilder
get() = TODO()
override val copyRangeTo: Map<ClassDescriptor, IrSimpleFunctionSymbol>
@@ -49,7 +51,7 @@ class WasmSymbols(
override val getContinuation
get() = TODO()
override val coroutineContextGetter by lazy {
context.excludedDeclarations.addFunction {
context.getExcludedPackageFragment(FqName("kotlin.stubs")).addFunction {
name = Name.identifier("coroutineContextGetter\$Stub")
}.symbol
}
@@ -61,7 +63,9 @@ class WasmSymbols(
override val returnIfSuspended
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(
context.irBuiltIns.booleanType to getInternalFunction("wasm_i32_eq"),
@@ -70,12 +74,16 @@ class WasmSymbols(
context.irBuiltIns.charType to getInternalFunction("wasm_i32_eq"),
context.irBuiltIns.intType to getInternalFunction("wasm_i32_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.doubleType to getInternalFunction("wasm_f64_eq")
)
private fun wasmString(classfier: IrClassifierSymbol): String = with(context.irBuiltIns) {
when (classfier) {
private fun wasmPrimitiveTypeName(classifier: IrClassifierSymbol): String = with(context.irBuiltIns) {
when (classifier) {
booleanClass, byteClass, shortClass, charClass, intClass -> "i32"
floatClass -> "f32"
doubleClass -> "f64"
@@ -84,23 +92,66 @@ class WasmSymbols(
}
}
val irBuiltInsToWasmIntrinsics = context.irBuiltIns.run {
mapOf(
val comparisonBuiltInsToWasmIntrinsics = context.irBuiltIns.run {
listOf(
lessFunByOperandType to "lt",
lessOrEqualFunByOperandType to "le",
greaterOrEqualFunByOperandType to "ge",
greaterFunByOperandType to "gt"
).map { (typeToBuiltIn, wasmOp) ->
typeToBuiltIn.map { (type, builtin) ->
val wasmType = wasmString(type)
val wasmType = wasmPrimitiveTypeName(type)
val markSign = if (wasmType == "i32" || wasmType == "i64") "_s" else ""
builtin to getInternalFunction("wasm_${wasmType}_$wasmOp$markSign")
}
}.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 structNarrow = getInternalFunction("wasm_struct_narrow")
val boxIntrinsic: IrSimpleFunctionSymbol = getInternalFunction("boxIntrinsic")
val unboxIntrinsic: IrSimpleFunctionSymbol = getInternalFunction("unboxIntrinsic")
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 =
memberScope.getContributedClassifier(name, NoLookupLocation.FROM_BACKEND) as ClassDescriptor
@@ -116,7 +167,7 @@ class WasmSymbols(
internal fun getProperty(fqName: FqName): PropertyDescriptor =
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()
return symbolTable.referenceSimpleFunction(tmp)
}

View File

@@ -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 + "\""
}

View File

@@ -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")
}
}

View File

@@ -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)

View File

@@ -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")

View File

@@ -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)
}
}

View File

@@ -0,0 +1,70 @@
/*
* 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.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()
}
}

View File

@@ -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)
}
)
}
}

View File

@@ -1,206 +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.IrSimpleFunction
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 visitSetVariable(expression: IrSetVariable, 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
require(function is IrSimpleFunction) { "Only IrSimpleFunction could be called via IrCall" }
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)
when (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)
}

View File

@@ -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.Visibilities
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 != Visibilities.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

View File

@@ -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 "$.@_"

View File

@@ -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()
}

View File

@@ -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()}")
}

View File

@@ -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}")
}

View File

@@ -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.backend.wasm.codegen.interfaces
import org.jetbrains.kotlin.backend.wasm.ir2wasm.WasmBaseCodegenContext
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 getNextLabelId(): Int
fun defineLoopLevel(irLoop: IrLoop, labelType: LoopLabelType, level: Int)
fun referenceLoopLevel(irLoop: IrLoop, labelType: LoopLabelType): Int
val bodyGen: WasmExpressionBuilder
fun addInstruction(wasmInstr: WasmInstr)
}

View File

@@ -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.backend.wasm.codegen.interfaces
import org.jetbrains.kotlin.wasm.ir.*
import org.jetbrains.kotlin.backend.wasm.codegen.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, wasmStructType: WasmStructType)
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)
}

View File

@@ -9,7 +9,10 @@ import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analyzer.AbstractAnalyzerWithCompilerReport
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel
import org.jetbrains.kotlin.backend.wasm.codegen.IrModuleToWasm
import org.jetbrains.kotlin.wasm.ir.WatBuilder
import org.jetbrains.kotlin.backend.wasm.ir2wasm.WasmCodeGenerator
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.languageVersionSettings
import org.jetbrains.kotlin.ir.backend.js.MainModule
@@ -60,5 +63,16 @@ fun compileWasm(
wasmPhases.invokeToplevel(phaseConfig, context, moduleFragment)
return IrModuleToWasm(context).generateModule(moduleFragment)
val compiledWasmModule = WasmCompiledModuleFragment()
val codeGenerator = WasmCodeGenerator(context, compiledWasmModule)
codeGenerator.generateModule(moduleFragment)
val linkedModule = compiledWasmModule.linkWasmCompiledFragments()
val watGenerator = WatBuilder()
watGenerator.appendWasmModule(linkedModule)
val wat = watGenerator.toString()
return WasmCompilerResult(
wat,
generateStringLiteralsSupport(compiledWasmModule.stringLiterals)
)
}

View File

@@ -0,0 +1,475 @@
/*
* 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.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.codegen.interfaces.LoopLabelType
import org.jetbrains.kotlin.backend.wasm.codegen.interfaces.WasmFunctionCodegenContext
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.getClass
import org.jetbrains.kotlin.ir.types.isAny
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 -> body.buildRefNull()
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(
symbol = context.referenceFunction(wasmSymbols.stringGetLiteral),
type = WasmAnyRef
)
}
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(
global = context.referenceGlobal(field.symbol),
type = context.transformType(field.type)
)
}
}
private fun generateInstanceFieldAccess(field: IrField) {
body.buildStructGet(
context.referenceStructType(field.parentAsClass.symbol),
context.getStructFieldRef(field),
context.transformType(field.type)
)
}
override fun visitSetField(expression: IrSetField) {
val field = expression.symbol.owner
val receiver = expression.receiver
if (receiver != null) {
generateExpression(receiver)
generateExpression(expression.value)
body.buildStructSet(
structType = 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 visitSetVariable(expression: IrSetVariable) {
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 wasmStructType: WasmSymbol<WasmStructType> = context.referenceStructType(klass.symbol)
val wasmClassId = context.referenceClassId(klass.symbol)
val irFields: List<IrField> = klass.allFields(backendContext.irBuiltIns)
assert(irFields.isNotEmpty()) { "Class should have at least a single classId filed" }
irFields.forEachIndexed { index, field ->
if (index == 0)
body.buildConstI32Symbol(wasmClassId)
else
generateDefaultInitializerForType(context.transformType(field.type), body)
}
body.buildStructNew(wasmStructType)
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))
generateCall(expression)
}
private fun generateCall(call: IrFunctionAccessExpression) {
val function: IrFunction = call.symbol.owner.realOverrideTarget
// Box intrinsic has an additional klass ID argument.
// Processing it separately
if (function.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.buildStructNew(structTypeName)
return
}
call.dispatchReceiver?.let { generateExpression(it) }
call.extensionReceiver?.let { generateExpression(it) }
for (i in 0 until call.valueArgumentsCount) {
generateExpression(call.getValueArgument(i)!!)
}
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)
generateExpression(call.dispatchReceiver!!)
body.buildConstI32(vfSlot)
body.buildCall(context.referenceFunction(wasmSymbols.getVirtualMethodId), type = WasmI32)
} else {
val signatureId = context.referenceSignatureId(function.wasmSignature(backendContext.irBuiltIns))
generateExpression(call.dispatchReceiver!!)
body.buildConstI32Symbol(signatureId)
body.buildCall(context.referenceFunction(wasmSymbols.getInterfaceMethodId), type = WasmI32)
}
body.buildCallIndirect(
symbol = context.referenceFunctionType(function.symbol),
type = context.transformResultType(function.returnType)
)
} else { // Static function call
val name = context.referenceFunction(function.symbol)
body.buildCall(name, context.transformResultType(function.returnType))
}
}
// Return true if generated.
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.structNarrow -> {
val fromType = call.getTypeArgument(0)!!
val toType = call.getTypeArgument(1)!!
body.buildStructNarrow(context.transformType(fromType), context.transformType(toType))
}
wasmSymbols.wasmUnreachable -> {
body.buildUnreachable()
}
wasmSymbols.wasmFloatNaN -> {
body.buildF32NaN()
}
wasmSymbols.wasmDoubleNaN -> {
body.buildF64NaN()
}
wasmSymbols.unboxIntrinsic -> {
val fromType = call.getTypeArgument(0)!!
val toType = call.getTypeArgument(1)!!
val klass: IrClass = backendContext.inlineClassesUtils.getInlinedClass(toType)!!
val field = getInlineClassBackingField(klass)
body.buildStructNarrow(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
) {
body.buildRefNull()
}
body.buildReturn()
}
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.buildUnary(WasmUnaryOp.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 body.buildNop() // TODO: Don't nop
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.buildDrop()
}
// Return true if generated.
fun tryToGenerateWasmOpIntrinsicCall(function: IrFunction): Boolean {
if (function.hasWasmReinterpretAnnotation()) {
return true
}
val unaryOp = function.getWasmUnaryOpAnnotation()
if (unaryOp != null) {
body.buildUnary(WasmUnaryOp.valueOf(unaryOp))
return true
}
val binaryOp = function.getWasmBinaryOpAnnotation()
if (binaryOp != null) {
body.buildBinary(WasmBinaryOp.valueOf(binaryOp))
return true
}
val loadOp = function.getWasmLoadOpAnnotation()
if (loadOp != null) {
body.buildLoad(WasmLoadOp.valueOf(loadOp), WasmMemoryArgument(0, 0))
return true
}
val refOp = function.getWasmRefOpAnnotation()
if (refOp != null) {
when (WasmRefOp.valueOf(refOp)) {
WasmRefOp.REF_NULL -> body.buildRefNull()
WasmRefOp.REF_IS_NULL -> body.buildRefIsNull()
WasmRefOp.REF_EQ -> body.buildRefEq()
}
return true
}
return false
}
}

View File

@@ -0,0 +1,111 @@
/*
* 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.allInterfaces()
// Virtual methods in Wasm order
val virtualMethods: List<VirtualMethodMetadata> = run {
val virtualFunctions =
klass.declarations
.filterIsInstance<IrSimpleFunction>()
.filterVirtualFunctions()
.map {
VirtualMethodMetadata(
it,
it.wasmSignature(irBuiltIns)
)
}
val signatureToVirtualFunction = virtualFunctions.associateBy { it.signature }
val newVirtualMethods = virtualFunctions.filter { it.signature !in superClass?.virtualMethodsSignatures.orEmpty() }
val superVirtualMethods = superClass?.virtualMethods.orEmpty().map {
signatureToVirtualFunction[it.signature] ?: it
}
val orderedVirtualFunctions = superVirtualMethods + newVirtualMethods
orderedVirtualFunctions
}
init {
val vmToSignature = mutableMapOf<WasmSignature, MutableList<IrSimpleFunction>>()
for (vm in virtualMethods) {
vmToSignature.getOrPut(vm.signature) { mutableListOf() }.add(vm.function)
}
for ((sig, funcs) in vmToSignature) {
if (funcs.size > 1) {
val funcList = funcs.joinToString { " ---- ${it.fqNameWhenAvailable} \n" }
error(
"Class ${klass.fqNameWhenAvailable} has ${funcs.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
)
private val IrClass.superBroadClasses: List<IrClass>
get() = superTypes.map { it.classifierOrFail.owner as IrClass }
fun IrClass.allInterfaces(): List<IrClass> {
val shallowSuperClasses = superBroadClasses
return shallowSuperClasses.filter { it.isInterface } + shallowSuperClasses.flatMap { it.allInterfaces() }
}
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>()

View File

@@ -0,0 +1,293 @@
/*
* 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.codegen.ConstantDataIntArray
import org.jetbrains.kotlin.backend.wasm.codegen.ConstantDataIntField
import org.jetbrains.kotlin.backend.wasm.codegen.ConstantDataStruct
import org.jetbrains.kotlin.backend.wasm.codegen.interfaces.WasmModuleCodegenContext
import org.jetbrains.kotlin.backend.wasm.utils.*
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
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.fqNameWhenAvailable
import org.jetbrains.kotlin.ir.util.isAnnotationClass
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 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)
if (backendContext.inlineClassesUtils.isClassInlineLike(declaration.parentAsClass))
return
// Generate function type
val watName = declaration.fqNameWhenAvailable.toString()
val irParameters = declaration.getEffectiveValueParameters()
val wasmFunctionType =
WasmFunctionType(
watName,
parameterTypes = irParameters.map { context.transformValueParameterType(it) },
resultType = context.transformResultType(declaration.returnType)
)
context.defineFunctionType(declaration.symbol, wasmFunctionType)
val isIntrinsic = declaration.hasWasmReinterpretAnnotation() ||
declaration.getWasmUnaryOpAnnotation() != null ||
declaration.getWasmBinaryOpAnnotation() != null ||
declaration.getWasmRefOpAnnotation() != null
if (declaration is IrSimpleFunction) {
if (declaration.modality == Modality.ABSTRACT) return
if (declaration.isFakeOverride) return
val isVirtual = declaration.isOverridableOrOverrides
if (isVirtual) {
// Register function as virtual, meaning this function
// will be stored Wasm table and could be called indirectly.
context.registerVirtualFunction(declaration.symbol)
}
if (!isVirtual && isIntrinsic) {
// Calls to non-virtual intrinsic functions are replaced with something else.
// No need to generate them.
return
}
}
assert(declaration == declaration.realOverrideTarget) {
"Sanity check that $declaration is a real function that can be used in calls"
}
val importedName = declaration.getWasmImportAnnotation()
if (importedName != null) {
// Imported functions don't have bodies. Declaring the signature:
context.defineFunction(
declaration.symbol,
WasmImportedFunction(watName, wasmFunctionType, importedName)
)
// TODO: Support re-export of imported functions.
return
}
val function = WasmDefinedFunction(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)
if (isIntrinsic) {
bodyBuilder.tryToGenerateWasmOpIntrinsicCall(declaration)
} else {
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.buildReturn()
}
// Add unreachable if function returns something but not as a last instruction.
if (wasmFunctionType.resultType != null && declaration.body is IrBlockBody) {
exprGen.buildUnreachable()
}
context.defineFunction(declaration.symbol, function)
if (declaration == backendContext.startFunction)
context.setStartFunction(function)
if (declaration.isExported(backendContext)) {
context.addExport(
WasmExport(
function = function,
exportedName = declaration.name.identifier,
kind = WasmExport.Kind.FUNCTION
)
)
}
}
override fun visitClass(declaration: IrClass) {
if (declaration.isAnnotationClass) return
val symbol = declaration.symbol
if (declaration.isInterface) {
context.registerInterface(symbol)
} else {
val structType = WasmStructType(
name = declaration.fqNameWhenAvailable.toString(),
fields = declaration.allFields(irBuiltIns).map {
WasmStructFieldDeclaration(
name = it.name.toString(),
type = context.transformType(it.type),
isMutable = true
)
}
)
context.defineStructType(symbol, structType)
context.registerClass(symbol)
context.generateTypeInfo(symbol, binaryDataStruct(context.getClassMetadata(symbol)))
}
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: WasmValueType, 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)
WasmAnyRef -> g.buildRefNull()
is WasmStructRef -> g.buildRefNull()
WasmNullRefType -> g.buildRefNull()
WasmUnreachableType -> error("Unreachable type can't be initialized")
else -> error("Unknown value type")
}
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 == Visibilities.PUBLIC && fqNameWhenAvailable in context.additionalExportedDeclarations

View File

@@ -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()
}

View File

@@ -0,0 +1,115 @@
/*
* 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(): WasmValueType? =
when (this) {
builtIns.unitType,
builtIns.nothingType ->
null
else ->
toWasmValueType()
}
fun IrType.toWasmBlockResultType(): WasmValueType? =
when (this) {
builtIns.unitType ->
null
// TODO: Lower blocks with Nothing type?
builtIns.nothingType ->
WasmUnreachableType
else ->
toWasmValueType()
}
fun IrType.toBoxedInlineClassType(): WasmValueType =
WasmStructRef(context.referenceStructType(erasedUpperBound?.symbol ?: builtIns.anyClass))
fun IrType.toWasmValueType(): WasmValueType =
when (this) {
builtIns.booleanType,
builtIns.byteType,
builtIns.shortType,
builtIns.intType,
builtIns.charType ->
WasmI32
builtIns.longType ->
WasmI64
builtIns.floatType ->
WasmF32
builtIns.doubleType ->
WasmF64
builtIns.stringType ->
WasmAnyRef
builtIns.nothingNType ->
WasmNullRefType
builtIns.nothingType ->
WasmAnyRef
else -> {
val klass = this.getClass()
val ic = context.backendContext.inlineClassesUtils.getInlinedClass(this)
if (klass != null && klass.hasWasmForeignAnnotation()) {
WasmAnyRef
} else if (ic != null) {
getInlineClassUnderlyingType(ic).toWasmValueType()
} else {
WasmStructRef(context.referenceStructType(erasedUpperBound?.symbol ?: builtIns.anyClass))
}
}
}
}
// 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.classOrNull?.owner ?: 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
}

View File

@@ -0,0 +1,44 @@
/*
* 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<WasmStructType>
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 referenceSignatureId(signature: WasmSignature): WasmSymbol<Int>
fun referenceStringLiteral(string: String): WasmSymbol<Int>
fun transformType(irType: IrType): WasmValueType
fun transformBoxedType(irType: IrType): WasmValueType
fun transformValueParameterType(irValueParameter: IrValueParameter): WasmValueType
fun transformResultType(irType: IrType): WasmValueType?
fun transformBlockResultType(irType: IrType): WasmValueType?
fun getStructFieldRef(field: IrField): WasmSymbol<Int>
fun getClassMetadata(irClass: IrClassSymbol): ClassMetadata
}

View File

@@ -0,0 +1,179 @@
/*
* 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.codegen.ConstantDataElement
import org.jetbrains.kotlin.backend.wasm.codegen.ConstantDataStruct
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, WasmStructType>()
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 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>()
}
fun linkWasmCompiledFragments(): WasmModule {
bind(functions.unbound, functions.defined)
bind(globals.unbound, globals.defined)
bind(functionTypes.unbound, functionTypes.defined)
bind(structTypes.unbound, structTypes.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)
WasmData(id, 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(virtualFunctions.map { functions.defined.getValue(it) })
val typeInfoSize = classId
val memorySizeInPages = (typeInfoSize / 65_536) + 1
val memory = WasmMemory(memorySizeInPages, memorySizeInPages)
val module = WasmModule(
functionTypes = functionTypes.elements,
structTypes = structTypes.elements,
importedFunctions = functions.elements.filterIsInstance<WasmImportedFunction>(),
definedFunctions = functions.elements.filterIsInstance<WasmDefinedFunction>(),
table = table,
memory = memory,
globals = globals.elements,
exports = exports,
startFunction = WasmStartFunction(startFunction!!),
data = data
)
module.calculateIds()
return module
}
}
fun <IrSymbolType, WasmDeclarationType, 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)
}
}

View File

@@ -0,0 +1,71 @@
/*
* 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.backend.wasm.codegen.interfaces.LoopLabelType
import org.jetbrains.kotlin.backend.wasm.codegen.interfaces.WasmFunctionCodegenContext
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: WasmDefinedFunction,
override val backendContext: WasmBackendContext,
private val referencing: WasmBaseCodegenContext
) : WasmBaseCodegenContext by referencing,
WasmFunctionCodegenContext {
override val bodyGen: WasmExpressionBuilder =
WasmIrExpressionBuilder(wasmFunction.instructions)
private var labelIds = 0
override fun getNextLabelId(): Int = labelIds++
override fun addInstruction(wasmInstr: WasmInstr) {
irFunction.body
}
private val wasmLocals = LinkedHashMap<IrValueSymbol, WasmLocal>()
private val loopLevels = LinkedHashMap<Pair<IrLoop, LoopLabelType>, Int>()
private var localIds: Int = 0
override fun defineLocal(irValueDeclaration: IrValueSymbol) {
assert(irValueDeclaration !in wasmLocals) { "Redefinition of local" }
val owner = irValueDeclaration.owner
val wasmLocal = WasmLocal(
localIds++,
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))
}
}

View File

@@ -0,0 +1,164 @@
/*
* 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.codegen.ConstantDataElement
import org.jetbrains.kotlin.backend.wasm.codegen.interfaces.WasmModuleCodegenContext
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): WasmValueType {
return with(typeTransformer) { irType.toWasmValueType() }
}
override fun transformBoxedType(irType: IrType): WasmValueType {
return with(typeTransformer) { irType.toBoxedInlineClassType() }
}
override fun transformValueParameterType(irValueParameter: IrValueParameter): WasmValueType {
return with(typeTransformer) {
if (context.backendContext.inlineClassesUtils.shouldValueParameterBeBoxed(irValueParameter)) {
irValueParameter.type.toBoxedInlineClassType()
} else {
irValueParameter.type.toWasmValueType()
}
}
}
override fun transformResultType(irType: IrType): WasmValueType? {
return with(typeTransformer) { irType.toWasmResultType() }
}
override fun transformBlockResultType(irType: IrType): WasmValueType? {
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, wasmStructType: WasmStructType) {
wasmFragment.structTypes.define(irClass, wasmStructType)
}
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<WasmStructType> {
val type = irClass.defaultType
require(!type.isNothing()) {
"Can't reference Nothing type"
}
return wasmFragment.structTypes.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)
}
}

View File

@@ -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 WasmCodeGenerator(
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)
}
}

View File

@@ -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()
}

View File

@@ -6,40 +6,139 @@
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.createIrBuilder
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.IrSimpleFunction
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.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.render
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.util.isFunction
import org.jetbrains.kotlin.ir.util.isNullConst
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.util.OperatorNameConventions
class BuiltInsLowering(val context: WasmBackendContext) : FileLoweringPass {
private val irBuiltins = context.irBuiltIns
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) {
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 newSymbol = symbols.equalityFunctions[type]
?: error("Unsupported equality operator with type: ${type.render()}")
val newSymbol = symbols.equalityFunctions[type] ?: symbols.floatEqualityFunctions[type] ?: symbols.refEq
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)
}
// 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 nativeInvoke = isNativeInvoke(call)
if (nativeInvoke != null) {
return irCall(call, symbols.functionNInvokeMethods[nativeInvoke])
}
return call
}
private fun isNativeInvoke(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) {
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
val builder = context.createIrBuilder(irFile.symbol)
irFile.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
override fun visitCall(expression: IrCall): IrExpression {
val newExpression = transformCall(expression)
val newExpression = transformCall(expression, builder)
newExpression.transformChildrenVoid(this)
return newExpression
}

View File

@@ -0,0 +1,74 @@
/*
* 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.FunctionLoweringPass
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.builders.irGet
import org.jetbrains.kotlin.ir.builders.irImplicitCast
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.transformChildrenVoid
/**
* This lowering erases dispatch receiver types of virtual functions down to Any.
*
* WebAssembly function types are covariant 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) : FunctionLoweringPass {
override 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
}
})
}
}

View File

@@ -8,102 +8,22 @@ package org.jetbrains.kotlin.backend.wasm.lower
import org.jetbrains.kotlin.backend.common.ir.addChild
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.backend.wasm.utils.hasExcludedFromCodegenAnnotation
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.name.FqName
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()
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
/**
* 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 isExcluded(declaration: IrDeclaration): Boolean {
if (declaration is IrDeclarationWithName && declaration.fqNameWhenAvailable in BODILESS_BUILTIN_CLASSES)
return true
// Annotation can be applied to top-level declarations ...
if (declaration.hasExcludedFromCodegenAnnotation())
return true
// ... or files as a whole
val parentFile = declaration.parent as? IrFile
if (parentFile?.hasExcludedFromCodegenAnnotation() == true)
return true
@@ -117,7 +37,8 @@ fun excludeDeclarationsFromCodegen(context: WasmBackendContext, module: IrModule
val d = it.next() as? IrDeclarationWithName ?: continue
if (isExcluded(d)) {
it.remove()
context.excludedDeclarations.addChild(d)
// Move to "excluded" package fragment preserving fq-name
context.getExcludedPackageFragment(file.fqName).addChild(d)
}
}
}

View File

@@ -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
*/
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
}
})
}
}

View File

@@ -0,0 +1,93 @@
/*
* 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.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.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 typeParameter = this.classifierOrNull?.owner as? IrTypeParameter
if (typeParameter != null) {
val upperBoundType = typeParameter.eraseUpperBoundType()
return if (this.isMarkedNullable())
upperBoundType.makeNullable()
else
upperBoundType
}
return this
}
private fun IrTypeParameter.eraseUpperBoundType(): IrType {
return superTypes.firstOrNull()?.eraseUpperBoundType() ?: context.irBuiltIns.anyNType
}
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
}
}

View File

@@ -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()

View File

@@ -0,0 +1,51 @@
/*
* 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.FunctionLoweringPass
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.backend.js.utils.realOverrideTarget
import org.jetbrains.kotlin.ir.builders.createTmpVariable
import org.jetbrains.kotlin.ir.builders.irGet
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.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) : FunctionLoweringPass {
override fun lower(irFunction: IrFunction) {
val builder by lazy { context.createIrBuilder(irFunction.symbol) }
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 !is IrSimpleFunction || !function.isOverridable)
return expression
// TODO: Keep other simple receivers without side effects
if (receiver is IrGetValue)
return expression
return with(builder) {
irBlock(expression) {
val tmp = createTmpVariable(receiver)
expression.dispatchReceiver = irGet(tmp)
+expression
}
}
}
})
}
}

View File

@@ -0,0 +1,54 @@
/*
* 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.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: CommonBackendContext) : 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()
}

View File

@@ -0,0 +1,310 @@
/*
* 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.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.declarations.impl.IrClassImpl
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: This is a partial copy of JVM callable reference lowering. This should be reimplemented.
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<IrCallableReference>()
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 = buildClass {
setSourceRange(irFunctionReference)
visibility = Visibilities.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 as IrClassImpl).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) }
}
})
}
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -0,0 +1,277 @@
/*
* 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))
}
}
@Suppress("DuplicatedCode")
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)) {
return { value.deepCopyWithSymbols() as IrExpressionWithCopy }
}
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 = valueProvider()
val fromType = valueInstance.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
isToNullable ->
builder.irIfThenElse(
type = builtIns.booleanType,
condition = builder.irEqualsNull(valueProvider()),
thenPart = builder.irTrue(),
elsePart = instanceCheck
)
else ->
builder.irIfThenElse(
type = builtIns.booleanType,
condition = builder.irNot(builder.irEqualsNull(valueProvider())),
thenPart = instanceCheck,
elsePart = builder.irFalse()
)
}
}
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
+builder.irFalse()
}
toType.isTypeParameter() -> generateTypeCheckWithTypeParameter(argument, toType)
toType.isInterface() -> generateIsInterface(argument, toType)
else -> generateIsSubClass(argument, 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
}
return builder.irCall(symbols.structNarrow, 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())
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(), 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)
}
}
}

View File

@@ -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.irTemporaryVar
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 = irTemporaryVar(
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
}
}

View File

@@ -11,19 +11,36 @@ import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.util.getAnnotation
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.wasm.ir.WasmImportPair
fun IrAnnotationContainer.hasExcludedFromCodegenAnnotation(): Boolean =
hasAnnotation(FqName("kotlin.wasm.internal.ExcludedFromCodegen"))
fun IrAnnotationContainer.getWasmInstructionAnnotation(): String? =
getAnnotation(FqName("kotlin.wasm.internal.WasmInstruction"))?.getSingleConstStringArgument()
fun IrAnnotationContainer.getWasmBinaryOpAnnotation(): String? =
getAnnotation(FqName("kotlin.wasm.internal.WasmBinaryOp"))?.getSingleConstStringArgument()
fun IrAnnotationContainer.getWasmRefOpAnnotation(): String? =
getAnnotation(FqName("kotlin.wasm.internal.WasmRefOp"))?.getSingleConstStringArgument()
fun IrAnnotationContainer.getWasmUnaryOpAnnotation(): String? =
getAnnotation(FqName("kotlin.wasm.internal.WasmUnaryOp"))?.getSingleConstStringArgument()
fun IrAnnotationContainer.getWasmLoadOpAnnotation(): String? =
getAnnotation(FqName("kotlin.wasm.internal.WasmLoadOp"))?.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? =
getAnnotation(FqName("kotlin.wasm.internal.WasmImport"))?.let {
WasmImportPair(
(it.getValueArgument(0) as IrConst<String>).value,
(it.getValueArgument(1) as IrConst<String>).value
(it.getValueArgument(0) as IrConst<*>).value as String,
(it.getValueArgument(1) as IrConst<*>).value as String
)
}

View File

@@ -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
}

View File

@@ -9,52 +9,7 @@ import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.isMarkedNullable
/**
* Returns inline class for given class or null of type is not inlined
* TODO: Make this configurable for different backends (currently implements logic of JS BE)
*/
fun IrType.getInlinedClass(): IrClass? {
if (this is IrSimpleType) {
val erased = erase(this) ?: return null
if (erased.isInline) {
if (this.isMarkedNullable()) {
var fieldType: IrType
var fieldInlinedClass = erased
while (true) {
fieldType = getInlineClassUnderlyingType(fieldInlinedClass)
if (fieldType.isMarkedNullable()) {
return null
}
fieldInlinedClass = fieldType.getInlinedClass() ?: break
}
}
return erased
}
}
return null
}
fun IrType.isInlined(): Boolean = this.getInlinedClass() != null
private tailrec fun erase(type: IrType): IrClass? {
val classifier = type.classifierOrFail
return when (classifier) {
is IrClassSymbol -> classifier.owner
is IrTypeParameterSymbol -> erase(classifier.owner.superTypes.first())
else -> error(classifier)
}
}
fun getInlineClassUnderlyingType(irClass: IrClass): IrType {
for (declaration in irClass.declarations) {

View File

@@ -371,12 +371,12 @@ tailrec fun IrElement.getPackageFragment(): IrPackageFragment? {
fun IrAnnotationContainer.getAnnotation(name: FqName): IrConstructorCall? =
annotations.find {
it.symbol.owner.parentAsClass.descriptor.fqNameSafe == name
it.symbol.owner.parentAsClass.fqNameWhenAvailable == name
}
fun IrAnnotationContainer.hasAnnotation(name: FqName) =
annotations.any {
it.symbol.owner.parentAsClass.descriptor.fqNameSafe == name
it.symbol.owner.parentAsClass.fqNameWhenAvailable == name
}
fun IrAnnotationContainer.hasAnnotation(symbol: IrClassSymbol) =
@@ -524,14 +524,16 @@ fun irCall(
newFunction: IrFunction,
receiversAsArguments: Boolean = false,
argumentsAsReceivers: Boolean = false,
newSuperQualifierSymbol: IrClassSymbol? = null
newSuperQualifierSymbol: IrClassSymbol? = null,
newReturnType: IrType? = null
): IrCall =
irCall(
call,
newFunction.symbol,
receiversAsArguments,
argumentsAsReceivers,
newSuperQualifierSymbol
newSuperQualifierSymbol,
newReturnType
)
fun irCall(
@@ -539,13 +541,14 @@ fun irCall(
newSymbol: IrFunctionSymbol,
receiversAsArguments: Boolean = false,
argumentsAsReceivers: Boolean = false,
newSuperQualifierSymbol: IrClassSymbol? = null
newSuperQualifierSymbol: IrClassSymbol? = null,
newReturnType: IrType? = null
): IrCall =
call.run {
IrCallImpl(
startOffset,
endOffset,
type,
newReturnType ?: type,
newSymbol,
typeArgumentsCount,
origin,

View File

@@ -2,6 +2,7 @@
// IGNORE_BACKEND: JS_IR
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// DONT_TARGET_EXACT_BACKEND: WASM
// WITH_REFLECT

View File

@@ -1,6 +1,7 @@
// IGNORE_BACKEND: JS_IR
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// DONT_TARGET_EXACT_BACKEND: WASM
fun foo(x: Cloneable) = x

View File

@@ -1,6 +1,7 @@
// IGNORE_BACKEND: JS_IR
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// DONT_TARGET_EXACT_BACKEND: WASM
// WITH_RUNTIME

View File

@@ -1,6 +1,7 @@
// IGNORE_BACKEND: JS_IR
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// DONT_TARGET_EXACT_BACKEND: WASM
// WITH_RUNTIME

View File

@@ -11,3 +11,8 @@ fun box(): String {
s[2, -2] += "K"
return s[2, -2]
}
// DONT_TARGET_EXACT_BACKEND: WASM
//DONT_TARGET_WASM_REASON: STDLIB_ARRAY_LIST

View File

@@ -10,3 +10,8 @@ fun box(): String {
s[1, -1] = "OK"
return s[2, -2]
}
// DONT_TARGET_EXACT_BACKEND: WASM
//DONT_TARGET_WASM_REASON: STDLIB_ARRAY_LIST

View File

@@ -15,4 +15,7 @@ fun box(): String {
assertEquals(4321, sum)
return "OK"
}
}
// DONT_TARGET_EXACT_BACKEND: WASM
//DONT_TARGET_WASM_REASON: UNIT

View File

@@ -15,4 +15,8 @@ fun box(): String {
assertEquals(1234, sum)
return "OK"
}
}
// DONT_TARGET_EXACT_BACKEND: WASM
//DONT_TARGET_WASM_REASON: UNIT

View File

@@ -8,3 +8,8 @@ fun box(): String {
s["239"] = 239
return if (s["239"] == 239) "OK" else "Fail"
}
// DONT_TARGET_EXACT_BACKEND: WASM
//DONT_TARGET_WASM_REASON: STDLIB_HASH_MAP

View File

@@ -6,3 +6,8 @@ fun box () : String {
s[0] += "bar"
return if(s[0] == "foobar") "OK" else "fail"
}
// DONT_TARGET_EXACT_BACKEND: WASM
//DONT_TARGET_WASM_REASON: STDLIB_ARRAY_LIST

View File

@@ -31,3 +31,8 @@ fun box() : String {
fun thirdElementIsThree(a : IntArray) =
a.size >= 3 && a[2] == 3
// DONT_TARGET_EXACT_BACKEND: WASM
//DONT_TARGET_WASM_REASON: IR_TRY

View File

@@ -14,3 +14,6 @@ fun box() : String {
return "OK"
}
// DONT_TARGET_EXACT_BACKEND: WASM
//DONT_TARGET_WASM_REASON: UNIT

View File

@@ -2,6 +2,7 @@
// IGNORE_BACKEND: JS_IR
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS
// DONT_TARGET_EXACT_BACKEND: WASM
// WITH_RUNTIME

View File

@@ -6,3 +6,8 @@ fun box() : String {
map["239"] = "932"
return if(map["239"] == "932") "OK" else "fail"
}
// DONT_TARGET_EXACT_BACKEND: WASM
//DONT_TARGET_WASM_REASON: STDLIB_HASH_MAP

View File

@@ -75,3 +75,6 @@ fun box(): String {
testDoubleArray()
return "OK"
}
// DONT_TARGET_EXACT_BACKEND: WASM
//DONT_TARGET_WASM_REASON: UNIT

View File

@@ -192,4 +192,6 @@ fun checkExactArrayType(
a is FloatArray == floatArray &&
a is DoubleArray == doubleArray &&
a is Array<*> == array
}
}
// DONT_TARGET_EXACT_BACKEND: WASM
//DONT_TARGET_WASM_REASON: KOTLIN_TEST_LIB

View File

@@ -22,4 +22,6 @@ fun box(): String {
val s1 = expectFail { "${2 * (1 / 0) }" } ?: "OK"
return s1
}
}
// DONT_TARGET_EXACT_BACKEND: WASM
//DONT_TARGET_WASM_REASON: IR_TRY

View File

@@ -1,6 +1,7 @@
// IGNORE_BACKEND: JS_IR
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// DONT_TARGET_EXACT_BACKEND: WASM
// WITH_RUNTIME

View File

@@ -6,3 +6,6 @@ fun box() : String {
null?.toString()
}
}
// DONT_TARGET_EXACT_BACKEND: WASM
//DONT_TARGET_WASM_REASON: IR_TRY

View File

@@ -8,3 +8,9 @@ fun box(): String {
assertEquals(400L, x.first())
return "OK"
}
// DONT_TARGET_EXACT_BACKEND: WASM
//DONT_TARGET_WASM_REASON: STDLIB_ARRAY_LIST

View File

@@ -32,3 +32,6 @@ fun box() : String {
return "OK"
}
// DONT_TARGET_EXACT_BACKEND: WASM
//DONT_TARGET_WASM_REASON: STDLIB_COLLECTIONS

View File

@@ -32,3 +32,6 @@ fun box(): String {
r += a.foo()[0]
return if (r == "BBBBB") "OK" else "Fail: $r"
}
// DONT_TARGET_EXACT_BACKEND: WASM
//DONT_TARGET_WASM_REASON: STDLIB_GENERATED

View File

@@ -21,3 +21,6 @@ fun box(): String {
}
return "Fail"
}
// DONT_TARGET_EXACT_BACKEND: WASM
//DONT_TARGET_WASM_REASON: IR_TRY

View File

@@ -20,3 +20,6 @@ fun box(): String {
(C() as B2).f("OK")
return result
}
// DONT_TARGET_EXACT_BACKEND: WASM
//DONT_TARGET_WASM_REASON: IGNORED_IN_JS

View File

@@ -21,3 +21,6 @@ fun box(): String {
if (a.get(0) != "StringList.get()") return "Fail #3"
return "OK"
}
// DONT_TARGET_EXACT_BACKEND: WASM
//DONT_TARGET_WASM_REASON: STDLIB_ARRAY_LIST

View File

@@ -22,3 +22,6 @@ fun box(): String {
r += a.foo().iterator().next()
return if (r == "CCC") "OK" else "Fail: $r"
}
// DONT_TARGET_EXACT_BACKEND: WASM
//DONT_TARGET_WASM_REASON: STDLIB_GENERATED

View File

@@ -33,3 +33,6 @@ fun box(): String {
return "OK"
}
// DONT_TARGET_EXACT_BACKEND: WASM
//DONT_TARGET_WASM_REASON: BRIDGE_ISSUES

View File

@@ -15,3 +15,6 @@ fun box(): String {
bar(::foo)
return "OK"
}
// DONT_TARGET_EXACT_BACKEND: WASM
//DONT_TARGET_WASM_REASON: IGNORED_IN_JS

View File

@@ -46,3 +46,8 @@ fun box(): String {
return "OK"
}
// DONT_TARGET_EXACT_BACKEND: WASM
//DONT_TARGET_WASM_REASON: IGNORED_IN_JS

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