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">
<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
@@ -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
)
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 {
@@ -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 {
@@ -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)
@@ -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 =
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
@@ -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
+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)