From add8387141b7d57fc9d6d4eebb9e7dfade04d2d7 Mon Sep 17 00:00:00 2001 From: "Pavel V. Talanov" Date: Mon, 20 Jul 2015 18:18:40 +0300 Subject: [PATCH] Fix some problems with jsCode function 1. Fix a bug in frontend when passing non-String constant led to exception 2. Fix a bug in backend when passing non-JetStringTeplate string constant led to exception 3. Avoid recomputing constant argument in backend --- .../jsCode/argumentIsLiteral.kt | 7 +++++- .../js/resolve/diagnostics/JsCallChecker.kt | 22 ++++++++++------ .../test/semantics/JsCodeTestGenerated.java | 6 +++++ .../reference/CallExpressionTranslator.java | 25 ++++++------------- .../testData/jsCode/cases/codeFromVariable.kt | 14 +++++++++++ 5 files changed, 49 insertions(+), 25 deletions(-) create mode 100644 js/js.translator/testData/jsCode/cases/codeFromVariable.kt diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/jsCode/argumentIsLiteral.kt b/compiler/testData/diagnostics/testsWithJsStdLib/jsCode/argumentIsLiteral.kt index 12b96554aaf..0a93b0e54c1 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/jsCode/argumentIsLiteral.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/jsCode/argumentIsLiteral.kt @@ -6,7 +6,12 @@ fun test() { val b = "b" js(a) - js(b) + js((b)) + js(("c")) + js(3) + js(3 + 2) + js(1.0f) + js(true) js("$a") js("${1}") js("$b;") diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsCallChecker.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsCallChecker.kt index 9cb24cfde40..e1ab616f0e3 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsCallChecker.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsCallChecker.kt @@ -40,6 +40,9 @@ import org.jetbrains.kotlin.resolve.TemporaryBindingTrace import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant +import org.jetbrains.kotlin.resolve.constants.StringValue +import org.jetbrains.kotlin.resolve.constants.TypedCompileTimeConstant import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator import kotlin.platform.platformStatic @@ -53,6 +56,11 @@ public class JsCallChecker : CallChecker { val descriptor = getResultingDescriptor() return descriptor is SimpleFunctionDescriptor && JS_PATTERN.apply(descriptor) } + + platformStatic + public fun extractStringValue(compileTimeConstant: CompileTimeConstant<*>?): String? { + return ((compileTimeConstant as? TypedCompileTimeConstant<*>)?.constantValue as? StringValue)?.value + } } override fun check(resolvedCall: ResolvedCall, context: BasicCallResolutionContext) { @@ -62,20 +70,20 @@ public class JsCallChecker : CallChecker { if (expression !is JetCallExpression) return val arguments = expression.getValueArgumentList()?.getArguments() - val argument = arguments?.firstOrNull()?.getArgumentExpression() + val argument = arguments?.firstOrNull()?.getArgumentExpression() ?: return - if (argument == null) return - - val stringType = KotlinBuiltIns.getInstance().getStringType() val trace = TemporaryBindingTrace.create(context.trace, "JsCallChecker") - val evaluationResult = ConstantExpressionEvaluator.evaluate(argument, trace, stringType) - if (evaluationResult == null) { + val evaluationResult = ConstantExpressionEvaluator.evaluate(argument, trace, KotlinBuiltIns.getInstance().getStringType()) + val code = extractStringValue(evaluationResult) + + if (code == null) { context.trace.report(ErrorsJs.JSCODE_ARGUMENT_SHOULD_BE_CONSTANT.on(argument)) return } - val code = evaluationResult.getValue(stringType) as String + trace.commit() + val errorReporter = JsCodeErrorReporter(argument, code, context.trace) try { diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodeTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodeTestGenerated.java index 40ce3f18bf6..100585fdfd2 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodeTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodeTestGenerated.java @@ -47,6 +47,12 @@ public class JsCodeTestGenerated extends AbstractJsCodeTest { doTest(fileName); } + @TestMetadata("codeFromVariable.kt") + public void testCodeFromVariable() throws Exception { + String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/jsCode/cases/codeFromVariable.kt"); + doTest(fileName); + } + @TestMetadata("continue.kt") public void testContinue() throws Exception { String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/jsCode/cases/continue.kt"); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallExpressionTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallExpressionTranslator.java index 235ff76853d..0b2539ca2ac 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallExpressionTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallExpressionTranslator.java @@ -20,22 +20,16 @@ import com.google.dart.compiler.backend.js.ast.*; import com.google.gwt.dev.js.ThrowExceptionOnErrorReporter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.js.parser.ParserPackage; +import org.jetbrains.kotlin.js.resolve.diagnostics.JsCallChecker; import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator; import org.jetbrains.kotlin.js.translate.context.TranslationContext; import org.jetbrains.kotlin.psi.JetCallExpression; import org.jetbrains.kotlin.psi.JetExpression; -import org.jetbrains.kotlin.psi.JetStringTemplateExpression; import org.jetbrains.kotlin.psi.ValueArgument; -import org.jetbrains.kotlin.resolve.BindingTrace; -import org.jetbrains.kotlin.resolve.TemporaryBindingTrace; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; -import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant; -import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator; import org.jetbrains.kotlin.resolve.inline.InlineUtil; -import org.jetbrains.kotlin.types.JetType; import java.util.List; @@ -43,6 +37,7 @@ import static org.jetbrains.kotlin.js.resolve.diagnostics.JsCallChecker.isJsCall import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.getFunctionDescriptor; import static org.jetbrains.kotlin.js.translate.utils.UtilsPackage.setInlineCallMetadata; import static org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage.getFunctionResolvedCallWithAssert; +import static org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator.getConstant; public final class CallExpressionTranslator extends AbstractCallExpressionTranslator { @@ -104,9 +99,9 @@ public final class CallExpressionTranslator extends AbstractCallExpressionTransl private JsNode translateJsCode() { List arguments = expression.getValueArguments(); JetExpression argumentExpression = arguments.get(0).getArgumentExpression(); - assert argumentExpression instanceof JetStringTemplateExpression; + assert argumentExpression != null; - List statements = parseJsCode((JetStringTemplateExpression) argumentExpression); + List statements = parseJsCode(argumentExpression); int size = statements.size(); if (size == 0) { @@ -124,20 +119,16 @@ public final class CallExpressionTranslator extends AbstractCallExpressionTransl } @NotNull - private List parseJsCode(@NotNull JetStringTemplateExpression jsCodeExpression) { - BindingTrace bindingTrace = TemporaryBindingTrace.create(context().bindingTrace(), "parseJsCode"); - JetType stringType = KotlinBuiltIns.getInstance().getStringType(); - CompileTimeConstant constant = ConstantExpressionEvaluator.evaluate(jsCodeExpression, bindingTrace, stringType); + private List parseJsCode(@NotNull JetExpression jsCodeExpression) { + String jsCode = JsCallChecker.extractStringValue(getConstant(jsCodeExpression, context().bindingContext())); - assert constant != null: "jsCode must be compile time string " + jsCodeExpression; - String jsCode = (String) constant.getValue(stringType); - assert jsCode != null: jsCodeExpression.toString(); + assert jsCode != null : "jsCode must be compile time string " + jsCodeExpression.getText(); // Parser can change local or global scope. // In case of js we want to keep new local names, // but no new global ones. JsScope currentScope = context().scope(); - assert currentScope instanceof JsFunctionScope: "Usage of js outside of function is unexpected"; + assert currentScope instanceof JsFunctionScope : "Usage of js outside of function is unexpected"; JsScope temporaryRootScope = new JsRootScope(new JsProgram("")); JsScope scope = new DelegatingJsFunctionScopeWithTemporaryParent((JsFunctionScope) currentScope, temporaryRootScope); return ParserPackage.parse(jsCode, ThrowExceptionOnErrorReporter.INSTANCE$, scope); diff --git a/js/js.translator/testData/jsCode/cases/codeFromVariable.kt b/js/js.translator/testData/jsCode/cases/codeFromVariable.kt new file mode 100644 index 00000000000..e2bf554b258 --- /dev/null +++ b/js/js.translator/testData/jsCode/cases/codeFromVariable.kt @@ -0,0 +1,14 @@ +package foo + +fun box(): String { + var c: Int = 0 + + val code = "c = 3" + js(code) + + assertEquals(3, c) + js(("c = 5")) + + assertEquals(5, c) + return "OK" +} \ No newline at end of file