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
This commit is contained in:
Pavel V. Talanov
2015-07-20 18:18:40 +03:00
parent b4825ada69
commit add8387141
5 changed files with 49 additions and 25 deletions
@@ -6,7 +6,12 @@ fun test() {
val b = "b"
js(a)
js(b)
js((b))
js(("c"))
js(<!CONSTANT_EXPECTED_TYPE_MISMATCH, JSCODE_ARGUMENT_SHOULD_BE_CONSTANT!>3<!>)
js(<!TYPE_MISMATCH, JSCODE_ARGUMENT_SHOULD_BE_CONSTANT!>3 + 2<!>)
js(<!CONSTANT_EXPECTED_TYPE_MISMATCH, JSCODE_ARGUMENT_SHOULD_BE_CONSTANT!>1.0f<!>)
js(<!CONSTANT_EXPECTED_TYPE_MISMATCH, JSCODE_ARGUMENT_SHOULD_BE_CONSTANT!>true<!>)
js("$a")
js("${1}")
js("$b;")
@@ -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 <F : CallableDescriptor?> check(resolvedCall: ResolvedCall<F>, 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 {
@@ -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");
@@ -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<? extends ValueArgument> arguments = expression.getValueArguments();
JetExpression argumentExpression = arguments.get(0).getArgumentExpression();
assert argumentExpression instanceof JetStringTemplateExpression;
assert argumentExpression != null;
List<JsStatement> statements = parseJsCode((JetStringTemplateExpression) argumentExpression);
List<JsStatement> statements = parseJsCode(argumentExpression);
int size = statements.size();
if (size == 0) {
@@ -124,20 +119,16 @@ public final class CallExpressionTranslator extends AbstractCallExpressionTransl
}
@NotNull
private List<JsStatement> 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<JsStatement> 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("<js code>"));
JsScope scope = new DelegatingJsFunctionScopeWithTemporaryParent((JsFunctionScope) currentScope, temporaryRootScope);
return ParserPackage.parse(jsCode, ThrowExceptionOnErrorReporter.INSTANCE$, scope);
@@ -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"
}