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:
+6
-1
@@ -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");
|
||||
|
||||
+8
-17
@@ -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"
|
||||
}
|
||||
Reference in New Issue
Block a user