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">
|
||||
<option name="shouldCheckCurrentProject" value="true" />
|
||||
</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.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.*
|
||||
|
||||
@@ -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())
|
||||
|
||||
|
||||
@@ -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<SimpleFunctionDescriptor> =
|
||||
findFunctions(module.getPackage(fqName.parent()).memberScope, fqName.shortName())
|
||||
|
||||
|
||||
@@ -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<Lowering>(
|
||||
objectUsageLoweringPhase,
|
||||
captureStackTraceInThrowablesPhase,
|
||||
callsLoweringPhase,
|
||||
escapedIdentifiersLowering,
|
||||
cleanupLoweringPhase,
|
||||
// Currently broken due to static members lowering making single-open-class
|
||||
// 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
|
||||
)
|
||||
return JsNameRef(field.getJsNameOrKotlinName().identifier, receiver).withSource(expression, context)
|
||||
.also { context.staticContext.polyfills.visitDeclaration(field) }
|
||||
}
|
||||
|
||||
if (fieldParent is IrClass && fieldParent.isInline) {
|
||||
@@ -121,9 +122,11 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
||||
}
|
||||
|
||||
override fun visitGetValue(expression: IrGetValue, context: JsGenerationContext): JsExpression {
|
||||
if (expression.symbol.owner.isThisReceiver()) return JsThisRef().withSource(expression, context)
|
||||
val possibleGlobalVarRef = context.getNameForValueDeclaration(expression.symbol.owner).makeRef().withSource(expression, context)
|
||||
return jsGlobalVarRef(possibleGlobalVarRef)
|
||||
val owner = expression.symbol.owner
|
||||
if (owner.isThisReceiver()) return JsThisRef().withSource(expression, context)
|
||||
|
||||
return context.getNameForValueDeclaration(owner).makeRef().withSource(expression, context)
|
||||
.also { context.staticContext.polyfills.visitDeclaration(owner) }
|
||||
}
|
||||
|
||||
override fun visitGetObjectValue(expression: IrGetObjectValue, context: JsGenerationContext): JsExpression {
|
||||
@@ -131,22 +134,24 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
||||
assert(obj.kind == ClassKind.OBJECT)
|
||||
assert(obj.isEffectivelyExternal()) { "Non external IrGetObjectValue must be lowered" }
|
||||
|
||||
val possibleGlobalVarRef = context.getRefForExternalClass(obj).withSource(expression, context)
|
||||
return jsGlobalVarRef(possibleGlobalVarRef)
|
||||
return context.getRefForExternalClass(obj).withSource(expression, context)
|
||||
}
|
||||
|
||||
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 source = expression.value.accept(this, context)
|
||||
return jsAssignment(dest, source).withSource(expression, context)
|
||||
.also { context.staticContext.polyfills.visitDeclaration(field) }
|
||||
}
|
||||
|
||||
override fun visitSetValue(expression: IrSetValue, context: JsGenerationContext): JsExpression {
|
||||
val possibleGlobalRef = JsNameRef(context.getNameForValueDeclaration(expression.symbol.owner))
|
||||
val ref = jsGlobalVarRef(possibleGlobalRef)
|
||||
val field = expression.symbol.owner
|
||||
val ref = JsNameRef(context.getNameForValueDeclaration(field))
|
||||
val value = expression.value.accept(this, context)
|
||||
return JsBinaryOperation(JsBinaryOperator.ASG, ref, value).withSource(expression, context)
|
||||
.also { context.staticContext.polyfills.visitDeclaration(field) }
|
||||
}
|
||||
|
||||
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, context: JsGenerationContext): JsExpression {
|
||||
@@ -250,6 +255,10 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
||||
|
||||
}
|
||||
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 {
|
||||
|
||||
+4
-1
@@ -84,9 +84,11 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsSta
|
||||
}
|
||||
|
||||
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
|
||||
.maybeOptimizeIntoSwitch(context) { jsAssignment(ref, it).withSource(expression, context).makeStmt() }
|
||||
.also { context.staticContext.polyfills.visitDeclaration(owner) }
|
||||
}
|
||||
|
||||
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()
|
||||
.also { data.staticContext.polyfills.visitDeclaration(expression.symbol.owner) }
|
||||
}
|
||||
|
||||
override fun visitInstanceInitializerCall(expression: IrInstanceInitializerCall, context: JsGenerationContext): JsStatement {
|
||||
|
||||
+5
-7
@@ -42,8 +42,7 @@ class IrModuleToJsTransformer(
|
||||
private val multiModule: Boolean = false,
|
||||
private val relativeRequirePath: Boolean = false,
|
||||
private val moduleToName: Map<IrModuleFragment, String> = 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<String>(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<IrModuleFragment>, staticContext: JsStaticContext): List<JsStatement> {
|
||||
val statements = mutableListOf<JsStatement>().also {
|
||||
if (!generateScriptModule) it += JsStringLiteral("use strict").makeStmt()
|
||||
if (generateGlobalThisPolyfill) it += jsGlobalThisPolyfill()
|
||||
}
|
||||
val statements = mutableListOf<JsStatement>()
|
||||
|
||||
val preDeclarationBlock = JsGlobalBlock()
|
||||
|
||||
val postDeclarationBlock = JsGlobalBlock()
|
||||
|
||||
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 =
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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<IrDeclaration>,
|
||||
) : IrNamer by irNamer {
|
||||
val polyfills = JsPolyfillsVisitor()
|
||||
val intrinsics = JsIntrinsicTransformers(backendContext)
|
||||
val classModels = mutableMapOf<IrClassSymbol, JsIrClassModel>()
|
||||
val coroutineImplDeclaration = backendContext.ir.symbols.coroutineImpl.owner
|
||||
|
||||
+3
-1
@@ -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"
|
||||
}
|
||||
@@ -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