From a6f45946796fa9d285d20a2c30149d424fbea2a1 Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Mon, 23 Apr 2018 22:57:20 +0300 Subject: [PATCH] [JS IR BE] Intrinsicify `kotlin.js.js` --- .../kotlin/ir/backend/js/JsIntrinsics.kt | 3 ++ .../IrElementToJsExpressionTransformer.kt | 8 ++-- .../irToJs/JsIntrinsicTransformers.kt | 31 ++++++++++++-- .../backend/js/transformers/irToJs/jsCode.kt | 42 +++++++++++++++++++ .../backend/js/utils/descriptorBasedUtils.kt | 15 ++++++- 5 files changed, 90 insertions(+), 9 deletions(-) create mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsCode.kt 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 7baa2e2168a..1cbb1602954 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 @@ -9,11 +9,13 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl +import org.jetbrains.kotlin.ir.backend.js.utils.getFunctions import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.util.DeclarationStubGenerator import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.js.resolve.JsPlatform.builtIns +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinTypeFactory @@ -78,6 +80,7 @@ class JsIntrinsics(private val module: ModuleDescriptor, private val irBuiltIns: val jsObjectCreate: IrSimpleFunction = defineObjectCreateIntrinsic() + val jsCode = module.getFunctions(FqName("kotlin.js.js")).singleOrNull()?.let { symbolTable.referenceFunction(it) } // Helpers: 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 d542d20e8b1..9e04386bb37 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 @@ -98,15 +98,15 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer) -> JsExpression +typealias IrCallTransformer = (IrCall, context: JsGenerationContext) -> JsExpression class JsIntrinsicTransformers(intrinsics: JsIntrinsics) { private val transformers: Map @@ -64,6 +65,16 @@ class JsIntrinsicTransformers(intrinsics: JsIntrinsics) { JsInvocation(Namer.JS_OBJECT_CREATE_FUNCTION, prototype) } + + addIfNotNull(intrinsics.jsCode) { call, context -> + val jsCode = translateJsCode(call, context.currentScope) + + when (jsCode) { + is JsExpression -> jsCode + // TODO don't generate function for this case + else -> JsInvocation(JsFunction(context.currentScope, jsCode as? JsBlock ?: JsBlock(jsCode as JsStatement), "")) + } + } } } @@ -74,14 +85,26 @@ private fun MutableMap.add(function: IrFunction, t: put(function.symbol, t) } +private fun MutableMap.addIfNotNull(symbol: IrSymbol?, t: IrCallTransformer) { + if (symbol == null) return + put(symbol, t) +} + private fun MutableMap.binOp(function: IrFunction, op: JsBinaryOperator) { - put(function.symbol, { _, args -> JsBinaryOperation(op, args[0], args[1]) }) + withTranslatedArgs(function) { JsBinaryOperation(op, it[0], it[1]) } } private fun MutableMap.prefixOp(function: IrFunction, op: JsUnaryOperator) { - put(function.symbol, { _, args -> JsPrefixOperation(op, args[0]) }) + withTranslatedArgs(function) { JsPrefixOperation(op, it[0]) } } private fun MutableMap.postfixOp(function: IrFunction, op: JsUnaryOperator) { - put(function.symbol, { _, args -> JsPostfixOperation(op, args[0]) }) + withTranslatedArgs(function) { JsPostfixOperation(op, it[0]) } +} + +private inline fun MutableMap.withTranslatedArgs( + function: IrFunction, + crossinline t: (List) -> JsExpression +) { + put(function.symbol) { call, context -> t(translateCallArguments(call, context)) } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsCode.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsCode.kt new file mode 100644 index 00000000000..d746bddf8f5 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsCode.kt @@ -0,0 +1,42 @@ +/* + * 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 org.jetbrains.kotlin.ir.backend.js.transformers.irToJs + +import com.google.gwt.dev.js.ThrowExceptionOnErrorReporter +import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrConst +import org.jetbrains.kotlin.ir.expressions.IrConstKind +import org.jetbrains.kotlin.js.backend.ast.* +import org.jetbrains.kotlin.js.parser.parse + +fun translateJsCode(call: IrCall, scope: JsScope): JsNode { + //TODO check non simple compile time constants (expressions) + + val arg = call.getValueArgument(0) as? IrConst<*> + val kind = arg?.kind as? IrConstKind.String ?: error("Parameter of js function must be compile time String constant") + + val statements = parseJsCode(kind.valueOf(arg), JsFunctionScope(scope, "")).orEmpty() + val size = statements.size + + return when (size) { + 0 -> JsEmpty + 1 -> statements[0].let { if (it is JsExpressionStatement) it.expression else it } + else -> JsBlock(statements) + } +} + +private fun parseJsCode(jsCode: String, currentScope: JsScope): List? { + // Parser can change local or global scope. + // In case of js we want to keep new local names, + // but no new global ones. + assert(currentScope is JsFunctionScope) { "Usage of js outside of function is unexpected" } + val temporaryRootScope = JsRootScope(JsProgram()) + val scope = DelegatingJsFunctionScopeWithTemporaryParent(currentScope as JsFunctionScope, temporaryRootScope) + + // TODO write debug info, see how it's done in CallExpressionTranslator.parseJsCode + + return parse(jsCode, ThrowExceptionOnErrorReporter, scope, "") +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/descriptorBasedUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/descriptorBasedUtils.kt index 822ac371e6f..77a9a3cc8bf 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/descriptorBasedUtils.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/descriptorBasedUtils.kt @@ -6,7 +6,12 @@ package org.jetbrains.kotlin.ir.backend.js.utils import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.ir.symbols.* +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name val IrFunctionSymbol.kind get() = descriptor.kind @@ -26,4 +31,12 @@ val IrClassSymbol.kind get() = descriptor.kind val IrClassSymbol.modality get() = descriptor.modality -val IrClassSymbol.isAny get() = KotlinBuiltIns.isAny(descriptor) \ No newline at end of file +val IrClassSymbol.isAny get() = KotlinBuiltIns.isAny(descriptor) + +fun ModuleDescriptor.getFunctions(fqName: FqName): List { + return getFunctions(fqName.parent(), fqName.shortName()) +} + +fun ModuleDescriptor.getFunctions(packageFqName: FqName, name: Name): List { + return getPackage(packageFqName).memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND).toList() +}