feat(polyfills): use stdlib for js polyfills.

This commit is contained in:
Artem Kobzar
2021-11-26 08:14:34 +00:00
committed by Space
parent 4a8f00bc7c
commit 08b0e17d47
18 changed files with 296 additions and 60 deletions
+1 -1
View File
@@ -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
@@ -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
}
}
}
@@ -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 {
@@ -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 {
@@ -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)
@@ -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")
}
}
@@ -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
@@ -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
+15
View File
@@ -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
-13
View File
@@ -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)