Support string templates in ConstantExpressionEvaluator

This commit is contained in:
Natalia Ukhorskaya
2013-11-07 18:59:16 +04:00
parent c56c2263f0
commit 50d29e0526
8 changed files with 261 additions and 2 deletions
@@ -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]
@@ -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;
}
}