Support string templates in ConstantExpressionEvaluator
This commit is contained in:
+108
-2
@@ -25,14 +25,16 @@ import org.jetbrains.jet.lang.resolve.*;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedValueArgument;
|
||||
import org.jetbrains.jet.lang.resolve.constants.*;
|
||||
import org.jetbrains.jet.lang.resolve.constants.StringValue;
|
||||
import org.jetbrains.jet.lang.resolve.name.Name;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
|
||||
import javax.rmi.CORBA.ClassDesc;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.jet.lang.resolve.BindingContext.COMPILE_TIME_INITIALIZER;
|
||||
import static org.jetbrains.jet.lang.resolve.BindingContext.RESOLVED_CALL;
|
||||
|
||||
@SuppressWarnings("StaticMethodReferencedViaSubclass")
|
||||
public class ConstantExpressionEvaluator extends JetVisitor<CompileTimeConstant<?>, Void> {
|
||||
@NotNull private final BindingTrace trace;
|
||||
@NotNull private final JetType expectedType;
|
||||
@@ -56,7 +58,111 @@ public class ConstantExpressionEvaluator extends JetVisitor<CompileTimeConstant<
|
||||
|
||||
@Override
|
||||
public CompileTimeConstant<?> visitStringTemplateExpression(@NotNull JetStringTemplateExpression expression, Void nothing) {
|
||||
return trace.get(BindingContext.COMPILE_TIME_VALUE, expression);
|
||||
CompileTimeConstant<?> compileTimeConstant = trace.get(BindingContext.COMPILE_TIME_VALUE, expression);
|
||||
if (compileTimeConstant != null) {
|
||||
return compileTimeConstant;
|
||||
}
|
||||
|
||||
JetStringTemplateEntry[] stringTemplateEntries = expression.getEntries();
|
||||
if (stringTemplateEntries.length == 1) {
|
||||
CompileTimeConstant singleEntry = stringTemplateEntries[0].accept(this, nothing);
|
||||
if (!(singleEntry instanceof StringValue) && singleEntry != null) {
|
||||
return new StringValue(singleEntry.getValue().toString());
|
||||
}
|
||||
return singleEntry;
|
||||
}
|
||||
else if (stringTemplateEntries.length > 1) {
|
||||
CompileTimeConstant<?> first = stringTemplateEntries[0].accept(this, nothing);
|
||||
if (first == null) {
|
||||
return null;
|
||||
}
|
||||
CompileTimeConstant<?> second;
|
||||
String tmpResult = null;
|
||||
for (int i = 1; i < stringTemplateEntries.length; i++) {
|
||||
second = stringTemplateEntries[i].accept(this, nothing);
|
||||
if (second == null) {
|
||||
return null;
|
||||
}
|
||||
Object object = EvaluatePackage.evaluateBinaryExpression(first, second, Name.identifier("plus"));
|
||||
if (object instanceof String) {
|
||||
tmpResult = (String) object;
|
||||
}
|
||||
else {
|
||||
tmpResult = object.toString();
|
||||
}
|
||||
first = new StringValue(tmpResult);
|
||||
}
|
||||
|
||||
return new StringValue(tmpResult);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompileTimeConstant<?> visitBlockStringTemplateEntry(@NotNull JetBlockStringTemplateEntry entry, Void data) {
|
||||
JetExpression expression = entry.getExpression();
|
||||
if (expression != null) {
|
||||
return expression.accept(this, data);
|
||||
}
|
||||
return super.visitBlockStringTemplateEntry(entry, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompileTimeConstant<?> visitLiteralStringTemplateEntry(@NotNull JetLiteralStringTemplateEntry entry, Void data) {
|
||||
return new StringValue(entry.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompileTimeConstant<?> visitSimpleNameStringTemplateEntry(@NotNull JetSimpleNameStringTemplateEntry entry, Void data) {
|
||||
JetExpression expression = entry.getExpression();
|
||||
if (expression != null) {
|
||||
return expression.accept(this, data);
|
||||
}
|
||||
return super.visitSimpleNameStringTemplateEntry(entry, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompileTimeConstant<?> visitEscapeStringTemplateEntry(@NotNull JetEscapeStringTemplateEntry entry, Void data) {
|
||||
return new StringValue(entry.getUnescapedValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompileTimeConstant<?> visitBinaryExpression(@NotNull JetBinaryExpression expression, Void data) {
|
||||
JetExpression leftExpression = expression.getLeft();
|
||||
if (leftExpression == null) {
|
||||
return null;
|
||||
}
|
||||
JetExpression rightExpression = expression.getRight();
|
||||
if (rightExpression == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CompileTimeConstant<?> leftConstant = leftExpression.accept(this, data);
|
||||
if (leftConstant == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CompileTimeConstant<?> rightConstant = rightExpression.accept(this, data);
|
||||
if (rightConstant == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ResolvedCall<? extends CallableDescriptor> resolvedCall = trace.getBindingContext().get(RESOLVED_CALL, expression.getOperationReference());
|
||||
if (resolvedCall != null) {
|
||||
CallableDescriptor resultingDescriptor = resolvedCall.getResultingDescriptor();
|
||||
Object value = EvaluatePackage.evaluateBinaryExpression(leftConstant, rightConstant, resultingDescriptor.getName());
|
||||
if (value != null) {
|
||||
return createCompileTimeConstant(value);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static CompileTimeConstant<?> createCompileTimeConstant(@NotNull Object value) {
|
||||
if (value instanceof String) {
|
||||
return new StringValue((String) value);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package org.jetbrains.jet.lang.evaluate
|
||||
|
||||
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant
|
||||
import org.jetbrains.jet.lang.resolve.name.Name
|
||||
|
||||
fun evaluateBinaryExpression(firstCompileTimeConstant: CompileTimeConstant<*>, secondCompileTimeConstant: CompileTimeConstant<*>, functionName: Name): Any? {
|
||||
val compileTimeTypeFirst = getCompileTimeType(firstCompileTimeConstant)
|
||||
val compileTimeTypeSecond = getCompileTimeType(secondCompileTimeConstant)
|
||||
if (compileTimeTypeFirst == null || compileTimeTypeSecond == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
val first = firstCompileTimeConstant.getValue()
|
||||
val second = secondCompileTimeConstant.getValue()
|
||||
|
||||
val function = binaryOperations[BinaryOperation(compileTimeTypeFirst, compileTimeTypeSecond, functionName)]
|
||||
if (function != null) {
|
||||
return function(first, second)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun getCompileTimeType(c: CompileTimeConstant<*>): CompileTimeType<out Any>? = when (c.getValue()) {
|
||||
is Int -> INT
|
||||
is Byte -> BYTE
|
||||
is Short -> SHORT
|
||||
is Long -> LONG
|
||||
is Double -> DOUBLE
|
||||
is Float -> FLOAT
|
||||
is Char -> CHAR
|
||||
is Boolean -> BOOLEAN
|
||||
is String -> STRING
|
||||
else -> null
|
||||
}
|
||||
|
||||
private class CompileTimeType<T>
|
||||
|
||||
private val BYTE = CompileTimeType<Byte>()
|
||||
private val SHORT = CompileTimeType<Short>()
|
||||
private val INT = CompileTimeType<Int>()
|
||||
private val LONG = CompileTimeType<Long>()
|
||||
private val DOUBLE = CompileTimeType<Double>()
|
||||
private val FLOAT = CompileTimeType<Float>()
|
||||
private val CHAR = CompileTimeType<Char>()
|
||||
private val BOOLEAN = CompileTimeType<Boolean>()
|
||||
private val STRING = CompileTimeType<String>()
|
||||
|
||||
private fun <A, B> bOp(a: CompileTimeType<A>, b: CompileTimeType<B>, functionNameAsString: String, f: (A, B) -> Any) = BinaryOperation(a, b, Name.identifier(functionNameAsString)) to f as Function2<Any?, Any?, Any>
|
||||
|
||||
private data class BinaryOperation<A, B>(val f: CompileTimeType<out A>, val s: CompileTimeType<out B>, val functionName: Name)
|
||||
|
||||
private val binaryOperations = hashMapOf<BinaryOperation<*, *>, (Any?, Any?) -> Any>(
|
||||
// String
|
||||
bOp(STRING, STRING, "plus", { a, b -> a + b }),
|
||||
bOp(STRING, BYTE, "plus", { a, b -> a + b }),
|
||||
bOp(STRING, SHORT, "plus", { a, b -> a + b }),
|
||||
bOp(STRING, INT, "plus", { a, b -> a + b }),
|
||||
bOp(STRING, LONG, "plus", { a, b -> a + b }),
|
||||
bOp(STRING, DOUBLE, "plus", { a, b -> a + b }),
|
||||
bOp(STRING, FLOAT, "plus", { a, b -> a + b }),
|
||||
bOp(STRING, CHAR, "plus", { a, b -> a + b })
|
||||
)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package test
|
||||
|
||||
annotation class Ann(val s1: String)
|
||||
|
||||
Ann(s1 = "\$ab") class MyClass
|
||||
|
||||
// EXPECTED: Ann[s1 = "$ab": jet.String]
|
||||
@@ -0,0 +1,7 @@
|
||||
package test
|
||||
|
||||
annotation class Ann(val s1: String)
|
||||
|
||||
Ann(s1 = """a""" + "b") class MyClass
|
||||
|
||||
// EXPECTED: Ann[s1 = "ab": jet.String]
|
||||
@@ -0,0 +1,7 @@
|
||||
package test
|
||||
|
||||
annotation class Ann(val s1: String)
|
||||
|
||||
Ann(s1 = "a" + 1) class MyClass
|
||||
|
||||
// EXPECTED: Ann[s1 = "a1": jet.String]
|
||||
@@ -0,0 +1,19 @@
|
||||
package test
|
||||
|
||||
annotation class Ann(
|
||||
val s1: String,
|
||||
val s2: String,
|
||||
val s3: String,
|
||||
val s4: String
|
||||
)
|
||||
|
||||
val i = 1
|
||||
|
||||
Ann(
|
||||
s1 = "a$i",
|
||||
s2 = "a$i b",
|
||||
s3 = "$i",
|
||||
s4 = "a${i}a$i"
|
||||
) class MyClass
|
||||
|
||||
// EXPECTED: Ann[s1 = "a1": jet.String, s2 = "a1 b": jet.String, s3 = "1": jet.String, s4 = "a1a1": jet.String]
|
||||
@@ -0,0 +1,9 @@
|
||||
package test
|
||||
|
||||
annotation class Ann(val s1: String, val s2: String)
|
||||
|
||||
val i = 1
|
||||
|
||||
Ann(s1 = "a" + "b", s2 = "a" + "a$i") class MyClass
|
||||
|
||||
// EXPECTED: Ann[s1 = "ab": jet.String, s2 = "aa1": jet.String]
|
||||
+40
@@ -31,6 +31,7 @@ import org.jetbrains.jet.resolve.annotation.AbstractAnnotationParameterTest;
|
||||
/** This class is generated by {@link org.jetbrains.jet.generators.tests.GenerateTests}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("compiler/testData/resolveAnnotations/parameters")
|
||||
@InnerTestClasses({AnnotationParameterTestGenerated.Expressions.class})
|
||||
public class AnnotationParameterTestGenerated extends AbstractAnnotationParameterTest {
|
||||
public void testAllFilesPresentInParameters() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/resolveAnnotations/parameters"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
@@ -71,4 +72,43 @@ public class AnnotationParameterTestGenerated extends AbstractAnnotationParamete
|
||||
doTest("compiler/testData/resolveAnnotations/parameters/short.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/resolveAnnotations/parameters/expressions")
|
||||
public static class Expressions extends AbstractAnnotationParameterTest {
|
||||
public void testAllFilesPresentInExpressions() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/resolveAnnotations/parameters/expressions"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("escapedString.kt")
|
||||
public void testEscapedString() throws Exception {
|
||||
doTest("compiler/testData/resolveAnnotations/parameters/expressions/escapedString.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("multilineString.kt")
|
||||
public void testMultilineString() throws Exception {
|
||||
doTest("compiler/testData/resolveAnnotations/parameters/expressions/multilineString.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("stringPlusInt.kt")
|
||||
public void testStringPlusInt() throws Exception {
|
||||
doTest("compiler/testData/resolveAnnotations/parameters/expressions/stringPlusInt.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("stringTemplate.kt")
|
||||
public void testStringTemplate() throws Exception {
|
||||
doTest("compiler/testData/resolveAnnotations/parameters/expressions/stringTemplate.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("strings.kt")
|
||||
public void testStrings() throws Exception {
|
||||
doTest("compiler/testData/resolveAnnotations/parameters/expressions/strings.kt");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static Test suite() {
|
||||
TestSuite suite = new TestSuite("AnnotationParameterTestGenerated");
|
||||
suite.addTestSuite(AnnotationParameterTestGenerated.class);
|
||||
suite.addTestSuite(Expressions.class);
|
||||
return suite;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user