Compare commits

...

1 Commits

Author SHA1 Message Date
Svyatoslav Kuzmich
47310cbec9 [JS IR] Fix kotlin.js.js with complex constant expressions
Use IR interpreter to evaluate complex constexprs
2021-02-15 18:07:30 +03:00
8 changed files with 56 additions and 7 deletions

View File

@@ -132,7 +132,7 @@ private class JsCodeOutlineTransformer(
return null
val jsCodeArg = expression.getValueArgument(0) ?: error("Expected js code string")
val jsStatements = translateJsCodeIntoStatementList(jsCodeArg)
val jsStatements = translateJsCodeIntoStatementList(jsCodeArg, backendContext.irBuiltIns)
// Collect used Kotlin local variables and parameters.
val kotlinLocalsUsedInJs = mutableListOf<IrValueDeclaration>()

View File

@@ -17,7 +17,10 @@ import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.types.isUnit
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
import org.jetbrains.kotlin.ir.util.isEnumClass
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.js.backend.ast.*
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
@@ -190,7 +193,10 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
override fun visitCall(expression: IrCall, context: JsGenerationContext): JsExpression {
if (context.checkIfJsCode(expression.symbol)) {
val statements = translateJsCodeIntoStatementList(expression.getValueArgument(0) ?: error("JsCode is expected"))
val statements = translateJsCodeIntoStatementList(
expression.getValueArgument(0) ?: error("JsCode is expected"),
context.staticContext.backendContext.irBuiltIns
)
if (statements.isEmpty()) return JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(3)) // TODO: report warning or even error
val lastStatement = statements.last()

View File

@@ -11,7 +11,7 @@ import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.isAny
import org.jetbrains.kotlin.ir.util.constructedClassType
import org.jetbrains.kotlin.js.backend.ast.*
@@ -106,7 +106,10 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsSta
override fun visitCall(expression: IrCall, data: JsGenerationContext): JsStatement {
if (data.checkIfJsCode(expression.symbol)) {
val statements = translateJsCodeIntoStatementList(expression.getValueArgument(0) ?: error("JsCode is expected"))
val statements = translateJsCodeIntoStatementList(
expression.getValueArgument(0) ?: error("JsCode is expected"),
data.staticContext.backendContext.irBuiltIns
)
return when (statements.size) {
0 -> JsEmpty
1 -> statements.single()

View File

@@ -8,9 +8,11 @@ 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.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrStringConcatenation
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
@@ -21,16 +23,26 @@ import org.jetbrains.kotlin.js.backend.ast.JsRootScope
import org.jetbrains.kotlin.js.backend.ast.JsStatement
import org.jetbrains.kotlin.js.parser.parseExpressionOrStatement
fun translateJsCodeIntoStatementList(code: IrExpression): List<JsStatement> {
// TODO: check non simple compile time constants (expressions)
fun translateJsCodeIntoStatementList(code: IrExpression, irBuiltIns: IrBuiltIns): List<JsStatement> {
// TODO: support proper symbol linkage and label clash resolution
val interpreter by lazy { IrInterpreter(irBuiltIns) }
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 visitExpression(expression: IrExpression) {
val result = interpreter.interpret(expression)
if (result is IrConst<*>) {
result.acceptVoid(this)
} else {
error("Couldn't evaluate constant string expression ${expression.render()}")
}
}
override fun <T> visitConst(expression: IrConst<T>) {
builder.append(expression.kind.valueOf(expression))
}

View File

@@ -5050,6 +5050,11 @@ public class IrBoxJsES6TestGenerated extends AbstractIrBoxJsES6Test {
runTest("js/js.translator/testData/box/jsCode/codeFromVariable.kt");
}
@TestMetadata("constantExpression.kt")
public void testConstantExpression() throws Exception {
runTest("js/js.translator/testData/box/jsCode/constantExpression.kt");
}
@TestMetadata("continue.kt")
public void testContinue() throws Exception {
runTest("js/js.translator/testData/box/jsCode/continue.kt");

View File

@@ -5050,6 +5050,11 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
runTest("js/js.translator/testData/box/jsCode/codeFromVariable.kt");
}
@TestMetadata("constantExpression.kt")
public void testConstantExpression() throws Exception {
runTest("js/js.translator/testData/box/jsCode/constantExpression.kt");
}
@TestMetadata("continue.kt")
public void testContinue() throws Exception {
runTest("js/js.translator/testData/box/jsCode/continue.kt");

View File

@@ -5065,6 +5065,11 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
runTest("js/js.translator/testData/box/jsCode/codeFromVariable.kt");
}
@TestMetadata("constantExpression.kt")
public void testConstantExpression() throws Exception {
runTest("js/js.translator/testData/box/jsCode/constantExpression.kt");
}
@TestMetadata("continue.kt")
public void testContinue() throws Exception {
runTest("js/js.translator/testData/box/jsCode/continue.kt");

View File

@@ -0,0 +1,13 @@
// EXPECTED_REACHABLE_NODES: 1282
package foo
const val x = '2'
const val y = '+'
const val z = '3'
fun box(): String {
if (js(x.toString() + z) !== 23) return "Fail 1"
if (js(x.toString() + y + z) !== 5) return "Fail 2"
return "OK"
}