From 08b0e17d4787936985ff3f9c1142df31926a9bcf Mon Sep 17 00:00:00 2001 From: Artem Kobzar Date: Fri, 26 Nov 2021 08:14:34 +0000 Subject: [PATCH] feat(polyfills): use stdlib for js polyfills. --- .idea/misc.xml | 2 +- .../jetbrains/kotlin/cli/js/K2JsIrCompiler.kt | 5 +- .../kotlin/ir/backend/js/JsIntrinsics.kt | 6 + .../ir/backend/js/JsIrBackendContext.kt | 5 + .../kotlin/ir/backend/js/JsLoweringPhases.kt | 7 + .../js/lower/EscapedIdentifiersLowering.kt | 162 ++++++++++++++++++ .../IrElementToJsExpressionTransformer.kt | 25 ++- .../IrElementToJsStatementTransformer.kt | 5 +- .../irToJs/IrModuleToJsTransformer.kt | 12 +- .../transformers/irToJs/JsPolyfillsVisitor.kt | 28 +++ .../js/transformers/irToJs/jsAstUtils.kt | 30 +--- .../ir/backend/js/utils/AnnotationUtils.kt | 4 + .../ir/backend/js/utils/JsStaticContext.kt | 2 + .../externalEscapedTopLevel.kt | 4 +- libraries/stdlib/js-ir/runtime/globalThis.kt | 21 +++ libraries/stdlib/js-ir/runtime/math.kt | 15 ++ libraries/stdlib/js-ir/runtime/misc.kt | 13 -- .../js/src/kotlin/internalAnnotations.kt | 10 ++ 18 files changed, 296 insertions(+), 60 deletions(-) create mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/EscapedIdentifiersLowering.kt create mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsPolyfillsVisitor.kt create mode 100644 libraries/stdlib/js-ir/runtime/globalThis.kt create mode 100644 libraries/stdlib/js-ir/runtime/math.kt delete mode 100644 libraries/stdlib/js-ir/runtime/misc.kt create mode 100644 libraries/stdlib/js/src/kotlin/internalAnnotations.kt diff --git a/.idea/misc.xml b/.idea/misc.xml index d1ca2e70450..8ff9c44e7a1 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -95,4 +95,4 @@ - \ No newline at end of file + diff --git a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt index bf58b331a9e..d115563b7eb 100644 --- a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt +++ b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt @@ -36,11 +36,14 @@ import org.jetbrains.kotlin.incremental.js.IncrementalDataProvider import org.jetbrains.kotlin.incremental.js.IncrementalNextRoundChecker import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumer 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.ic.* 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.declarations.impl.IrFactoryImpl import org.jetbrains.kotlin.ir.declarations.persistent.PersistentIrFactory import org.jetbrains.kotlin.js.analyzer.JsAnalysisResult import org.jetbrains.kotlin.js.config.* diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt index b9573374d44..54d8180b30b 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt @@ -26,6 +26,9 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC // TODO: Should we drop operator intrinsics in favor of IrDynamicOperatorExpression? + // Global variables + val globalThis = getInternalProperty("globalThis") + // Equality operations: val jsEqeq = getInternalFunction("jsEqeq") @@ -328,6 +331,9 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC private fun getInternalFunction(name: String) = context.symbolTable.referenceSimpleFunction(context.getJsInternalFunction(name)) + private fun getInternalProperty(name: String) = + context.symbolTable.referenceProperty(context.getJsInternalProperty(name)) + private fun getInternalWithoutPackage(name: String) = context.symbolTable.referenceSimpleFunction(context.getFunctions(FqName(name)).single()) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt index 3c12accc901..d8824709d09 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt @@ -123,6 +123,7 @@ class JsIrBackendContext( // TODO: what is more clear way reference this getter? 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_POLYFILLS_PACKAGE = JS_PACKAGE_FQNAME.child(Name.identifier("polyfill")) 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 @@ -334,6 +335,10 @@ class JsIrBackendContext( internal fun getJsInternalFunction(name: String): SimpleFunctionDescriptor = 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 = findFunctions(module.getPackage(fqName.parent()).memberScope, fqName.shortName()) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt index 73a4e7e968a..8860319ef43 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt @@ -784,6 +784,12 @@ private val captureStackTraceInThrowablesPhase = makeBodyLoweringPhase( 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( { CleanupLowering() }, name = "CleanupLowering", @@ -903,6 +909,7 @@ val loweringList = listOf( objectUsageLoweringPhase, captureStackTraceInThrowablesPhase, callsLoweringPhase, + escapedIdentifiersLowering, cleanupLoweringPhase, // Currently broken due to static members lowering making single-open-class // files non-recognizable as single-class files diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/EscapedIdentifiersLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/EscapedIdentifiersLowering.kt new file mode 100644 index 00000000000..70ed0cf71ea --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/EscapedIdentifiersLowering.kt @@ -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 + } + } +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt index df342749788..d13f72bea6e 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt @@ -111,6 +111,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer = emptyMap(), - private val removeUnusedAssociatedObjects: Boolean = true, - private val generateGlobalThisPolyfill: Boolean = false + private val removeUnusedAssociatedObjects: Boolean = true ) { private val generateRegionComments = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_REGION_COMMENTS) @@ -152,7 +151,6 @@ class IrModuleToJsTransformer( ) val moduleBody = generateModuleBody(modules, staticContext) - val internalModuleName = JsName("_", false) val globalNames = NameTable(namer.globalNames) val exportStatements = ExportModelToJsStatements(nameGenerator) { globalNames.declareFreshName(it, it) } @@ -173,6 +171,7 @@ class IrModuleToJsTransformer( parameters += JsParameter(internalModuleName) parameters += (importedJsModules + importedKotlinModules).map { JsParameter(it.internalName) } with(body) { + statements += JsStringLiteral("use strict").makeStmt() statements.addWithComment("block: imports", importStatements + crossModuleImports) statements += moduleBody statements.addWithComment("block: exports", exportStatements + crossModuleExports) @@ -221,6 +220,7 @@ class IrModuleToJsTransformer( null } + staticContext.polyfills.addAllNeededPolyfillsTo(jsCode) program.accept(JsToStringGenerationVisitor(jsCode, sourceMapBuilderConsumer ?: NoOpSourceLocationConsumer)) return CompilationOutputs( @@ -284,12 +284,10 @@ class IrModuleToJsTransformer( } private fun generateModuleBody(modules: Iterable, staticContext: JsStaticContext): List { - val statements = mutableListOf().also { - if (!generateScriptModule) it += JsStringLiteral("use strict").makeStmt() - if (generateGlobalThisPolyfill) it += jsGlobalThisPolyfill() - } + val statements = mutableListOf() val preDeclarationBlock = JsGlobalBlock() + val postDeclarationBlock = JsGlobalBlock() statements.addWithComment("block: pre-declaration", preDeclarationBlock) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsPolyfillsVisitor.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsPolyfillsVisitor.kt new file mode 100644 index 00000000000..e8f574a3ef8 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsPolyfillsVisitor.kt @@ -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() + + 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") + } +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt index 014bd83b691..50f8535d522 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt @@ -45,25 +45,6 @@ fun IrWhen.toJsNode( } } -// https://mathiasbynens.be/notes/globalthis -// TODO: add DCE for globalThis polyfill declaration -fun jsGlobalThisPolyfill(): List = - 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 = if (receiver == null || name.isValidES5Identifier()) { JsNameRef(JsName(name, false), receiver) @@ -71,13 +52,6 @@ fun jsElementAccess(name: String, receiver: JsExpression?): JsExpression = 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 prototypeOf(classNameRef: JsExpression) = JsNameRef(Namer.PROTOTYPE_NAME, classNameRef) @@ -154,7 +128,7 @@ fun translateCall( ) { val propertyName = context.getNameForProperty(property) val nameRef = when (jsDispatchReceiver) { - null -> jsGlobalVarRef(JsNameRef(propertyName)) + null -> JsNameRef(propertyName) else -> jsElementAccess(propertyName.ident, jsDispatchReceiver) } return when (function) { @@ -201,7 +175,7 @@ fun translateCall( } val ref = when (jsDispatchReceiver) { - null -> jsGlobalVarRef(JsNameRef(symbolName)) + null -> JsNameRef(symbolName) else -> jsElementAccess(symbolName.ident, jsDispatchReceiver) } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/AnnotationUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/AnnotationUtils.kt index 412009215e8..ef87cc54670 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/AnnotationUtils.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/AnnotationUtils.kt @@ -27,6 +27,7 @@ object JsAnnotations { val jsNativeSetter = FqName("kotlin.js.nativeSetter") val jsNativeInvoke = FqName("kotlin.js.nativeInvoke") val jsFunFqn = FqName("kotlin.js.JsFun") + val jsNativeImplementationFqn = FqName("kotlin.js.JsNativeImplementation") } @Suppress("UNCHECKED_CAST") @@ -45,6 +46,9 @@ fun IrAnnotationContainer.getJsQualifier(): String? = fun IrAnnotationContainer.getJsName(): String? = getAnnotation(JsAnnotations.jsNameFqn)?.getSingleConstStringArgument() +fun IrAnnotationContainer.getJsNativeImplementation(): String? = + getAnnotation(JsAnnotations.jsNativeImplementationFqn)?.getSingleConstStringArgument() + fun IrAnnotationContainer.getJsFunAnnotation(): String? = getAnnotation(JsAnnotations.jsFunFqn)?.getSingleConstStringArgument() diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt index 325f7bc319f..b564619a75a 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt @@ -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.transformers.irToJs.JsIntrinsicTransformers 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.symbols.IrClassSymbol import org.jetbrains.kotlin.js.backend.ast.JsGlobalBlock @@ -19,6 +20,7 @@ class JsStaticContext( private val irNamer: IrNamer, val globalNameScope: NameTable, ) : IrNamer by irNamer { + val polyfills = JsPolyfillsVisitor() val intrinsics = JsIntrinsicTransformers(backendContext) val classModels = mutableMapOf() val coroutineImplDeclaration = backendContext.ir.symbols.coroutineImpl.owner diff --git a/js/js.translator/testData/box/escapedIdentifiers/externalEscapedTopLevel.kt b/js/js.translator/testData/box/escapedIdentifiers/externalEscapedTopLevel.kt index bb1c6422896..8a2bf3b7e96 100644 --- a/js/js.translator/testData/box/escapedIdentifiers/externalEscapedTopLevel.kt +++ b/js/js.translator/testData/box/escapedIdentifiers/externalEscapedTopLevel.kt @@ -14,10 +14,12 @@ external object `+some+object%:` { } fun box(): String { - val a = js("2") assertEquals(42, `some+value`) assertEquals("%%++%%", `+some+object%:`.foo) assertEquals("something invalid", `@get something-invalid`()) + `some+value` = 43 + assertEquals(43, `some+value`) + return "OK" } \ No newline at end of file diff --git a/libraries/stdlib/js-ir/runtime/globalThis.kt b/libraries/stdlib/js-ir/runtime/globalThis.kt new file mode 100644 index 00000000000..cafb01ad109 --- /dev/null +++ b/libraries/stdlib/js-ir/runtime/globalThis.kt @@ -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 \ No newline at end of file diff --git a/libraries/stdlib/js-ir/runtime/math.kt b/libraries/stdlib/js-ir/runtime/math.kt new file mode 100644 index 00000000000..9792710078b --- /dev/null +++ b/libraries/stdlib/js-ir/runtime/math.kt @@ -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 diff --git a/libraries/stdlib/js-ir/runtime/misc.kt b/libraries/stdlib/js-ir/runtime/misc.kt deleted file mode 100644 index 3eb9122d156..00000000000 --- a/libraries/stdlib/js-ir/runtime/misc.kt +++ /dev/null @@ -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) -} diff --git a/libraries/stdlib/js/src/kotlin/internalAnnotations.kt b/libraries/stdlib/js/src/kotlin/internalAnnotations.kt new file mode 100644 index 00000000000..3cf64c2f2f2 --- /dev/null +++ b/libraries/stdlib/js/src/kotlin/internalAnnotations.kt @@ -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)