feat(polyfills): use stdlib for js polyfills.
This commit is contained in:
Generated
+1
-1
@@ -95,4 +95,4 @@
|
|||||||
<component name="intellij-api-watcher-check-current-project">
|
<component name="intellij-api-watcher-check-current-project">
|
||||||
<option name="shouldCheckCurrentProject" value="true" />
|
<option name="shouldCheckCurrentProject" value="true" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -36,11 +36,14 @@ import org.jetbrains.kotlin.incremental.js.IncrementalDataProvider
|
|||||||
import org.jetbrains.kotlin.incremental.js.IncrementalNextRoundChecker
|
import org.jetbrains.kotlin.incremental.js.IncrementalNextRoundChecker
|
||||||
import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumer
|
import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumer
|
||||||
import org.jetbrains.kotlin.ir.backend.js.*
|
import org.jetbrains.kotlin.ir.backend.js.*
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.ic.actualizeCacheForModule
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.ic.buildCache
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.ic.checkCaches
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
|
||||||
import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity
|
import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity
|
||||||
import org.jetbrains.kotlin.ir.backend.js.ic.*
|
import org.jetbrains.kotlin.ir.backend.js.ic.*
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformerTmp
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformerTmp
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.persistent.PersistentIrFactory
|
import org.jetbrains.kotlin.ir.declarations.persistent.PersistentIrFactory
|
||||||
import org.jetbrains.kotlin.js.analyzer.JsAnalysisResult
|
import org.jetbrains.kotlin.js.analyzer.JsAnalysisResult
|
||||||
import org.jetbrains.kotlin.js.config.*
|
import org.jetbrains.kotlin.js.config.*
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
|
|||||||
|
|
||||||
// TODO: Should we drop operator intrinsics in favor of IrDynamicOperatorExpression?
|
// TODO: Should we drop operator intrinsics in favor of IrDynamicOperatorExpression?
|
||||||
|
|
||||||
|
// Global variables
|
||||||
|
val globalThis = getInternalProperty("globalThis")
|
||||||
|
|
||||||
// Equality operations:
|
// Equality operations:
|
||||||
|
|
||||||
val jsEqeq = getInternalFunction("jsEqeq")
|
val jsEqeq = getInternalFunction("jsEqeq")
|
||||||
@@ -328,6 +331,9 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
|
|||||||
private fun getInternalFunction(name: String) =
|
private fun getInternalFunction(name: String) =
|
||||||
context.symbolTable.referenceSimpleFunction(context.getJsInternalFunction(name))
|
context.symbolTable.referenceSimpleFunction(context.getJsInternalFunction(name))
|
||||||
|
|
||||||
|
private fun getInternalProperty(name: String) =
|
||||||
|
context.symbolTable.referenceProperty(context.getJsInternalProperty(name))
|
||||||
|
|
||||||
private fun getInternalWithoutPackage(name: String) =
|
private fun getInternalWithoutPackage(name: String) =
|
||||||
context.symbolTable.referenceSimpleFunction(context.getFunctions(FqName(name)).single())
|
context.symbolTable.referenceSimpleFunction(context.getFunctions(FqName(name)).single())
|
||||||
|
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ class JsIrBackendContext(
|
|||||||
// TODO: what is more clear way reference this getter?
|
// TODO: what is more clear way reference this getter?
|
||||||
private val REFLECT_PACKAGE_FQNAME = KOTLIN_PACKAGE_FQN.child(Name.identifier("reflect"))
|
private val REFLECT_PACKAGE_FQNAME = KOTLIN_PACKAGE_FQN.child(Name.identifier("reflect"))
|
||||||
private val JS_PACKAGE_FQNAME = KOTLIN_PACKAGE_FQN.child(Name.identifier("js"))
|
private val JS_PACKAGE_FQNAME = KOTLIN_PACKAGE_FQN.child(Name.identifier("js"))
|
||||||
|
private val JS_POLYFILLS_PACKAGE = JS_PACKAGE_FQNAME.child(Name.identifier("polyfill"))
|
||||||
private val JS_INTERNAL_PACKAGE_FQNAME = JS_PACKAGE_FQNAME.child(Name.identifier("internal"))
|
private val JS_INTERNAL_PACKAGE_FQNAME = JS_PACKAGE_FQNAME.child(Name.identifier("internal"))
|
||||||
|
|
||||||
// TODO: due to name clash those weird suffix is required, remove it once `MemberNameGenerator` is implemented
|
// TODO: due to name clash those weird suffix is required, remove it once `MemberNameGenerator` is implemented
|
||||||
@@ -334,6 +335,10 @@ class JsIrBackendContext(
|
|||||||
internal fun getJsInternalFunction(name: String): SimpleFunctionDescriptor =
|
internal fun getJsInternalFunction(name: String): SimpleFunctionDescriptor =
|
||||||
findFunctions(internalPackage.memberScope, Name.identifier(name)).singleOrNull() ?: error("Internal function '$name' not found")
|
findFunctions(internalPackage.memberScope, Name.identifier(name)).singleOrNull() ?: error("Internal function '$name' not found")
|
||||||
|
|
||||||
|
internal fun getJsInternalProperty(name: String): PropertyDescriptor =
|
||||||
|
findProperty(internalPackage.memberScope, Name.identifier(name)).singleOrNull() ?: error("Internal function '$name' not found")
|
||||||
|
|
||||||
|
|
||||||
fun getFunctions(fqName: FqName): List<SimpleFunctionDescriptor> =
|
fun getFunctions(fqName: FqName): List<SimpleFunctionDescriptor> =
|
||||||
findFunctions(module.getPackage(fqName.parent()).memberScope, fqName.shortName())
|
findFunctions(module.getPackage(fqName.parent()).memberScope, fqName.shortName())
|
||||||
|
|
||||||
|
|||||||
@@ -784,6 +784,12 @@ private val captureStackTraceInThrowablesPhase = makeBodyLoweringPhase(
|
|||||||
description = "Capture stack trace in Throwable constructors"
|
description = "Capture stack trace in Throwable constructors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private val escapedIdentifiersLowering = makeBodyLoweringPhase(
|
||||||
|
::EscapedIdentifiersLowering,
|
||||||
|
name = "EscapedIdentifiersLowering",
|
||||||
|
description = "Convert global variables with invalid names access to globalThis member expression"
|
||||||
|
)
|
||||||
|
|
||||||
private val cleanupLoweringPhase = makeBodyLoweringPhase(
|
private val cleanupLoweringPhase = makeBodyLoweringPhase(
|
||||||
{ CleanupLowering() },
|
{ CleanupLowering() },
|
||||||
name = "CleanupLowering",
|
name = "CleanupLowering",
|
||||||
@@ -903,6 +909,7 @@ val loweringList = listOf<Lowering>(
|
|||||||
objectUsageLoweringPhase,
|
objectUsageLoweringPhase,
|
||||||
captureStackTraceInThrowablesPhase,
|
captureStackTraceInThrowablesPhase,
|
||||||
callsLoweringPhase,
|
callsLoweringPhase,
|
||||||
|
escapedIdentifiersLowering,
|
||||||
cleanupLoweringPhase,
|
cleanupLoweringPhase,
|
||||||
// Currently broken due to static members lowering making single-open-class
|
// Currently broken due to static members lowering making single-open-class
|
||||||
// files non-recognizable as single-class files
|
// files non-recognizable as single-class files
|
||||||
|
|||||||
+162
@@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 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.runOnFilePostfix
|
||||||
|
import org.jetbrains.kotlin.config.LanguageFeature
|
||||||
|
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||||
|
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.utils.hasStableJsName
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
|
||||||
|
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
|
import org.jetbrains.kotlin.js.common.isValidES5Identifier
|
||||||
|
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||||
|
import org.jetbrains.kotlin.name.SpecialNames
|
||||||
|
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||||
|
|
||||||
|
class EscapedIdentifiersLowering(context: JsIrBackendContext) : BodyLoweringPass {
|
||||||
|
private val transformer = ReferenceTransformer(context)
|
||||||
|
private val moduleKind = context.configuration[JSConfigurationKeys.MODULE_KIND]!!
|
||||||
|
private val isEscapedIdentifiersResolved =
|
||||||
|
context.configuration.languageVersionSettings.supportsFeature(LanguageFeature.JsAllowInvalidCharsIdentifiersEscaping)
|
||||||
|
|
||||||
|
override fun lower(irFile: IrFile) {
|
||||||
|
if (!isEscapedIdentifiersResolved || moduleKind != ModuleKind.PLAIN) return
|
||||||
|
runOnFilePostfix(irFile, withLocalDeclarations = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun lower(irBody: IrBody, container: IrDeclaration) {
|
||||||
|
if (!isEscapedIdentifiersResolved || moduleKind != ModuleKind.PLAIN) return
|
||||||
|
irBody.transformChildrenVoid(transformer)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ReferenceTransformer(val context: JsIrBackendContext) : IrElementTransformerVoid() {
|
||||||
|
private val globalThisReceiver
|
||||||
|
get() = IrCallImpl(
|
||||||
|
startOffset = UNDEFINED_OFFSET,
|
||||||
|
endOffset = UNDEFINED_OFFSET,
|
||||||
|
type = context.dynamicType,
|
||||||
|
symbol = context.intrinsics.globalThis.owner.getter!!.symbol,
|
||||||
|
typeArgumentsCount = 0,
|
||||||
|
valueArgumentsCount = 0,
|
||||||
|
)
|
||||||
|
|
||||||
|
private val IrFunction.dummyDispatchReceiverParameter
|
||||||
|
get() = context.irFactory.createValueParameter(
|
||||||
|
startOffset, endOffset,
|
||||||
|
origin,
|
||||||
|
IrValueParameterSymbolImpl(),
|
||||||
|
SpecialNames.THIS,
|
||||||
|
-1,
|
||||||
|
context.irBuiltIns.anyType,
|
||||||
|
null,
|
||||||
|
isCrossinline = false,
|
||||||
|
isNoinline = false,
|
||||||
|
isHidden = false,
|
||||||
|
isAssignable = false
|
||||||
|
).also { it.parent = this }
|
||||||
|
|
||||||
|
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||||
|
val owner = expression.symbol.owner
|
||||||
|
|
||||||
|
return if (
|
||||||
|
!owner.isEffectivelyExternal() ||
|
||||||
|
owner.isThisReceiver() ||
|
||||||
|
!owner.needToBeWrappedWithGlobalThis()
|
||||||
|
) {
|
||||||
|
super.visitGetValue(expression)
|
||||||
|
} else {
|
||||||
|
owner.wrapInGlobalThis(expression)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitSetValue(expression: IrSetValue): IrExpression {
|
||||||
|
val field = expression.symbol.owner
|
||||||
|
|
||||||
|
return if (
|
||||||
|
!field.isEffectivelyExternal() ||
|
||||||
|
!field.needToBeWrappedWithGlobalThis()
|
||||||
|
) {
|
||||||
|
super.visitSetValue(expression)
|
||||||
|
} else {
|
||||||
|
IrSetFieldImpl(
|
||||||
|
startOffset = expression.startOffset,
|
||||||
|
endOffset = expression.endOffset,
|
||||||
|
receiver = field.wrapInGlobalThis(expression),
|
||||||
|
value = expression.value,
|
||||||
|
type = expression.type,
|
||||||
|
origin = null,
|
||||||
|
superQualifierSymbol = null,
|
||||||
|
symbol = IrFieldSymbolImpl(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression {
|
||||||
|
val owner = expression.symbol.owner
|
||||||
|
|
||||||
|
return if (
|
||||||
|
!owner.isEffectivelyExternal() ||
|
||||||
|
!owner.needToBeWrappedWithGlobalThis()
|
||||||
|
) {
|
||||||
|
super.visitGetObjectValue(expression)
|
||||||
|
} else {
|
||||||
|
owner.wrapInGlobalThis(expression)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitCall(expression: IrCall): IrExpression {
|
||||||
|
val function = expression.symbol.owner.realOverrideTarget
|
||||||
|
val property = function.correspondingPropertySymbol?.owner ?: function
|
||||||
|
|
||||||
|
val updatedCall = if (
|
||||||
|
expression.dispatchReceiver != null ||
|
||||||
|
!property.isEffectivelyExternal() ||
|
||||||
|
!property.needToBeWrappedWithGlobalThis()
|
||||||
|
) {
|
||||||
|
expression
|
||||||
|
} else {
|
||||||
|
expression
|
||||||
|
.apply { dispatchReceiver = globalThisReceiver }
|
||||||
|
.also {
|
||||||
|
if (function.dispatchReceiverParameter == null) {
|
||||||
|
function.dispatchReceiverParameter = function.dummyDispatchReceiverParameter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.visitCall(updatedCall)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IrDeclarationWithName.needToBeWrappedWithGlobalThis(): Boolean =
|
||||||
|
!getJsNameOrKotlinName().toString().isValidES5Identifier()
|
||||||
|
|
||||||
|
private fun IrDeclarationWithName.wrapInGlobalThis(expression: IrExpression): IrDynamicMemberExpression =
|
||||||
|
IrDynamicMemberExpressionImpl(
|
||||||
|
startOffset = expression.startOffset,
|
||||||
|
endOffset = expression.endOffset,
|
||||||
|
type = expression.type,
|
||||||
|
memberName = getJsNameOrKotlinName().asString(),
|
||||||
|
receiver = globalThisReceiver
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun IrValueDeclaration.isThisReceiver(): Boolean = this !is IrVariable && when (val p = parent) {
|
||||||
|
is IrSimpleFunction -> this === p.dispatchReceiverParameter
|
||||||
|
is IrClass -> this === p.thisReceiver
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
-8
@@ -111,6 +111,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
|||||||
expression
|
expression
|
||||||
)
|
)
|
||||||
return JsNameRef(field.getJsNameOrKotlinName().identifier, receiver).withSource(expression, context)
|
return JsNameRef(field.getJsNameOrKotlinName().identifier, receiver).withSource(expression, context)
|
||||||
|
.also { context.staticContext.polyfills.visitDeclaration(field) }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fieldParent is IrClass && fieldParent.isInline) {
|
if (fieldParent is IrClass && fieldParent.isInline) {
|
||||||
@@ -121,9 +122,11 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitGetValue(expression: IrGetValue, context: JsGenerationContext): JsExpression {
|
override fun visitGetValue(expression: IrGetValue, context: JsGenerationContext): JsExpression {
|
||||||
if (expression.symbol.owner.isThisReceiver()) return JsThisRef().withSource(expression, context)
|
val owner = expression.symbol.owner
|
||||||
val possibleGlobalVarRef = context.getNameForValueDeclaration(expression.symbol.owner).makeRef().withSource(expression, context)
|
if (owner.isThisReceiver()) return JsThisRef().withSource(expression, context)
|
||||||
return jsGlobalVarRef(possibleGlobalVarRef)
|
|
||||||
|
return context.getNameForValueDeclaration(owner).makeRef().withSource(expression, context)
|
||||||
|
.also { context.staticContext.polyfills.visitDeclaration(owner) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitGetObjectValue(expression: IrGetObjectValue, context: JsGenerationContext): JsExpression {
|
override fun visitGetObjectValue(expression: IrGetObjectValue, context: JsGenerationContext): JsExpression {
|
||||||
@@ -131,22 +134,24 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
|||||||
assert(obj.kind == ClassKind.OBJECT)
|
assert(obj.kind == ClassKind.OBJECT)
|
||||||
assert(obj.isEffectivelyExternal()) { "Non external IrGetObjectValue must be lowered" }
|
assert(obj.isEffectivelyExternal()) { "Non external IrGetObjectValue must be lowered" }
|
||||||
|
|
||||||
val possibleGlobalVarRef = context.getRefForExternalClass(obj).withSource(expression, context)
|
return context.getRefForExternalClass(obj).withSource(expression, context)
|
||||||
return jsGlobalVarRef(possibleGlobalVarRef)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitSetField(expression: IrSetField, context: JsGenerationContext): JsExpression {
|
override fun visitSetField(expression: IrSetField, context: JsGenerationContext): JsExpression {
|
||||||
val fieldName = context.getNameForField(expression.symbol.owner)
|
val field = expression.symbol.owner
|
||||||
|
val fieldName = context.getNameForField(field)
|
||||||
val dest = jsElementAccess(fieldName.ident, expression.receiver?.accept(this, context))
|
val dest = jsElementAccess(fieldName.ident, expression.receiver?.accept(this, context))
|
||||||
val source = expression.value.accept(this, context)
|
val source = expression.value.accept(this, context)
|
||||||
return jsAssignment(dest, source).withSource(expression, context)
|
return jsAssignment(dest, source).withSource(expression, context)
|
||||||
|
.also { context.staticContext.polyfills.visitDeclaration(field) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitSetValue(expression: IrSetValue, context: JsGenerationContext): JsExpression {
|
override fun visitSetValue(expression: IrSetValue, context: JsGenerationContext): JsExpression {
|
||||||
val possibleGlobalRef = JsNameRef(context.getNameForValueDeclaration(expression.symbol.owner))
|
val field = expression.symbol.owner
|
||||||
val ref = jsGlobalVarRef(possibleGlobalRef)
|
val ref = JsNameRef(context.getNameForValueDeclaration(field))
|
||||||
val value = expression.value.accept(this, context)
|
val value = expression.value.accept(this, context)
|
||||||
return JsBinaryOperation(JsBinaryOperator.ASG, ref, value).withSource(expression, context)
|
return JsBinaryOperation(JsBinaryOperator.ASG, ref, value).withSource(expression, context)
|
||||||
|
.also { context.staticContext.polyfills.visitDeclaration(field) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, context: JsGenerationContext): JsExpression {
|
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, context: JsGenerationContext): JsExpression {
|
||||||
@@ -250,6 +255,10 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
|||||||
|
|
||||||
}
|
}
|
||||||
return translateCall(expression, context, this).withSource(expression, context)
|
return translateCall(expression, context, this).withSource(expression, context)
|
||||||
|
.also {
|
||||||
|
val function = expression.symbol.owner
|
||||||
|
context.staticContext.polyfills.visitDeclaration(function.correspondingPropertySymbol?.owner ?: function)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitWhen(expression: IrWhen, context: JsGenerationContext): JsExpression {
|
override fun visitWhen(expression: IrWhen, context: JsGenerationContext): JsExpression {
|
||||||
|
|||||||
+4
-1
@@ -84,9 +84,11 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsSta
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitSetValue(expression: IrSetValue, context: JsGenerationContext): JsStatement {
|
override fun visitSetValue(expression: IrSetValue, context: JsGenerationContext): JsStatement {
|
||||||
val ref = JsNameRef(context.getNameForValueDeclaration(expression.symbol.owner))
|
val owner = expression.symbol.owner
|
||||||
|
val ref = JsNameRef(context.getNameForValueDeclaration(owner))
|
||||||
return expression.value
|
return expression.value
|
||||||
.maybeOptimizeIntoSwitch(context) { jsAssignment(ref, it).withSource(expression, context).makeStmt() }
|
.maybeOptimizeIntoSwitch(context) { jsAssignment(ref, it).withSource(expression, context).makeStmt() }
|
||||||
|
.also { context.staticContext.polyfills.visitDeclaration(owner) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitReturn(expression: IrReturn, context: JsGenerationContext): JsStatement {
|
override fun visitReturn(expression: IrReturn, context: JsGenerationContext): JsStatement {
|
||||||
@@ -153,6 +155,7 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsSta
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return translateCall(expression, data, IrElementToJsExpressionTransformer()).withSource(expression, data).makeStmt()
|
return translateCall(expression, data, IrElementToJsExpressionTransformer()).withSource(expression, data).makeStmt()
|
||||||
|
.also { data.staticContext.polyfills.visitDeclaration(expression.symbol.owner) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitInstanceInitializerCall(expression: IrInstanceInitializerCall, context: JsGenerationContext): JsStatement {
|
override fun visitInstanceInitializerCall(expression: IrInstanceInitializerCall, context: JsGenerationContext): JsStatement {
|
||||||
|
|||||||
+5
-7
@@ -42,8 +42,7 @@ class IrModuleToJsTransformer(
|
|||||||
private val multiModule: Boolean = false,
|
private val multiModule: Boolean = false,
|
||||||
private val relativeRequirePath: Boolean = false,
|
private val relativeRequirePath: Boolean = false,
|
||||||
private val moduleToName: Map<IrModuleFragment, String> = emptyMap(),
|
private val moduleToName: Map<IrModuleFragment, String> = emptyMap(),
|
||||||
private val removeUnusedAssociatedObjects: Boolean = true,
|
private val removeUnusedAssociatedObjects: Boolean = true
|
||||||
private val generateGlobalThisPolyfill: Boolean = false
|
|
||||||
) {
|
) {
|
||||||
private val generateRegionComments = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_REGION_COMMENTS)
|
private val generateRegionComments = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_REGION_COMMENTS)
|
||||||
|
|
||||||
@@ -152,7 +151,6 @@ class IrModuleToJsTransformer(
|
|||||||
)
|
)
|
||||||
|
|
||||||
val moduleBody = generateModuleBody(modules, staticContext)
|
val moduleBody = generateModuleBody(modules, staticContext)
|
||||||
|
|
||||||
val internalModuleName = JsName("_", false)
|
val internalModuleName = JsName("_", false)
|
||||||
val globalNames = NameTable<String>(namer.globalNames)
|
val globalNames = NameTable<String>(namer.globalNames)
|
||||||
val exportStatements = ExportModelToJsStatements(nameGenerator) { globalNames.declareFreshName(it, it) }
|
val exportStatements = ExportModelToJsStatements(nameGenerator) { globalNames.declareFreshName(it, it) }
|
||||||
@@ -173,6 +171,7 @@ class IrModuleToJsTransformer(
|
|||||||
parameters += JsParameter(internalModuleName)
|
parameters += JsParameter(internalModuleName)
|
||||||
parameters += (importedJsModules + importedKotlinModules).map { JsParameter(it.internalName) }
|
parameters += (importedJsModules + importedKotlinModules).map { JsParameter(it.internalName) }
|
||||||
with(body) {
|
with(body) {
|
||||||
|
statements += JsStringLiteral("use strict").makeStmt()
|
||||||
statements.addWithComment("block: imports", importStatements + crossModuleImports)
|
statements.addWithComment("block: imports", importStatements + crossModuleImports)
|
||||||
statements += moduleBody
|
statements += moduleBody
|
||||||
statements.addWithComment("block: exports", exportStatements + crossModuleExports)
|
statements.addWithComment("block: exports", exportStatements + crossModuleExports)
|
||||||
@@ -221,6 +220,7 @@ class IrModuleToJsTransformer(
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
staticContext.polyfills.addAllNeededPolyfillsTo(jsCode)
|
||||||
program.accept(JsToStringGenerationVisitor(jsCode, sourceMapBuilderConsumer ?: NoOpSourceLocationConsumer))
|
program.accept(JsToStringGenerationVisitor(jsCode, sourceMapBuilderConsumer ?: NoOpSourceLocationConsumer))
|
||||||
|
|
||||||
return CompilationOutputs(
|
return CompilationOutputs(
|
||||||
@@ -284,12 +284,10 @@ class IrModuleToJsTransformer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun generateModuleBody(modules: Iterable<IrModuleFragment>, staticContext: JsStaticContext): List<JsStatement> {
|
private fun generateModuleBody(modules: Iterable<IrModuleFragment>, staticContext: JsStaticContext): List<JsStatement> {
|
||||||
val statements = mutableListOf<JsStatement>().also {
|
val statements = mutableListOf<JsStatement>()
|
||||||
if (!generateScriptModule) it += JsStringLiteral("use strict").makeStmt()
|
|
||||||
if (generateGlobalThisPolyfill) it += jsGlobalThisPolyfill()
|
|
||||||
}
|
|
||||||
|
|
||||||
val preDeclarationBlock = JsGlobalBlock()
|
val preDeclarationBlock = JsGlobalBlock()
|
||||||
|
|
||||||
val postDeclarationBlock = JsGlobalBlock()
|
val postDeclarationBlock = JsGlobalBlock()
|
||||||
|
|
||||||
statements.addWithComment("block: pre-declaration", preDeclarationBlock)
|
statements.addWithComment("block: pre-declaration", preDeclarationBlock)
|
||||||
|
|||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 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.transformers.irToJs
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNativeImplementation
|
||||||
|
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
||||||
|
import org.jetbrains.kotlin.js.util.TextOutput
|
||||||
|
|
||||||
|
class JsPolyfillsVisitor {
|
||||||
|
private val polyfills = mutableSetOf<String>()
|
||||||
|
|
||||||
|
fun visitDeclaration(declaration: IrDeclaration) {
|
||||||
|
if (!declaration.isEffectivelyExternal()) return
|
||||||
|
val implementation = declaration.getJsNativeImplementation() ?: return
|
||||||
|
polyfills.add(implementation)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addAllNeededPolyfillsTo(output: TextOutput) {
|
||||||
|
if (polyfills.isEmpty()) return
|
||||||
|
output.print("// region block: polyfills")
|
||||||
|
output.print(polyfills.joinToString("\n"))
|
||||||
|
output.print("// endregion\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-28
@@ -45,25 +45,6 @@ fun <T : JsNode> IrWhen.toJsNode(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://mathiasbynens.be/notes/globalthis
|
|
||||||
// TODO: add DCE for globalThis polyfill declaration
|
|
||||||
fun jsGlobalThisPolyfill(): List<JsStatement> =
|
|
||||||
parseJsCode(
|
|
||||||
"""
|
|
||||||
(function() {
|
|
||||||
if (typeof globalThis === 'object') return;
|
|
||||||
Object.defineProperty(Object.prototype, '__magic__', {
|
|
||||||
get: function() {
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
configurable: true
|
|
||||||
});
|
|
||||||
__magic__.globalThis = __magic__;
|
|
||||||
delete Object.prototype.__magic__;
|
|
||||||
}());
|
|
||||||
""".trimIndent()
|
|
||||||
) ?: emptyList()
|
|
||||||
|
|
||||||
fun jsElementAccess(name: String, receiver: JsExpression?): JsExpression =
|
fun jsElementAccess(name: String, receiver: JsExpression?): JsExpression =
|
||||||
if (receiver == null || name.isValidES5Identifier()) {
|
if (receiver == null || name.isValidES5Identifier()) {
|
||||||
JsNameRef(JsName(name, false), receiver)
|
JsNameRef(JsName(name, false), receiver)
|
||||||
@@ -71,13 +52,6 @@ fun jsElementAccess(name: String, receiver: JsExpression?): JsExpression =
|
|||||||
JsArrayAccess(receiver, JsStringLiteral(name))
|
JsArrayAccess(receiver, JsStringLiteral(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun jsGlobalVarRef(ref: JsNameRef): JsExpression =
|
|
||||||
if (ref.qualifier != null || ref.ident.isValidES5Identifier()) {
|
|
||||||
ref
|
|
||||||
} else {
|
|
||||||
jsElementAccess(ref.ident, JsNameRef("globalThis"))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun jsAssignment(left: JsExpression, right: JsExpression) = JsBinaryOperation(JsBinaryOperator.ASG, left, right)
|
fun jsAssignment(left: JsExpression, right: JsExpression) = JsBinaryOperation(JsBinaryOperator.ASG, left, right)
|
||||||
|
|
||||||
fun prototypeOf(classNameRef: JsExpression) = JsNameRef(Namer.PROTOTYPE_NAME, classNameRef)
|
fun prototypeOf(classNameRef: JsExpression) = JsNameRef(Namer.PROTOTYPE_NAME, classNameRef)
|
||||||
@@ -154,7 +128,7 @@ fun translateCall(
|
|||||||
) {
|
) {
|
||||||
val propertyName = context.getNameForProperty(property)
|
val propertyName = context.getNameForProperty(property)
|
||||||
val nameRef = when (jsDispatchReceiver) {
|
val nameRef = when (jsDispatchReceiver) {
|
||||||
null -> jsGlobalVarRef(JsNameRef(propertyName))
|
null -> JsNameRef(propertyName)
|
||||||
else -> jsElementAccess(propertyName.ident, jsDispatchReceiver)
|
else -> jsElementAccess(propertyName.ident, jsDispatchReceiver)
|
||||||
}
|
}
|
||||||
return when (function) {
|
return when (function) {
|
||||||
@@ -201,7 +175,7 @@ fun translateCall(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val ref = when (jsDispatchReceiver) {
|
val ref = when (jsDispatchReceiver) {
|
||||||
null -> jsGlobalVarRef(JsNameRef(symbolName))
|
null -> JsNameRef(symbolName)
|
||||||
else -> jsElementAccess(symbolName.ident, jsDispatchReceiver)
|
else -> jsElementAccess(symbolName.ident, jsDispatchReceiver)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ object JsAnnotations {
|
|||||||
val jsNativeSetter = FqName("kotlin.js.nativeSetter")
|
val jsNativeSetter = FqName("kotlin.js.nativeSetter")
|
||||||
val jsNativeInvoke = FqName("kotlin.js.nativeInvoke")
|
val jsNativeInvoke = FqName("kotlin.js.nativeInvoke")
|
||||||
val jsFunFqn = FqName("kotlin.js.JsFun")
|
val jsFunFqn = FqName("kotlin.js.JsFun")
|
||||||
|
val jsNativeImplementationFqn = FqName("kotlin.js.JsNativeImplementation")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
@@ -45,6 +46,9 @@ fun IrAnnotationContainer.getJsQualifier(): String? =
|
|||||||
fun IrAnnotationContainer.getJsName(): String? =
|
fun IrAnnotationContainer.getJsName(): String? =
|
||||||
getAnnotation(JsAnnotations.jsNameFqn)?.getSingleConstStringArgument()
|
getAnnotation(JsAnnotations.jsNameFqn)?.getSingleConstStringArgument()
|
||||||
|
|
||||||
|
fun IrAnnotationContainer.getJsNativeImplementation(): String? =
|
||||||
|
getAnnotation(JsAnnotations.jsNativeImplementationFqn)?.getSingleConstStringArgument()
|
||||||
|
|
||||||
fun IrAnnotationContainer.getJsFunAnnotation(): String? =
|
fun IrAnnotationContainer.getJsFunAnnotation(): String? =
|
||||||
getAnnotation(JsAnnotations.jsFunFqn)?.getSingleConstStringArgument()
|
getAnnotation(JsAnnotations.jsFunFqn)?.getSingleConstStringArgument()
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.ir.backend.js.utils
|
|||||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIntrinsicTransformers
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIntrinsicTransformers
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrClassModel
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrClassModel
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsPolyfillsVisitor
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||||
import org.jetbrains.kotlin.js.backend.ast.JsGlobalBlock
|
import org.jetbrains.kotlin.js.backend.ast.JsGlobalBlock
|
||||||
@@ -19,6 +20,7 @@ class JsStaticContext(
|
|||||||
private val irNamer: IrNamer,
|
private val irNamer: IrNamer,
|
||||||
val globalNameScope: NameTable<IrDeclaration>,
|
val globalNameScope: NameTable<IrDeclaration>,
|
||||||
) : IrNamer by irNamer {
|
) : IrNamer by irNamer {
|
||||||
|
val polyfills = JsPolyfillsVisitor()
|
||||||
val intrinsics = JsIntrinsicTransformers(backendContext)
|
val intrinsics = JsIntrinsicTransformers(backendContext)
|
||||||
val classModels = mutableMapOf<IrClassSymbol, JsIrClassModel>()
|
val classModels = mutableMapOf<IrClassSymbol, JsIrClassModel>()
|
||||||
val coroutineImplDeclaration = backendContext.ir.symbols.coroutineImpl.owner
|
val coroutineImplDeclaration = backendContext.ir.symbols.coroutineImpl.owner
|
||||||
|
|||||||
+3
-1
@@ -14,10 +14,12 @@ external object `+some+object%:` {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
val a = js("2")
|
|
||||||
assertEquals(42, `some+value`)
|
assertEquals(42, `some+value`)
|
||||||
assertEquals("%%++%%", `+some+object%:`.foo)
|
assertEquals("%%++%%", `+some+object%:`.foo)
|
||||||
assertEquals("something invalid", `@get something-invalid`())
|
assertEquals("something invalid", `@get something-invalid`())
|
||||||
|
|
||||||
|
`some+value` = 43
|
||||||
|
assertEquals(43, `some+value`)
|
||||||
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kotlin.js
|
||||||
|
|
||||||
|
@JsNativeImplementation("""
|
||||||
|
(function() {
|
||||||
|
if (typeof globalThis === 'object') return;
|
||||||
|
Object.defineProperty(Object.prototype, '__magic__', {
|
||||||
|
get: function() {
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
__magic__.globalThis = __magic__;
|
||||||
|
delete Object.prototype.__magic__;
|
||||||
|
}());
|
||||||
|
""")
|
||||||
|
internal external val globalThis: dynamic
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
@file:JsQualifier("Math")
|
||||||
|
package kotlin.js
|
||||||
|
|
||||||
|
@JsNativeImplementation("""
|
||||||
|
if (typeof Math == "object" && Math !== null & typeof Math.imul === "undefined") {
|
||||||
|
Math.imul = function imul(a, b) {
|
||||||
|
return ((a & 0xffff0000) * (b & 0xffff) + (a & 0xffff) * (b | 0)) | 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
internal external fun imul(a_local: Int, b_local: Int): Int
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
|
||||||
* that can be found in the license/LICENSE.txt file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package kotlin.js
|
|
||||||
|
|
||||||
// TODO: Polyfill
|
|
||||||
internal fun imul(a_local: Int, b_local: Int): Int {
|
|
||||||
val lhs = jsBitwiseAnd(a_local, js("0xffff0000")).toDouble() * jsBitwiseAnd(b_local, 0xffff).toDouble()
|
|
||||||
val rhs = jsBitwiseAnd(a_local, 0xffff).toDouble() * b_local.toDouble()
|
|
||||||
return jsBitwiseOr(lhs + rhs, 0)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kotlin.js
|
||||||
|
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY)
|
||||||
|
internal annotation class JsNativeImplementation(val implementation: String)
|
||||||
Reference in New Issue
Block a user