[JS IR BE] Refactored js("...") function

- support object expression
 - do not wrap in function in statement-level position
 - support implicit return
 - code clean up
This commit is contained in:
Roman Artemev
2019-07-11 11:37:00 +03:00
committed by romanart
parent 946bc75cda
commit efafb6585e
9 changed files with 217 additions and 209 deletions
@@ -113,7 +113,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
val fromPrimary = context.currentFunction is IrConstructor
val thisRef =
if (fromPrimary) JsThisRef() else context.getNameForValueDeclaration(context.currentFunction!!.valueParameters.last()).makeRef()
val arguments = translateCallArguments(expression, context)
val arguments = translateCallArguments(expression, context, this)
val constructor = expression.symbol.owner
if (constructor.parentAsClass.isInline) {
@@ -128,13 +128,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
override fun visitConstructorCall(expression: IrConstructorCall, context: JsGenerationContext): JsExpression {
val function = expression.symbol.owner
val symbol = expression.symbol
context.staticContext.intrinsics[symbol]?.let {
return it(expression, context)
}
val arguments = translateCallArguments(expression, context)
val arguments = translateCallArguments(expression, context, this)
val klass = function.parentAsClass
return if (klass.isInline) {
assert(function.isPrimary) {
@@ -155,153 +149,32 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
}
override fun visitCall(expression: IrCall, context: JsGenerationContext): JsExpression {
val function = expression.symbol.owner.realOverrideTarget
val symbol = function.symbol
if (context.checkIfJsCode(expression.symbol)) {
val statements = translateJsCodeIntoStatementList(expression.getValueArgument(0) ?: error("JsCode is expected"))
if (statements.isEmpty()) return JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(3)) // TODO: report warning or even error
context.staticContext.intrinsics[symbol]?.let {
return it(expression, context)
}
val jsDispatchReceiver = expression.dispatchReceiver?.accept(this, context)
val jsExtensionReceiver = expression.extensionReceiver?.accept(this, context)
val arguments = translateCallArguments(expression, context)
// Transform external property accessor call
// @JsName-annotated external property accessors are translated as function calls
if (function is IrSimpleFunction && function.getJsName() == null) {
val property = function.correspondingPropertySymbol?.owner
if (property != null && property.isEffectivelyExternal()) {
val nameRef = JsNameRef(context.getNameForProperty(property), jsDispatchReceiver)
return when (function) {
property.getter -> nameRef
property.setter -> jsAssignment(nameRef, arguments.single())
else -> error("Function must be an accessor of corresponding property")
}
}
}
if (isNativeInvoke(expression)) {
return JsInvocation(jsDispatchReceiver!!, arguments)
}
expression.superQualifierSymbol?.let { superQualifier ->
require(function is IrSimpleFunction)
val (target, klass) = if (superQualifier.owner.isInterface) {
val impl = function.resolveFakeOverride()!!
Pair(impl, impl.parentAsClass)
} else {
Pair(function, superQualifier.owner)
val lastStatement = statements.last()
if (statements.size == 1) {
if (lastStatement is JsExpressionStatement) return lastStatement.expression
}
val qualifierName = context.getNameForClass(klass).makeRef()
val targetName = context.getNameForMemberFunction(target)
val qPrototype = JsNameRef(targetName, prototypeOf(qualifierName))
val callRef = JsNameRef(Namer.CALL_FUNCTION, qPrototype)
return JsInvocation(callRef, jsDispatchReceiver?.let { receiver -> listOf(receiver) + arguments } ?: arguments)
}
val newStatements = statements.toMutableList()
val varargParameterIndex = function.valueParameters.indexOfFirst { it.varargElementType != null }
val isExternalVararg = function.isEffectivelyExternal() && varargParameterIndex != -1
if (function is IrConstructor) {
// Inline class primary constructor takes a single value of to
// initialize underlying property.
// TODO: Support initialization block
val klass = function.parentAsClass
return if (klass.isInline) {
assert(function.isPrimary) {
"Inline class secondary constructors must be lowered into static methods"
when (lastStatement) {
is JsReturn -> {
}
// Argument value constructs unboxed inline class instance
arguments.single()
} else {
val ref = when {
klass.isEffectivelyExternal() ->
context.getRefForExternalClass(klass)
else ->
context.getNameForClass(klass).makeRef()
is JsExpressionStatement -> {
newStatements[statements.lastIndex] = JsReturn(lastStatement.expression)
}
JsNew(ref, arguments)
// TODO: report warning or even error
else -> newStatements += JsReturn(JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(3)))
}
val syntheticFunction = JsFunction(emptyScope, JsBlock(newStatements), "")
return JsInvocation(syntheticFunction)
}
require(function is IrSimpleFunction)
val symbolName = when (jsDispatchReceiver) {
null -> context.getNameForStaticFunction(function)
else -> context.getNameForMemberFunction(function)
}
val ref = when (jsDispatchReceiver) {
null -> JsNameRef(symbolName)
else -> JsNameRef(symbolName, jsDispatchReceiver)
}
return if (isExternalVararg) {
// External vararg arguments should be represented in JS as multiple "plain" arguments (opposed to arrays in Kotlin)
// We are using `Function.prototype.apply` function to pass all arguments as a single array.
// For this purpose are concatenating non-vararg arguments with vararg.
// TODO: Don't use `Function.prototype.apply` when number of arguments is known at compile time (e.g. there are no spread operators)
val arrayConcat = JsNameRef("concat", JsArrayLiteral())
val arraySliceCall = JsNameRef("call", JsNameRef("slice", JsArrayLiteral()))
val argumentsAsSingleArray = JsInvocation(
arrayConcat,
listOfNotNull(jsExtensionReceiver) + arguments.mapIndexed { index, argument ->
when (index) {
// Call `Array.prototype.slice` on vararg arguments in order to convert array-like objects into proper arrays
// TODO: Optimize for proper arrays
varargParameterIndex -> JsInvocation(arraySliceCall, argument)
// TODO: Don't wrap non-array-like arguments with array literal
// TODO: Wrap adjacent non-vararg arguments in a single array literal
else -> JsArrayLiteral(listOf(argument))
}
}
)
if (jsDispatchReceiver != null) {
// TODO: Do not create IIFE when receiver expression is simple or has no side effects
// TODO: Do not create IIFE at all? (Currently there is no reliable way to create temporary variable in current scope)
val receiverName = JsName("\$externalVarargReceiverTmp")
val receiverRef = receiverName.makeRef()
JsInvocation(
// Create scope for temporary variable holding dispatch receiver
// It is used both during method reference and passing `this` value to `apply` function.
JsNameRef(
"call",
JsFunction(
emptyScope,
JsBlock(
JsVars(JsVars.JsVar(receiverName, jsDispatchReceiver)),
JsReturn(
JsInvocation(
JsNameRef("apply", JsNameRef(symbolName, receiverRef)),
listOf(
receiverRef,
argumentsAsSingleArray
)
)
)
),
"VarargIIFE"
)),
JsThisRef()
)
} else {
JsInvocation(
JsNameRef("apply", JsNameRef(symbolName)),
listOf(JsNullLiteral(), argumentsAsSingleArray)
)
}
} else {
JsInvocation(ref, listOfNotNull(jsExtensionReceiver) + arguments)
}
return translateCall(expression, context, this)
}
override fun visitWhen(expression: IrWhen, context: JsGenerationContext): JsExpression {
@@ -320,7 +193,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
override fun visitTypeOperator(expression: IrTypeOperatorCall, data: JsGenerationContext): JsExpression {
return when (expression.operator) {
IrTypeOperator.IMPLICIT_CAST -> expression.argument.accept(this, data)
else -> throw IllegalStateException("All type operator calls except IMPLICIT_CAST should be lowered at this point")
else -> error("All type operator calls except IMPLICIT_CAST should be lowered at this point")
}
}
@@ -396,13 +269,4 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
expression.left.accept(this, data),
expression.right.accept(this, data)
)
private fun isNativeInvoke(call: IrCall): Boolean {
val simpleFunction = call.symbol.owner as? IrSimpleFunction ?: return false
val receiverType = simpleFunction.dispatchReceiverParameter?.type ?: return false
if (simpleFunction.isSuspend) return false
return simpleFunction.name == OperatorNameConventions.INVOKE && receiverType.isFunctionTypeOrSubtype()
}
}
@@ -71,6 +71,19 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsSta
return expression.accept(IrElementToJsExpressionTransformer(), context).makeStmt()
}
override fun visitCall(expression: IrCall, data: JsGenerationContext): JsStatement {
if (data.checkIfJsCode(expression.symbol)) {
val statements = translateJsCodeIntoStatementList(expression.getValueArgument(0) ?: error("JsCode is expected"))
return when (statements.size) {
0 -> JsEmpty
1 -> statements.single()
// TODO: use transparent block (e.g. JsCompositeBlock)
else -> JsBlock(statements)
}
}
return translateCall(expression, data, IrElementToJsExpressionTransformer()).makeStmt()
}
override fun visitInstanceInitializerCall(expression: IrInstanceInitializerCall, context: JsGenerationContext): JsStatement {
// TODO: implement
@@ -8,13 +8,14 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.util.getInlineClassBackingField
@@ -22,7 +23,7 @@ import org.jetbrains.kotlin.ir.util.getInlinedClass
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
import org.jetbrains.kotlin.js.backend.ast.*
typealias IrCallTransformer = (IrFunctionAccessExpression, context: JsGenerationContext) -> JsExpression
typealias IrCallTransformer = (IrCall, context: JsGenerationContext) -> JsExpression
class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
private val transformers: Map<IrSymbol, IrCallTransformer>
@@ -100,15 +101,7 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
}
}
addIfNotNull(intrinsics.jsCode) { call, context ->
val jsCode = translateJsCode(call as IrCall)
when (jsCode) {
is JsExpression -> jsCode
// TODO don't generate function for this case
else -> JsInvocation(JsFunction(emptyScope, jsCode as? JsBlock ?: JsBlock(jsCode as JsStatement), ""))
}
}
addIfNotNull(intrinsics.jsCode) { _, _ -> error("Should not be called") }
add(intrinsics.jsGetContinuation) { _, context: JsGenerationContext ->
context.continuation
@@ -181,7 +174,7 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
add(intrinsics.jsBind) { call, context: JsGenerationContext ->
val receiver = call.getValueArgument(0)!!
val reference = call.getValueArgument(1) as IrFunctionReference
val superClass = (call as IrCall).superQualifierSymbol!!
val superClass = call.superQualifierSymbol!!
val jsReceiver = receiver.accept(IrElementToJsExpressionTransformer(), context)
val functionName = context.getNameForMemberFunction(reference.symbol.owner as IrSimpleFunction)
@@ -201,11 +194,15 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
operator fun get(symbol: IrSymbol): IrCallTransformer? = transformers[symbol]
}
private fun translateCallArguments(expression: IrCall, context: JsGenerationContext): List<JsExpression> {
return translateCallArguments(expression, context, IrElementToJsExpressionTransformer())
}
private fun MutableMap<IrSymbol, IrCallTransformer>.add(functionSymbol: IrSymbol, t: IrCallTransformer) {
put(functionSymbol, t)
}
private fun MutableMap<IrSymbol, IrCallTransformer>.add(function: IrFunction, t: IrCallTransformer) {
private fun MutableMap<IrSymbol, IrCallTransformer>.add(function: IrSimpleFunction, t: IrCallTransformer) {
put(function.symbol, t)
}
@@ -214,20 +211,20 @@ private fun MutableMap<IrSymbol, IrCallTransformer>.addIfNotNull(symbol: IrSymbo
put(symbol, t)
}
private fun MutableMap<IrSymbol, IrCallTransformer>.binOp(function: IrFunctionSymbol, op: JsBinaryOperator) {
private fun MutableMap<IrSymbol, IrCallTransformer>.binOp(function: IrSimpleFunctionSymbol, op: JsBinaryOperator) {
withTranslatedArgs(function) { JsBinaryOperation(op, it[0], it[1]) }
}
private fun MutableMap<IrSymbol, IrCallTransformer>.prefixOp(function: IrFunctionSymbol, op: JsUnaryOperator) {
private fun MutableMap<IrSymbol, IrCallTransformer>.prefixOp(function: IrSimpleFunctionSymbol, op: JsUnaryOperator) {
withTranslatedArgs(function) { JsPrefixOperation(op, it[0]) }
}
private fun MutableMap<IrSymbol, IrCallTransformer>.postfixOp(function: IrFunctionSymbol, op: JsUnaryOperator) {
private fun MutableMap<IrSymbol, IrCallTransformer>.postfixOp(function: IrSimpleFunctionSymbol, op: JsUnaryOperator) {
withTranslatedArgs(function) { JsPostfixOperation(op, it[0]) }
}
private inline fun MutableMap<IrSymbol, IrCallTransformer>.withTranslatedArgs(
function: IrFunctionSymbol,
function: IrSimpleFunctionSymbol,
crossinline t: (List<JsExpression>) -> JsExpression
) {
put(function) { call, context -> t(translateCallArguments(call, context)) }
@@ -7,16 +7,13 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.backend.common.descriptors.isSuspend
import org.jetbrains.kotlin.backend.common.ir.isElseBranch
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope
import org.jetbrains.kotlin.ir.backend.js.utils.*
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrWhen
import org.jetbrains.kotlin.ir.util.isExternalOrInheritedFromExternal
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.util.OperatorNameConventions
fun jsVar(name: JsName, initializer: IrExpression?, context: JsGenerationContext): JsVars {
val jsInitializer = initializer?.accept(IrElementToJsExpressionTransformer(), context)
@@ -64,8 +61,145 @@ fun translateFunction(declaration: IrFunction, name: JsName?, context: JsGenerat
return function
}
fun translateCallArguments(expression: IrMemberAccessExpression, context: JsGenerationContext): List<JsExpression> {
val transformer = IrElementToJsExpressionTransformer()
private fun isNativeInvoke(call: IrCall): Boolean {
val simpleFunction = call.symbol.owner as? IrSimpleFunction ?: return false
val receiverType = simpleFunction.dispatchReceiverParameter?.type ?: return false
if (simpleFunction.isSuspend) return false
return simpleFunction.name == OperatorNameConventions.INVOKE && receiverType.isFunctionTypeOrSubtype()
}
fun translateCall(
expression: IrCall,
context: JsGenerationContext,
transformer: IrElementToJsExpressionTransformer
): JsExpression {
val function = expression.symbol.owner.realOverrideTarget
require(function is IrSimpleFunction) { "Only IrSimpleFunction could be called via IrCall" } // TODO: fix it in IrCall
val symbol = function.symbol
context.staticContext.intrinsics[symbol]?.let {
return it(expression, context)
}
val jsDispatchReceiver = expression.dispatchReceiver?.accept(transformer, context)
val jsExtensionReceiver = expression.extensionReceiver?.accept(transformer, context)
val arguments = translateCallArguments(expression, context, transformer)
// Transform external property accessor call
// @JsName-annotated external property accessors are translated as function calls
if (function.getJsName() == null) {
val property = function.correspondingPropertySymbol?.owner
if (property != null && property.isEffectivelyExternal()) {
val nameRef = JsNameRef(context.getNameForProperty(property), jsDispatchReceiver)
return when (function) {
property.getter -> nameRef
property.setter -> jsAssignment(nameRef, arguments.single())
else -> error("Function must be an accessor of corresponding property")
}
}
}
if (isNativeInvoke(expression)) {
return JsInvocation(jsDispatchReceiver!!, arguments)
}
expression.superQualifierSymbol?.let { superQualifier ->
val (target, klass) = if (superQualifier.owner.isInterface) {
val impl = function.resolveFakeOverride()!!
Pair(impl, impl.parentAsClass)
} else {
Pair(function, superQualifier.owner)
}
val qualifierName = context.getNameForClass(klass).makeRef()
val targetName = context.getNameForMemberFunction(target)
val qPrototype = JsNameRef(targetName, prototypeOf(qualifierName))
val callRef = JsNameRef(Namer.CALL_FUNCTION, qPrototype)
return JsInvocation(callRef, jsDispatchReceiver?.let { receiver -> listOf(receiver) + arguments } ?: arguments)
}
val varargParameterIndex = function.valueParameters.indexOfFirst { it.varargElementType != null }
val isExternalVararg = function.isEffectivelyExternal() && varargParameterIndex != -1
val symbolName = when (jsDispatchReceiver) {
null -> context.getNameForStaticFunction(function)
else -> context.getNameForMemberFunction(function)
}
val ref = when (jsDispatchReceiver) {
null -> JsNameRef(symbolName)
else -> JsNameRef(symbolName, jsDispatchReceiver)
}
return if (isExternalVararg) {
// External vararg arguments should be represented in JS as multiple "plain" arguments (opposed to arrays in Kotlin)
// We are using `Function.prototype.apply` function to pass all arguments as a single array.
// For this purpose are concatenating non-vararg arguments with vararg.
// TODO: Don't use `Function.prototype.apply` when number of arguments is known at compile time (e.g. there are no spread operators)
val arrayConcat = JsNameRef("concat", JsArrayLiteral())
val arraySliceCall = JsNameRef("call", JsNameRef("slice", JsArrayLiteral()))
val argumentsAsSingleArray = JsInvocation(
arrayConcat,
listOfNotNull(jsExtensionReceiver) + arguments.mapIndexed { index, argument ->
when (index) {
// Call `Array.prototype.slice` on vararg arguments in order to convert array-like objects into proper arrays
// TODO: Optimize for proper arrays
varargParameterIndex -> JsInvocation(arraySliceCall, argument)
// TODO: Don't wrap non-array-like arguments with array literal
// TODO: Wrap adjacent non-vararg arguments in a single array literal
else -> JsArrayLiteral(listOf(argument))
}
}
)
if (jsDispatchReceiver != null) {
// TODO: Do not create IIFE when receiver expression is simple or has no side effects
// TODO: Do not create IIFE at all? (Currently there is no reliable way to create temporary variable in current scope)
val receiverName = JsName("\$externalVarargReceiverTmp")
val receiverRef = receiverName.makeRef()
JsInvocation(
// Create scope for temporary variable holding dispatch receiver
// It is used both during method reference and passing `this` value to `apply` function.
JsNameRef(
"call",
JsFunction(
emptyScope,
JsBlock(
JsVars(JsVars.JsVar(receiverName, jsDispatchReceiver)),
JsReturn(
JsInvocation(
JsNameRef("apply", JsNameRef(symbolName, receiverRef)),
listOf(
receiverRef,
argumentsAsSingleArray
)
)
)
),
"VarargIIFE"
)
),
JsThisRef()
)
} else {
JsInvocation(
JsNameRef("apply", JsNameRef(symbolName)),
listOf(JsNullLiteral(), argumentsAsSingleArray)
)
}
} else {
JsInvocation(ref, listOfNotNull(jsExtensionReceiver) + arguments)
}
}
fun translateCallArguments(expression: IrMemberAccessExpression, context: JsGenerationContext, transformer: IrElementToJsExpressionTransformer): List<JsExpression> {
val size = expression.valueArgumentsCount
val arguments = (0 until size).mapTo(ArrayList(size)) { index ->
@@ -90,7 +224,6 @@ fun defineProperty(receiver: JsExpression, name: String, value: () -> JsExpressi
return JsInvocation(objectDefineProperty, receiver, JsStringLiteral(name), value())
}
fun defineProperty(receiver: JsExpression, name: String, getter: JsExpression?, setter: JsExpression? = null) =
defineProperty(receiver, name) {
val literal = JsObjectLiteral(true)
@@ -6,8 +6,8 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import com.google.gwt.dev.js.ThrowExceptionOnErrorReporter
import com.google.gwt.dev.js.rhino.CodePosition
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrStringConcatenation
@@ -15,17 +15,21 @@ import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.parser.parse
import org.jetbrains.kotlin.js.backend.ast.JsFunctionScope
import org.jetbrains.kotlin.js.backend.ast.JsProgram
import org.jetbrains.kotlin.js.backend.ast.JsRootScope
import org.jetbrains.kotlin.js.backend.ast.JsStatement
import org.jetbrains.kotlin.js.parser.parseExpressionOrStatement
fun translateJsCode(call: IrCall): JsNode {
//TODO check non simple compile time constants (expressions)
fun translateJsCodeIntoStatementList(code: IrExpression): List<JsStatement> {
// TODO: check non simple compile time constants (expressions)
// TODO: support proper symbol linkage and label clash resolution
fun foldString(expression: IrExpression): String {
val builder = StringBuilder()
expression.acceptVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) = error("Parameter of js function must be compile time String constant, not ${element.render()}")
override fun visitElement(element: IrElement) =
error("Parameter of js function must be compile time String constant, not ${element.render()}")
override fun <T> visitConst(expression: IrConst<T>) {
builder.append(expression.kind.valueOf(expression))
@@ -37,15 +41,7 @@ fun translateJsCode(call: IrCall): JsNode {
return builder.toString()
}
val code = call.getValueArgument(0)!!
val statements = parseJsCode(foldString(code)).orEmpty()
val size = statements.size
return when (size) {
0 -> JsEmpty
1 -> statements[0].let { (it as? JsExpressionStatement)?.expression ?: it }
else -> JsBlock(statements)
}
return parseJsCode(foldString(code)) ?: emptyList()
}
private fun parseJsCode(jsCode: String): List<JsStatement>? {
@@ -56,7 +52,7 @@ private fun parseJsCode(jsCode: String): List<JsStatement>? {
val temporaryRootScope = JsRootScope(JsProgram())
val currentScope = JsFunctionScope(temporaryRootScope, "js")
// TODO write debug info, see how it's done in CallExpressionTranslator.parseJsCode
// TODO: write debug info, see how it's done in CallExpressionTranslator.parseJsCode
return parse(jsCode, ThrowExceptionOnErrorReporter, currentScope, "<js-code>")
return parseExpressionOrStatement(jsCode, ThrowExceptionOnErrorReporter, currentScope, CodePosition(0, 0), "<js-code>")
}
@@ -7,7 +7,11 @@ package org.jetbrains.kotlin.ir.backend.js.utils
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.js.backend.ast.JsName
import org.jetbrains.kotlin.js.backend.ast.JsNameRef
import org.jetbrains.kotlin.js.backend.ast.JsScope
import org.jetbrains.kotlin.js.backend.ast.JsThisRef
val emptyScope: JsScope
get() = object : JsScope("nil") {
@@ -42,4 +46,6 @@ class JsGenerationContext(
val overriddenSymbols = (currentFunction as? IrSimpleFunction)?.overriddenSymbols ?: return false
return staticContext.doResumeFunctionSymbol in overriddenSymbols
}
fun checkIfJsCode(symbol: IrFunctionSymbol): Boolean = symbol == staticContext.backendContext.intrinsics.jsCode
}
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// EXPECTED_REACHABLE_NODES: 1280
fun box(): String {
return js("""
@@ -1,3 +1,4 @@
// IGNORE_BACKEND: JS_IR
// EXPECTED_REACHABLE_NODES: 1229
package foo
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// EXPECTED_REACHABLE_NODES: 1282
import kotlin.js.*