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)