Implement toString in painless's AST

This should make debugging painless' analysis and code generation a
little easier.

The `toString` implementations mirror the AST somewhat, and look like
`(SSource (SReturn (ENumeric 1)))`.
This commit is contained in:
Nik Everett 2016-11-14 10:08:56 -05:00
parent cb5c25ab4f
commit f5c8c746e6
65 changed files with 1383 additions and 12 deletions

View File

@ -27,7 +27,7 @@ package org.elasticsearch.painless;
*/
public enum Operation {
MUL ( "+" ),
MUL ( "*" ),
DIV ( "/" ),
REM ( "%" ),
ADD ( "+" ),

View File

@ -24,9 +24,18 @@ import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import static java.lang.Math.max;
import static java.util.Collections.emptyList;
/**
* The superclass for all nodes.
*/
@ -62,7 +71,87 @@ public abstract class ANode {
*/
abstract void write(MethodWriter writer, Globals globals);
/**
* Create an error with location information pointing to this node.
*/
RuntimeException createError(RuntimeException exception) {
return location.createError(exception);
}
/**
* Subclasses should implement this with a method like {@link #singleLineToString(Object...)} or
* {@link #multilineToString(Collection, Collection)}.
*/
public abstract String toString();
// Below are utilities for building the toString
/**
* Build {@link #toString()} for a node without inserting line breaks between the sub-nodes.
*/
protected String singleLineToString(Object... subs) {
return singleLineToString(Arrays.asList(subs));
}
/**
* Build {@link #toString()} for a node without inserting line breaks between the sub-nodes.
*/
protected String singleLineToString(Collection<? extends Object> subs) {
return joinWithName(getClass().getSimpleName(), subs, emptyList());
}
/**
* Build {@link #toString()} for a node that optionally ends in {@code (Args some arguments here)}. Usually function calls.
*/
protected String singleLineToStringWithOptionalArgs(Collection<? extends ANode> arguments, Object... restOfSubs) {
List<Object> subs = new ArrayList<>();
Collections.addAll(subs, restOfSubs);
if (false == arguments.isEmpty()) {
subs.add(joinWithName("Args", arguments, emptyList()));
}
return singleLineToString(subs);
}
/**
* Build {@link #toString()} for a node that should have new lines after some of its sub-nodes.
*/
protected String multilineToString(Collection<? extends Object> sameLine, Collection<? extends Object> ownLine) {
return joinWithName(getClass().getSimpleName(), sameLine, ownLine);
}
/**
* Zip two (potentially uneven) lists together into for {@link #toString()}.
*/
protected List<String> pairwiseToString(Collection<? extends Object> lefts, Collection<? extends Object> rights) {
List<String> pairs = new ArrayList<>(max(lefts.size(), rights.size()));
Iterator<? extends Object> left = lefts.iterator();
Iterator<? extends Object> right = rights.iterator();
while (left.hasNext() || right.hasNext()) {
pairs.add(joinWithName("Pair",
Arrays.asList(left.hasNext() ? left.next() : "<uneven>", right.hasNext() ? right.next() : "<uneven>"),
emptyList()));
}
return pairs;
}
/**
* Build a {@link #toString()} for some expressions. Usually best to use {@link #singleLineToString(Object...)} or
* {@link #multilineToString(Collection, Collection)} instead because they include the name of the node by default.
*/
protected String joinWithName(String name, Collection<? extends Object> sameLine,
Collection<? extends Object> ownLine) {
StringBuilder b = new StringBuilder();
b.append('(').append(name);
for (Object sub : sameLine) {
b.append(' ').append(sub);
}
if (ownLine.size() == 1 && sameLine.isEmpty()) {
b.append(' ').append(ownLine.iterator().next());
} else {
for (Object sub : ownLine) {
b.append("\n ").append(Objects.toString(sub).replace("\n", "\n "));
}
}
return b.append(')').toString();
}
}

View File

@ -31,6 +31,8 @@ import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.Operation;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@ -322,4 +324,24 @@ public final class EAssignment extends AExpression {
lhs.store(writer, globals); // store the lhs's value from the stack in its respective variable/field/array
}
}
@Override
public String toString() {
List<Object> subs = new ArrayList<>();
subs.add(lhs);
if (rhs != null) {
// Make sure "=" is in the symbol so this is easy to read at a glance
subs.add(operation == null ? "=" : operation.symbol + "=");
subs.add(rhs);
return singleLineToString(subs);
}
subs.add(operation.symbol);
if (pre) {
subs.add("pre");
}
if (post) {
subs.add("post");
}
return singleLineToString(subs);
}
}

View File

@ -681,4 +681,9 @@ public final class EBinary extends AExpression {
}
}
}
@Override
public String toString() {
return singleLineToString(left, operation.symbol, right);
}
}

View File

@ -114,4 +114,9 @@ public final class EBool extends AExpression {
throw createError(new IllegalStateException("Illegal tree structure."));
}
}
@Override
public String toString() {
return singleLineToString(left, operation.symbol, right);
}
}

View File

@ -56,4 +56,9 @@ public final class EBoolean extends AExpression {
void write(MethodWriter adapter, Globals globals) {
throw createError(new IllegalStateException("Illegal tree structure."));
}
@Override
public String toString() {
return singleLineToString(constant);
}
}

View File

@ -88,4 +88,9 @@ public final class ECallLocal extends AExpression {
writer.invokeStatic(CLASS_TYPE, method.method);
}
@Override
public String toString() {
return singleLineToStringWithOptionalArgs(arguments, name);
}
}

View File

@ -136,4 +136,9 @@ public final class ECapturingFunctionRef extends AExpression implements ILambda
public Type[] getCaptures() {
return new Type[] { captured.type.type };
}
@Override
public String toString() {
return singleLineToString(variable, call);
}
}

View File

@ -60,4 +60,9 @@ final class ECast extends AExpression {
writer.writeDebugInfo(location);
writer.writeCast(cast);
}
@Override
public String toString() {
return singleLineToString(cast.to, child);
}
}

View File

@ -568,4 +568,9 @@ public final class EComp extends AExpression {
writer.mark(end);
}
}
@Override
public String toString() {
return singleLineToString(left, operation.symbol, right);
}
}

View File

@ -106,4 +106,9 @@ public final class EConditional extends AExpression {
right.write(writer, globals);
writer.mark(end);
}
@Override
public String toString() {
return singleLineToString(condition, left, right);
}
}

View File

@ -89,4 +89,13 @@ final class EConstant extends AExpression {
throw createError(new IllegalStateException("Illegal tree structure."));
}
}
@Override
public String toString() {
String c = constant.toString();
if (constant instanceof String) {
c = "'" + c + "'";
}
return singleLineToString(constant.getClass().getSimpleName(), c);
}
}

View File

@ -75,4 +75,9 @@ public final class EDecimal extends AExpression {
void write(MethodWriter writer, Globals globals) {
throw createError(new IllegalStateException("Illegal tree structure."));
}
@Override
public String toString() {
return singleLineToString(value);
}
}

View File

@ -105,4 +105,9 @@ public class EElvis extends AExpression {
rhs.write(writer, globals);
writer.mark(end);
}
@Override
public String toString() {
return singleLineToString(lhs, rhs);
}
}

View File

@ -74,4 +74,9 @@ public final class EExplicit extends AExpression {
return child.cast(locals);
}
@Override
public String toString() {
return singleLineToString(type, child);
}
}

View File

@ -130,4 +130,9 @@ public final class EFunctionRef extends AExpression implements ILambda {
public Type[] getCaptures() {
return new Type[0]; // no captures
}
@Override
public String toString() {
return singleLineToString(type, call);
}
}

View File

@ -30,7 +30,7 @@ import java.util.Objects;
import java.util.Set;
/**
* Represents instanceof operator.
* Represents {@code instanceof} operator.
* <p>
* Unlike java's, this works for primitive types too.
*/
@ -96,4 +96,9 @@ public final class EInstanceof extends AExpression {
writer.instanceOf(org.objectweb.asm.Type.getType(resolvedType));
}
}
@Override
public String toString() {
return singleLineToString(expression, type);
}
}

View File

@ -19,16 +19,16 @@
package org.elasticsearch.painless.node;
import org.elasticsearch.painless.Definition;
import org.elasticsearch.painless.Definition.Method;
import org.elasticsearch.painless.Definition.Type;
import org.elasticsearch.painless.FunctionRef;
import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Locals.Variable;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.Definition.Method;
import org.elasticsearch.painless.node.SFunction.FunctionReserved;
import org.elasticsearch.painless.Definition;
import org.elasticsearch.painless.FunctionRef;
import org.elasticsearch.painless.Globals;
import org.objectweb.asm.Opcodes;
import java.lang.invoke.LambdaMetafactory;
@ -245,4 +245,9 @@ public final class ELambda extends AExpression implements ILambda {
}
return types;
}
@Override
public String toString() {
return multilineToString(pairwiseToString(paramTypeStrs, paramNameStrs), statements);
}
}

View File

@ -101,4 +101,9 @@ public final class EListInit extends AExpression {
writer.pop();
}
}
@Override
public String toString() {
return singleLineToString(values);
}
}

View File

@ -124,4 +124,9 @@ public final class EMapInit extends AExpression {
writer.pop();
}
}
@Override
public String toString() {
return singleLineToString(pairwiseToString(keys, values));
}
}

View File

@ -20,10 +20,10 @@
package org.elasticsearch.painless.node;
import org.elasticsearch.painless.Definition;
import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.Definition.Type;
import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import java.util.List;
@ -108,4 +108,9 @@ public final class ENewArray extends AExpression {
}
}
}
@Override
public String toString() {
return singleLineToStringWithOptionalArgs(arguments, type, initialize ? "init" : "dims");
}
}

View File

@ -20,12 +20,12 @@
package org.elasticsearch.painless.node;
import org.elasticsearch.painless.Definition;
import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.Definition.Method;
import org.elasticsearch.painless.Definition.Struct;
import org.elasticsearch.painless.Definition.Type;
import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import java.util.List;
@ -110,4 +110,9 @@ public final class ENewObj extends AExpression {
writer.invokeConstructor(constructor.owner.type, constructor.method);
}
@Override
public String toString() {
return singleLineToStringWithOptionalArgs(arguments, type);
}
}

View File

@ -66,4 +66,9 @@ public final class ENull extends AExpression {
void write(MethodWriter writer, Globals globals) {
writer.visitInsn(Opcodes.ACONST_NULL);
}
@Override
public String toString() {
return singleLineToString();
}
}

View File

@ -31,7 +31,7 @@ import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.MethodWriter;
/**
* Respresents a non-decimal numeric constant.
* Represents a non-decimal numeric constant.
*/
public final class ENumeric extends AExpression {
@ -121,4 +121,12 @@ public final class ENumeric extends AExpression {
void write(MethodWriter writer, Globals globals) {
throw createError(new IllegalStateException("Illegal tree structure."));
}
@Override
public String toString() {
if (radix != 10) {
return singleLineToString(value, radix);
}
return singleLineToString(value);
}
}

View File

@ -104,4 +104,23 @@ public final class ERegex extends AExpression {
throw new IllegalArgumentException("Unknown flag [" + c + "]");
}
}
@Override
public String toString() {
StringBuilder f = new StringBuilder();
if ((flags & Pattern.CANON_EQ) != 0) f.append('c');
if ((flags & Pattern.CASE_INSENSITIVE) != 0) f.append('i');
if ((flags & Pattern.LITERAL) != 0) f.append('l');
if ((flags & Pattern.MULTILINE) != 0) f.append('m');
if ((flags & Pattern.DOTALL) != 0) f.append('s');
if ((flags & Pattern.UNICODE_CHARACTER_CLASS) != 0) f.append('U');
if ((flags & Pattern.UNICODE_CASE) != 0) f.append('u');
if ((flags & Pattern.COMMENTS) != 0) f.append('x');
String p = "/" + pattern + "/";
if (f.length() == 0) {
return singleLineToString(p);
}
return singleLineToString(p, f);
}
}

View File

@ -60,4 +60,9 @@ public final class EStatic extends AExpression {
void write(MethodWriter writer, Globals globals) {
// Do nothing.
}
@Override
public String toString() {
return singleLineToString(type);
}
}

View File

@ -57,4 +57,9 @@ public final class EString extends AExpression {
void write(MethodWriter writer, Globals globals) {
throw new IllegalStateException("Illegal tree structure.");
}
@Override
public String toString() {
return singleLineToString("'" + constant.toString() + "'");
}
}

View File

@ -249,4 +249,9 @@ public final class EUnary extends AExpression {
}
}
}
@Override
public String toString() {
return singleLineToString(operation.symbol, child);
}
}

View File

@ -95,4 +95,9 @@ public final class EVariable extends AStoreable {
void store(MethodWriter writer, Globals globals) {
writer.visitVarInsn(actual.type.getOpcode(Opcodes.ISTORE), variable.getSlot());
}
@Override
public String toString() {
return singleLineToString(name);
}
}

View File

@ -117,4 +117,9 @@ public final class PBrace extends AStoreable {
void store(MethodWriter writer, Globals globals) {
sub.store(writer, globals);
}
@Override
public String toString() {
return singleLineToString(prefix, index);
}
}

View File

@ -106,4 +106,9 @@ public final class PCallInvoke extends AExpression {
prefix.write(writer, globals);
sub.write(writer, globals);
}
@Override
public String toString() {
return singleLineToStringWithOptionalArgs(arguments, prefix, name);
}
}

View File

@ -66,7 +66,7 @@ public final class PField extends AStoreable {
Sort sort = prefix.actual.sort;
if (sort == Sort.ARRAY) {
sub = new PSubArrayLength(location,prefix.actual.name, value);
sub = new PSubArrayLength(location, prefix.actual.name, value);
} else if (sort == Sort.DEF) {
sub = new PSubDefField(location, value);
} else {
@ -157,4 +157,12 @@ public final class PField extends AStoreable {
void store(MethodWriter writer, Globals globals) {
sub.store(writer, globals);
}
@Override
public String toString() {
if (nullSafe) {
return singleLineToString("nullSafe", prefix, value);
}
return singleLineToString(prefix, value);
}
}

View File

@ -97,4 +97,9 @@ final class PSubArrayLength extends AStoreable {
void store(MethodWriter writer, Globals globals) {
throw new IllegalStateException("Illegal tree structure.");
}
@Override
public String toString() {
return singleLineToString(prefix);
}
}

View File

@ -96,4 +96,9 @@ final class PSubBrace extends AStoreable {
writer.writeDebugInfo(location);
writer.arrayStore(actual.type);
}
@Override
public String toString() {
return singleLineToString(prefix, index);
}
}

View File

@ -81,4 +81,9 @@ final class PSubCallInvoke extends AExpression {
method.write(writer);
}
@Override
public String toString() {
return singleLineToStringWithOptionalArgs(arguments, prefix, method.name);
}
}

View File

@ -104,4 +104,9 @@ final class PSubDefArray extends AStoreable {
org.objectweb.asm.Type.getMethodType(Definition.VOID_TYPE.type, Definition.DEF_TYPE.type, index.actual.type, actual.type);
writer.invokeDefCall("arrayStore", methodType, DefBootstrap.ARRAY_STORE);
}
@Override
public String toString() {
return singleLineToString(prefix, index);
}
}

View File

@ -114,4 +114,9 @@ final class PSubDefCall extends AExpression {
args.addAll(pointers);
writer.invokeDefCall(name, methodType, DefBootstrap.METHOD_CALL, args.toArray());
}
@Override
public String toString() {
return singleLineToStringWithOptionalArgs(arguments, prefix, name);
}
}

View File

@ -99,4 +99,9 @@ final class PSubDefField extends AStoreable {
org.objectweb.asm.Type.getMethodType(Definition.VOID_TYPE.type, Definition.DEF_TYPE.type, actual.type);
writer.invokeDefCall(value, methodType, DefBootstrap.STORE);
}
@Override
public String toString() {
return singleLineToString(prefix, value);
}
}

View File

@ -110,4 +110,9 @@ final class PSubField extends AStoreable {
writer.putField(field.owner.type, field.javaName, field.type.type);
}
}
@Override
public String toString() {
return singleLineToString(prefix, field.name);
}
}

View File

@ -134,4 +134,9 @@ final class PSubListShortcut extends AStoreable {
writer.writePop(setter.rtn.sort.size);
}
@Override
public String toString() {
return singleLineToString(prefix, index);
}
}

View File

@ -137,4 +137,9 @@ final class PSubMapShortcut extends AStoreable {
writer.writePop(setter.rtn.sort.size);
}
@Override
public String toString() {
return singleLineToString(prefix, index);
}
}

View File

@ -67,4 +67,9 @@ public class PSubNullSafeCallInvoke extends AExpression {
guarded.write(writer, globals);
writer.mark(end);
}
@Override
public String toString() {
return singleLineToString(guarded);
}
}

View File

@ -96,4 +96,9 @@ public class PSubNullSafeField extends AStoreable {
void store(MethodWriter writer, Globals globals) {
throw createError(new IllegalArgumentException("Can't write to null safe field"));
}
@Override
public String toString() {
return singleLineToString(guarded);
}
}

View File

@ -126,4 +126,9 @@ final class PSubShortcut extends AStoreable {
writer.writePop(setter.rtn.sort.size);
}
@Override
public String toString() {
return singleLineToString(prefix, value);
}
}

View File

@ -28,6 +28,8 @@ import java.util.Collections;
import java.util.List;
import java.util.Set;
import static java.util.Collections.emptyList;
/**
* Represents a set of statements as a branch of control-flow.
*/
@ -86,4 +88,9 @@ public final class SBlock extends AStatement {
statement.write(writer, globals);
}
}
@Override
public String toString() {
return multilineToString(emptyList(), statements);
}
}

View File

@ -56,4 +56,9 @@ public final class SBreak extends AStatement {
void write(MethodWriter writer, Globals globals) {
writer.goTo(brake);
}
@Override
public String toString() {
return singleLineToString();
}
}

View File

@ -117,4 +117,9 @@ public final class SCatch extends AStatement {
writer.goTo(exception);
}
}
@Override
public String toString() {
return singleLineToString(type, name, block);
}
}

View File

@ -59,4 +59,9 @@ public final class SContinue extends AStatement {
void write(MethodWriter writer, Globals globals) {
writer.goTo(continu);
}
@Override
public String toString() {
return singleLineToString();
}
}

View File

@ -28,6 +28,8 @@ import java.util.Collections;
import java.util.List;
import java.util.Set;
import static java.util.Collections.emptyList;
/**
* Represents a series of declarations.
*/
@ -63,4 +65,9 @@ public final class SDeclBlock extends AStatement {
declaration.write(writer, globals);
}
}
@Override
public String toString() {
return multilineToString(emptyList(), declarations);
}
}

View File

@ -101,4 +101,12 @@ public final class SDeclaration extends AStatement {
writer.visitVarInsn(variable.type.type.getOpcode(Opcodes.ISTORE), variable.getSlot());
}
@Override
public String toString() {
if (expression == null) {
return singleLineToString(type, name);
}
return singleLineToString(type, name, expression);
}
}

View File

@ -125,4 +125,9 @@ public final class SDo extends AStatement {
writer.goTo(start);
writer.mark(end);
}
@Override
public String toString() {
return singleLineToString(condition, block);
}
}

View File

@ -114,4 +114,9 @@ public class SEach extends AStatement {
void write(MethodWriter writer, Globals globals) {
sub.write(writer, globals);
}
@Override
public String toString() {
return singleLineToString(type, name, expression, block);
}
}

View File

@ -82,4 +82,9 @@ public final class SExpression extends AStatement {
writer.writePop(expression.expected.sort.size);
}
}
@Override
public String toString() {
return singleLineToString(expression);
}
}

View File

@ -27,8 +27,11 @@ import org.elasticsearch.painless.MethodWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import java.util.Arrays;
import java.util.Set;
import static java.util.Collections.emptyList;
/**
* Represents a for loop.
*/
@ -203,4 +206,9 @@ public final class SFor extends AStatement {
writer.mark(end);
}
@Override
public String toString() {
return multilineToString(emptyList(), Arrays.asList(initializer, condition, afterthought, block));
}
}

View File

@ -46,6 +46,7 @@ import java.util.List;
import java.util.Objects;
import java.util.Set;
import static java.util.Collections.emptyList;
import static org.elasticsearch.painless.WriterConstants.CLASS_TYPE;
/**
@ -220,4 +221,15 @@ public final class SFunction extends AStatement {
false);
writer.push(handle);
}
@Override
public String toString() {
List<Object> description = new ArrayList<>();
description.add(rtnTypeStr);
description.add(name);
if (false == (paramTypeStrs.isEmpty() && paramNameStrs.isEmpty())) {
description.add(joinWithName("Args", pairwiseToString(paramTypeStrs, paramNameStrs), emptyList()));
}
return multilineToString(description, statements);
}
}

View File

@ -94,4 +94,9 @@ public final class SIf extends AStatement {
writer.mark(fals);
}
@Override
public String toString() {
return singleLineToString(condition, ifblock);
}
}

View File

@ -27,9 +27,12 @@ import org.elasticsearch.painless.MethodWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import java.util.Arrays;
import java.util.Objects;
import java.util.Set;
import static java.util.Collections.singleton;
/**
* Represents an if/else block.
*/
@ -128,4 +131,9 @@ public final class SIfElse extends AStatement {
writer.mark(end);
}
@Override
public String toString() {
return multilineToString(singleton(condition), Arrays.asList(ifblock, elseblock));
}
}

View File

@ -65,4 +65,9 @@ public final class SReturn extends AStatement {
expression.write(writer, globals);
writer.returnValue();
}
@Override
public String toString() {
return singleLineToString(expression);
}
}

View File

@ -47,6 +47,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import static java.util.Collections.emptyList;
import static org.elasticsearch.painless.WriterConstants.BASE_CLASS_TYPE;
import static org.elasticsearch.painless.WriterConstants.CLASS_TYPE;
import static org.elasticsearch.painless.WriterConstants.CONSTRUCTOR;
@ -332,4 +333,12 @@ public final class SSource extends AStatement {
public byte[] getBytes() {
return bytes;
}
@Override
public String toString() {
List<Object> subs = new ArrayList<>(functions.size() + statements.size());
subs.addAll(functions);
subs.addAll(statements);
return multilineToString(emptyList(), subs);
}
}

View File

@ -107,4 +107,9 @@ final class SSubEachArray extends AStatement {
writer.goTo(begin);
writer.mark(end);
}
@Override
public String toString() {
return singleLineToString(variable.type.name, variable.name, expression, block);
}
}

View File

@ -129,4 +129,9 @@ final class SSubEachIterable extends AStatement {
writer.goTo(begin);
writer.mark(end);
}
@Override
public String toString() {
return singleLineToString(variable.type.name, variable.name, expression, block);
}
}

View File

@ -64,4 +64,9 @@ public final class SThrow extends AStatement {
expression.write(writer, globals);
writer.throwException();
}
@Override
public String toString() {
return singleLineToString(expression);
}
}

View File

@ -29,6 +29,8 @@ import java.util.Collections;
import java.util.List;
import java.util.Set;
import static java.util.Collections.singleton;
/**
* Represents the try block as part of a try-catch block.
*/
@ -124,4 +126,9 @@ public final class STry extends AStatement {
writer.mark(exception);
}
}
@Override
public String toString() {
return multilineToString(singleton(block), catches);
}
}

View File

@ -134,4 +134,9 @@ public final class SWhile extends AStatement {
writer.mark(end);
}
@Override
public String toString() {
return singleLineToString(condition, block);
}
}

View File

@ -0,0 +1,905 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.painless.node;
import org.elasticsearch.painless.CompilerSettings;
import org.elasticsearch.painless.Definition;
import org.elasticsearch.painless.Definition.Cast;
import org.elasticsearch.painless.Definition.Field;
import org.elasticsearch.painless.Definition.Method;
import org.elasticsearch.painless.Definition.MethodKey;
import org.elasticsearch.painless.Definition.RuntimeClass;
import org.elasticsearch.painless.Definition.Struct;
import org.elasticsearch.painless.FeatureTest;
import org.elasticsearch.painless.Locals.Variable;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.Operation;
import org.elasticsearch.painless.antlr.Walker;
import org.elasticsearch.test.ESTestCase;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
/**
* Tests {@link Object#toString} implementations on all extensions of {@link ANode}.
*/
public class NodeToStringTests extends ESTestCase {
public void testEAssignment() {
assertToString(
"(SSource\n"
+ " (SDeclBlock (SDeclaration def i))\n"
+ " (SExpression (EAssignment (EVariable i) = (ENumeric 2)))\n"
+ " (SReturn (EVariable i)))",
"def i;\n"
+ "i = 2;\n"
+ "return i");
for (String operator : new String[] {"+", "-", "*", "/", "%", "&", "^", "|", "<<", ">>", ">>>"}) {
assertToString(
"(SSource\n"
+ " (SDeclBlock (SDeclaration def i (ENumeric 1)))\n"
+ " (SExpression (EAssignment (EVariable i) " + operator + "= (ENumeric 2)))\n"
+ " (SReturn (EVariable i)))",
"def i = 1;\n"
+ "i " + operator + "= 2;\n"
+ "return i");
}
// Compound
assertToString(
"(SSource\n"
+ " (SDeclBlock (SDeclaration def i))\n"
+ " (SReturn (EAssignment (EVariable i) = (ENumeric 2))))",
"def i;\n"
+ "return i = 2");
assertToString(
"(SSource\n"
+ " (SDeclBlock (SDeclaration def i))\n"
+ " (SReturn (EAssignment (EVariable i) ++ post)))",
"def i;\n"
+ "return i++");
assertToString(
"(SSource\n"
+ " (SDeclBlock (SDeclaration def i))\n"
+ " (SReturn (EAssignment (EVariable i) ++ pre)))",
"def i;\n"
+ "return ++i");
assertToString(
"(SSource\n"
+ " (SDeclBlock (SDeclaration def i))\n"
+ " (SReturn (EAssignment (EVariable i) -- post)))",
"def i;\n"
+ "return i--");
assertToString(
"(SSource\n"
+ " (SDeclBlock (SDeclaration def i))\n"
+ " (SReturn (EAssignment (EVariable i) -- pre)))",
"def i;\n"
+ "return --i");
}
public void testEBinary() {
assertToString( "(SSource (SReturn (EBinary (ENumeric 1) * (ENumeric 1))))", "return 1 * 1");
assertToString( "(SSource (SReturn (EBinary (ENumeric 1) / (ENumeric 1))))", "return 1 / 1");
assertToString( "(SSource (SReturn (EBinary (ENumeric 1) % (ENumeric 1))))", "return 1 % 1");
assertToString( "(SSource (SReturn (EBinary (ENumeric 1) + (ENumeric 1))))", "return 1 + 1");
assertToString( "(SSource (SReturn (EBinary (ENumeric 1) - (ENumeric 1))))", "return 1 - 1");
assertToString( "(SSource (SReturn (EBinary (EString 'asb') =~ (ERegex /cat/))))", "return 'asb' =~ /cat/");
assertToString("(SSource (SReturn (EBinary (EString 'asb') ==~ (ERegex /cat/))))", "return 'asb' ==~ /cat/");
assertToString( "(SSource (SReturn (EBinary (ENumeric 1) << (ENumeric 1))))", "return 1 << 1");
assertToString( "(SSource (SReturn (EBinary (ENumeric 1) >> (ENumeric 1))))", "return 1 >> 1");
assertToString( "(SSource (SReturn (EBinary (ENumeric 1) >>> (ENumeric 1))))", "return 1 >>> 1");
assertToString( "(SSource (SReturn (EBinary (ENumeric 1) & (ENumeric 1))))", "return 1 & 1");
assertToString( "(SSource (SReturn (EBinary (ENumeric 1) ^ (ENumeric 1))))", "return 1 ^ 1");
assertToString( "(SSource (SReturn (EBinary (ENumeric 1) | (ENumeric 1))))", "return 1 | 1");
}
public void testEBool() {
assertToString("(SSource (SReturn (EBool (EBoolean true) && (EBoolean false))))", "return true && false");
assertToString("(SSource (SReturn (EBool (EBoolean true) || (EBoolean false))))", "return true || false");
}
public void testEBoolean() {
assertToString("(SSource (SReturn (EBoolean true)))", "return true");
assertToString("(SSource (SReturn (EBoolean false)))", "return false");
}
public void testECallLocal() {
assertToString(
"(SSource\n"
+ " (SFunction def a\n"
+ " (SReturn (EBoolean true)))\n"
+ " (SReturn (ECallLocal a)))",
"def a() {\n"
+ " return true\n"
+ "}\n"
+ "return a()");
assertToString(
"(SSource\n"
+ " (SFunction def a (Args (Pair int i) (Pair int j))\n"
+ " (SReturn (EBoolean true)))\n"
+ " (SReturn (ECallLocal a (Args (ENumeric 1) (ENumeric 2)))))",
"def a(int i, int j) {\n"
+ " return true\n"
+ "}\n"
+ "return a(1, 2)");
}
public void testECapturingFunctionRef() {
assertToString(
"(SSource\n"
+ " (SDeclBlock (SDeclaration Integer x (PCallInvoke (EStatic Integer) valueOf (Args (ENumeric 5)))))\n"
+ " (SReturn (PCallInvoke (PCallInvoke (EStatic Optional) empty) orElseGet (Args (ECapturingFunctionRef x toString)))))",
"Integer x = Integer.valueOf(5);\n"
+ "return Optional.empty().orElseGet(x::toString)");
}
public void testECast() {
Location l = new Location(getTestName(), 0);
AExpression child = new EConstant(l, "test");
Cast cast = new Cast(Definition.STRING_TYPE, Definition.INT_OBJ_TYPE, true);
assertEquals("(ECast Integer (EConstant String 'test'))", new ECast(l, child, cast).toString());
l = new Location(getTestName(), 1);
child = new EBinary(l, Operation.ADD, new EConstant(l, "test"), new EConstant(l, 12));
cast = new Cast(Definition.INT_OBJ_TYPE, Definition.BOOLEAN_OBJ_TYPE, true);
assertEquals("(ECast Boolean (EBinary (EConstant String 'test') + (EConstant Integer 12)))", new ECast(l, child, cast).toString());
}
public void testEComp() {
assertToString( "(SSource (SReturn (EComp (PField (EVariable params) a) < (ENumeric 10))))", "return params.a < 10");
assertToString( "(SSource (SReturn (EComp (PField (EVariable params) a) <= (ENumeric 10))))", "return params.a <= 10");
assertToString( "(SSource (SReturn (EComp (PField (EVariable params) a) > (ENumeric 10))))", "return params.a > 10");
assertToString( "(SSource (SReturn (EComp (PField (EVariable params) a) >= (ENumeric 10))))", "return params.a >= 10");
assertToString( "(SSource (SReturn (EComp (PField (EVariable params) a) == (ENumeric 10))))", "return params.a == 10");
assertToString("(SSource (SReturn (EComp (PField (EVariable params) a) === (ENumeric 10))))", "return params.a === 10");
assertToString( "(SSource (SReturn (EComp (PField (EVariable params) a) != (ENumeric 10))))", "return params.a != 10");
assertToString("(SSource (SReturn (EComp (PField (EVariable params) a) !== (ENumeric 10))))", "return params.a !== 10");
}
public void testEConditional() {
assertToString("(SSource (SReturn (EConditional (PField (EVariable params) a) (ENumeric 1) (ENumeric 6))))",
"return params.a ? 1 : 6");
}
public void testEConstant() {
assertEquals("(EConstant String '121')", new EConstant(new Location(getTestName(), 0), "121").toString());
assertEquals("(EConstant String '92 ')", new EConstant(new Location(getTestName(), 0), "92 ").toString());
assertEquals("(EConstant Integer 1237)", new EConstant(new Location(getTestName(), 1), 1237).toString());
assertEquals("(EConstant Boolean true)", new EConstant(new Location(getTestName(), 2), true).toString());
}
public void testEDecimal() {
assertToString("(SSource (SReturn (EDecimal 1.0)))", "return 1.0");
assertToString("(SSource (SReturn (EDecimal 14.121d)))", "return 14.121d");
assertToString("(SSource (SReturn (EDecimal 2234.1f)))", "return 2234.1f");
assertToString("(SSource (SReturn (EDecimal 14.121D)))", "return 14.121D");
assertToString("(SSource (SReturn (EDecimal 1234.1F)))", "return 1234.1F");
}
public void testEElvis() {
assertToString("(SSource (SReturn (EElvis (PField (EVariable params) a) (ENumeric 1))))", "return params.a ?: 1");
}
public void testEExplicit() {
assertToString("(SSource (SReturn (EExplicit byte (PField (EVariable params) a))))", "return (byte)(params.a)");
}
public void testEFunctionRef() {
assertToString(
"(SSource (SReturn "
+ "(PCallInvoke (PCallInvoke (EStatic Optional) empty) orElseGet (Args (EFunctionRef Optional empty)))))",
"return Optional.empty().orElseGet(Optional::empty)");
}
public void testEInstanceOf() {
assertToString("(SSource (SReturn (EInstanceof (ENewObj Object) Object)))", "return new Object() instanceof Object");
assertToString("(SSource (SReturn (EInstanceof (ENumeric 12) double)))", "return 12 instanceof double");
}
public void testELambda() {
assertToString(
"(SSource (SReturn (PCallInvoke (PCallInvoke (EStatic Optional) empty) orElseGet (Args "
+ "(ELambda (SReturn (ENumeric 1)))))))",
"return Optional.empty().orElseGet(() -> {\n"
+ " return 1\n"
+ "})");
assertToString(
"(SSource (SReturn (PCallInvoke (PCallInvoke (EStatic Optional) empty) orElseGet (Args "
+ "(ELambda (SReturn (ENumeric 1)))))))",
"return Optional.empty().orElseGet(() -> 1)");
assertToString(
"(SSource (SReturn (PCallInvoke (PCallInvoke (PCallInvoke (EListInit (ENumeric 1) (ENumeric 2) (ENumeric 3)) stream) "
+ "mapToInt (Args (ELambda (Pair def x)\n"
+ " (SReturn (EBinary (EVariable x) + (ENumeric 1)))))) sum)))",
"return [1, 2, 3].stream().mapToInt((def x) -> {\n"
+ " return x + 1\n"
+ "}).sum()");
assertToString(
"(SSource (SReturn (PCallInvoke (PCallInvoke (PCallInvoke (EListInit (ENumeric 1) (ENumeric 2) (ENumeric 3)) stream) "
+ "mapToInt (Args (ELambda (Pair def x)\n"
+ " (SReturn (EBinary (EVariable x) + (ENumeric 1)))))) sum)))",
"return [1, 2, 3].stream().mapToInt(x -> x + 1).sum()");
assertToString(
"(SSource (SReturn (PCallInvoke (EListInit (EString 'a') (EString 'b')) sort (Args (ELambda (Pair def a) (Pair def b)\n"
+ " (SReturn (EBinary (PCallInvoke (EVariable a) length) - (PCallInvoke (EVariable b) length))))))))",
"return ['a', 'b'].sort((def a, def b) -> {\n"
+ " return a.length() - b.length()\n"
+ "})");
assertToString(
"(SSource (SReturn (PCallInvoke (EListInit (EString 'a') (EString 'b')) sort (Args (ELambda (Pair def a) (Pair def b)\n"
+ " (SReturn (EBinary (PCallInvoke (EVariable a) length) - (PCallInvoke (EVariable b) length))))))))",
"return ['a', 'b'].sort((a, b) -> a.length() - b.length())");
assertToString(
"(SSource (SReturn (PCallInvoke (EListInit (EString 'a') (EString 'b')) sort (Args (ELambda (Pair def a) (Pair def b)\n"
+ " (SIf (EComp (EVariable a) < (EVariable b)) (SBlock "
+ "(SReturn (EBinary (PCallInvoke (EVariable a) length) - (PCallInvoke (EVariable b) length)))))\n"
+ " (SReturn (ENumeric 1)))))))",
"return ['a', 'b'].sort((def a, def b) -> {\n"
+ " if (a < b) {\n"
+ " return a.length() - b.length()\n"
+ " }\n"
+ " return 1\n"
+ "})");
}
public void testEListInit() {
assertToString("(SSource (SReturn (EListInit (ENumeric 1) (ENumeric 2) (EString 'cat') (EString 'dog') (ENewObj Object))))",
"return [1, 2, 'cat', 'dog', new Object()]");
assertToString("(SSource (SReturn (EListInit)))", "return []");
}
public void testEMapInit() {
assertToString("(SSource (SReturn (EMapInit "
+ "(Pair (EString 'a') (ENumeric 1)) "
+ "(Pair (EString 'b') (ENumeric 3)) "
+ "(Pair (ENumeric 12) (ENewObj Object)))))",
"return ['a': 1, 'b': 3, 12: new Object()]");
assertToString("(SSource (SReturn (EMapInit)))", "return [:]");
}
public void testENewArray() {
assertToString("(SSource (SReturn (ENewArray int dims (Args (ENumeric 10)))))", "return new int[10]");
assertToString("(SSource (SReturn (ENewArray int dims (Args (ENumeric 10) (ENumeric 4) (ENumeric 5)))))",
"return new int[10][4][5]");
assertToString("(SSource (SReturn (ENewArray int init (Args (ENumeric 1) (ENumeric 2) (ENumeric 3)))))",
"return new int[] {1, 2, 3}");
assertToString("(SSource (SReturn (ENewArray def init (Args (ENumeric 1) (ENumeric 2) (EString 'bird')))))",
"return new def[] {1, 2, 'bird'}");
}
public void testENewObj() {
assertToString("(SSource (SReturn (ENewObj Object)))", "return new Object()");
assertToString("(SSource (SReturn (ENewObj DateTimeException (Args (EString 'test')))))", "return new DateTimeException('test')");
}
public void testENull() {
assertToString("(SSource (SReturn (ENull)))", "return null");
}
public void testENumeric() {
assertToString("(SSource (SReturn (ENumeric 1)))", "return 1");
assertToString("(SSource (SReturn (ENumeric 114121d)))", "return 114121d");
assertToString("(SSource (SReturn (ENumeric 114134f)))", "return 114134f");
assertToString("(SSource (SReturn (ENumeric 114121D)))", "return 114121D");
assertToString("(SSource (SReturn (ENumeric 111234F)))", "return 111234F");
assertToString("(SSource (SReturn (ENumeric 774121l)))", "return 774121l");
assertToString("(SSource (SReturn (ENumeric 881234L)))", "return 881234L");
assertToString("(SSource (SReturn (ENumeric 1 16)))", "return 0x1");
assertToString("(SSource (SReturn (ENumeric 774121l 16)))", "return 0x774121l");
assertToString("(SSource (SReturn (ENumeric 881234L 16)))", "return 0x881234L");
assertToString("(SSource (SReturn (ENumeric 1 8)))", "return 01");
assertToString("(SSource (SReturn (ENumeric 774121l 8)))", "return 0774121l");
assertToString("(SSource (SReturn (ENumeric 441234L 8)))", "return 0441234L");
}
public void testERegex() {
assertToString("(SSource (SReturn (ERegex /foo/)))", "return /foo/");
assertToString("(SSource (SReturn (ERegex /foo/ cix)))", "return /foo/cix");
assertToString("(SSource (SReturn (ERegex /foo/ cix)))", "return /foo/xci");
}
public void testEStatic() {
assertToString("(SSource (SReturn (PCallInvoke (EStatic Optional) empty)))", "return Optional.empty()");
}
public void testEString() {
assertToString("(SSource (SReturn (EString 'foo')))", "return 'foo'");
assertToString("(SSource (SReturn (EString ' oo')))", "return ' oo'");
assertToString("(SSource (SReturn (EString 'fo ')))", "return 'fo '");
assertToString("(SSource (SReturn (EString ' o ')))", "return ' o '");
}
public void testEUnary() {
assertToString("(SSource (SReturn (EUnary ! (EBoolean true))))", "return !true");
assertToString("(SSource (SReturn (EUnary ~ (ENumeric 1))))", "return ~1");
assertToString("(SSource (SReturn (EUnary + (ENumeric 1))))", "return +1");
assertToString("(SSource (SReturn (EUnary - (ENumeric 1))))", "return -(1)");
}
public void testEVariable() {
assertToString("(SSource (SReturn (EVariable params)))", "return params");
assertToString(
"(SSource\n"
+ " (SDeclBlock (SDeclaration def a (ENumeric 1)))\n"
+ " (SReturn (EVariable a)))",
"def a = 1;\n"
+ "return a");
}
public void testPBrace() {
assertToString("(SSource (SReturn (PBrace (PField (EVariable params) a) (ENumeric 10))))", "return params.a[10]");
assertToString("(SSource (SReturn (PBrace (EVariable params) (EString 'a'))))", "return params['a']");
}
public void testPCallInvoke() {
assertToString("(SSource (SReturn (PCallInvoke (EStatic Optional) empty)))", "return Optional.empty()");
assertToString("(SSource (SReturn (PCallInvoke (EStatic Optional) of (Args (ENumeric 1)))))", "return Optional.of(1)");
assertToString("(SSource (SReturn (PCallInvoke (EStatic Objects) equals (Args (ENumeric 1) (ENumeric 2)))))",
"return Objects.equals(1, 2)");
assertToString("(SSource (SReturn (PCallInvoke (EVariable params) equals (Args (ENumeric 1)))))", "return params.equals(1)");
}
public void testPField() {
assertToString("(SSource (SReturn (PField (EVariable params) a)))", "return params.a");
assertToString("(SSource (SReturn (PField nullSafe (EVariable params) a)))", "return params?.a");
assertToString(
"(SSource\n"
+ " (SDeclBlock (SDeclaration int[] a (ENewArray int dims (Args (ENumeric 10)))))\n"
+ " (SReturn (PField (EVariable a) length)))",
"int[] a = new int[10];\n"
+ "return a.length");
assertToString(
"(SSource\n"
+ " (SDeclBlock (SDeclaration org.elasticsearch.painless.FeatureTest a (ENewObj org.elasticsearch.painless.FeatureTest)))\n"
+ " (SExpression (EAssignment (PField (EVariable a) x) = (ENumeric 10)))\n"
+ " (SReturn (PField (EVariable a) x)))",
"org.elasticsearch.painless.FeatureTest a = new org.elasticsearch.painless.FeatureTest();\n"
+ "a.x = 10;\n"
+ "return a.x");
}
public void testPSubArrayLength() {
Location l = new Location(getTestName(), 0);
PSubArrayLength node = new PSubArrayLength(l, "int", "a");
node.prefix = new EVariable(l, "a");
assertEquals("(PSubArrayLength (EVariable a))", node.toString());
assertEquals("(PSubNullSafeField (PSubArrayLength (EVariable a)))", new PSubNullSafeField(l, node).toString());
}
public void testPSubBrace() {
Location l = new Location(getTestName(), 0);
PSubBrace node = new PSubBrace(l, Definition.INT_TYPE, new ENumeric(l, "1", 10));
node.prefix = new EVariable(l, "a");
assertEquals("(PSubBrace (EVariable a) (ENumeric 1))", node.toString());
}
public void testPSubCallInvoke() {
Location l = new Location(getTestName(), 0);
RuntimeClass c = Definition.getRuntimeClass(Integer.class);
Method m = c.methods.get(new MethodKey("toString", 0));
PSubCallInvoke node = new PSubCallInvoke(l, m, null, emptyList());
node.prefix = new EVariable(l, "a");
assertEquals("(PSubCallInvoke (EVariable a) toString)", node.toString());
assertEquals("(PSubNullSafeCallInvoke (PSubCallInvoke (EVariable a) toString))", new PSubNullSafeCallInvoke(l, node).toString());
l = new Location(getTestName(), 1);
m = c.methods.get(new MethodKey("equals", 1));
node = new PSubCallInvoke(l, m, null, singletonList(new EVariable(l, "b")));
node.prefix = new EVariable(l, "a");
assertEquals("(PSubCallInvoke (EVariable a) equals (Args (EVariable b)))", node.toString());
assertEquals("(PSubNullSafeCallInvoke (PSubCallInvoke (EVariable a) equals (Args (EVariable b))))",
new PSubNullSafeCallInvoke(l, node).toString());
}
public void testPSubDefArray() {
Location l = new Location(getTestName(), 0);
PSubDefArray node = new PSubDefArray(l, new EConstant(l, 1));
node.prefix = new EVariable(l, "a");
assertEquals("(PSubDefArray (EVariable a) (EConstant Integer 1))", node.toString());
}
public void testPSubDefCall() {
Location l = new Location(getTestName(), 0);
PSubDefCall node = new PSubDefCall(l, "toString", emptyList());
node.prefix = new EVariable(l, "a");
assertEquals("(PSubDefCall (EVariable a) toString)", node.toString());
assertEquals("(PSubNullSafeCallInvoke (PSubDefCall (EVariable a) toString))", new PSubNullSafeCallInvoke(l, node).toString());
l = new Location(getTestName(), 0);
node = new PSubDefCall(l, "equals", singletonList(new EVariable(l, "b")));
node.prefix = new EVariable(l, "a");
assertEquals("(PSubDefCall (EVariable a) equals (Args (EVariable b)))", node.toString());
assertEquals("(PSubNullSafeCallInvoke (PSubDefCall (EVariable a) equals (Args (EVariable b))))",
new PSubNullSafeCallInvoke(l, node).toString());
l = new Location(getTestName(), 0);
node = new PSubDefCall(l, "superWeird", Arrays.asList(new EVariable(l, "b"), new EVariable(l, "c"), new EVariable(l, "d")));
node.prefix = new EVariable(l, "a");
assertEquals("(PSubDefCall (EVariable a) superWeird (Args (EVariable b) (EVariable c) (EVariable d)))", node.toString());
assertEquals("(PSubNullSafeCallInvoke (PSubDefCall (EVariable a) superWeird (Args (EVariable b) (EVariable c) (EVariable d))))",
new PSubNullSafeCallInvoke(l, node).toString());
}
public void testPSubDefField() {
Location l = new Location(getTestName(), 0);
PSubDefField node = new PSubDefField(l, "ok");
node.prefix = new EVariable(l, "a");
assertEquals("(PSubDefField (EVariable a) ok)", node.toString());
assertEquals("(PSubNullSafeCallInvoke (PSubDefField (EVariable a) ok))", new PSubNullSafeCallInvoke(l, node).toString());
}
public void testPSubField() {
Location l = new Location(getTestName(), 0);
Struct s = Definition.getType(Boolean.class.getSimpleName()).struct;
Field f = s.staticMembers.get("TRUE");
PSubField node = new PSubField(l, f);
node.prefix = new EStatic(l, "Boolean");
assertEquals("(PSubField (EStatic Boolean) TRUE)", node.toString());
assertEquals("(PSubNullSafeCallInvoke (PSubField (EStatic Boolean) TRUE))", new PSubNullSafeCallInvoke(l, node).toString());
}
public void testPSubListShortcut() {
Location l = new Location(getTestName(), 0);
Struct s = Definition.getType(List.class.getSimpleName()).struct;
PSubListShortcut node = new PSubListShortcut(l, s, new EConstant(l, 1));
node.prefix = new EVariable(l, "a");
assertEquals("(PSubListShortcut (EVariable a) (EConstant Integer 1))", node.toString());
assertEquals("(PSubNullSafeCallInvoke (PSubListShortcut (EVariable a) (EConstant Integer 1)))",
new PSubNullSafeCallInvoke(l, node).toString());
l = new Location(getTestName(), 0);
s = Definition.getType(List.class.getSimpleName()).struct;
node = new PSubListShortcut(l, s, new EBinary(l, Operation.ADD, new EConstant(l, 1), new EConstant(l, 4)));
node.prefix = new EVariable(l, "a");
assertEquals("(PSubListShortcut (EVariable a) (EBinary (EConstant Integer 1) + (EConstant Integer 4)))", node.toString());
}
public void testPSubMapShortcut() {
Location l = new Location(getTestName(), 0);
Struct s = Definition.getType(Map.class.getSimpleName()).struct;
PSubMapShortcut node = new PSubMapShortcut(l, s, new EConstant(l, "cat"));
node.prefix = new EVariable(l, "a");
assertEquals("(PSubMapShortcut (EVariable a) (EConstant String 'cat'))", node.toString());
assertEquals("(PSubNullSafeCallInvoke (PSubMapShortcut (EVariable a) (EConstant String 'cat')))",
new PSubNullSafeCallInvoke(l, node).toString());
l = new Location(getTestName(), 1);
s = Definition.getType(Map.class.getSimpleName()).struct;
node = new PSubMapShortcut(l, s, new EBinary(l, Operation.ADD, new EConstant(l, 1), new EConstant(l, 4)));
node.prefix = new EVariable(l, "a");
assertEquals("(PSubMapShortcut (EVariable a) (EBinary (EConstant Integer 1) + (EConstant Integer 4)))", node.toString());
}
public void testPSubShortcut() {
Location l = new Location(getTestName(), 0);
Struct s = Definition.getType(FeatureTest.class.getName()).struct;
Method getter = s.methods.get(new MethodKey("getX", 0));
Method setter = s.methods.get(new MethodKey("setX", 1));
PSubShortcut node = new PSubShortcut(l, "x", FeatureTest.class.getName(), getter, setter);
node.prefix = new EVariable(l, "a");
assertEquals("(PSubShortcut (EVariable a) x)", node.toString());
assertEquals("(PSubNullSafeCallInvoke (PSubShortcut (EVariable a) x))",
new PSubNullSafeCallInvoke(l, node).toString());
}
public void testSBreak() {
assertToString(
"(SSource\n"
+ " (SDeclBlock (SDeclaration int itr (ENumeric 2)))\n"
+ " (SDeclBlock (SDeclaration int a (ENumeric 1)))\n"
+ " (SDeclBlock (SDeclaration int b (ENumeric 1)))\n"
+ " (SDo (EComp (EVariable b) < (ENumeric 1000)) (SBlock\n"
+ " (SExpression (EAssignment (EVariable itr) ++ post))\n"
+ " (SIf (EComp (EVariable itr) > (ENumeric 10000)) (SBlock (SBreak)))\n"
+ " (SDeclBlock (SDeclaration int tmp (EVariable a)))\n"
+ " (SExpression (EAssignment (EVariable a) = (EVariable b)))\n"
+ " (SExpression (EAssignment (EVariable b) = (EBinary (EVariable tmp) + (EVariable b))))))\n"
+ " (SReturn (EVariable b)))",
"int itr = 2;\n"
+ "int a = 1;\n"
+ "int b = 1;\n"
+ "do {\n"
+ " itr++;\n"
+ " if (itr > 10000) {\n"
+ " break\n"
+ " }\n"
+ " int tmp = a;\n"
+ " a = b;\n"
+ " b = tmp + b\n"
+ "} while (b < 1000);\n"
+ "return b");
}
public void testSContinue() {
assertToString(
"(SSource\n"
+ " (SDeclBlock (SDeclaration int itr (ENumeric 2)))\n"
+ " (SDeclBlock (SDeclaration int a (ENumeric 1)))\n"
+ " (SDeclBlock (SDeclaration int b (ENumeric 1)))\n"
+ " (SDo (EComp (EVariable b) < (ENumeric 1000)) (SBlock\n"
+ " (SExpression (EAssignment (EVariable itr) ++ post))\n"
+ " (SIf (EComp (EVariable itr) < (ENumeric 10000)) (SBlock (SContinue)))\n"
+ " (SDeclBlock (SDeclaration int tmp (EVariable a)))\n"
+ " (SExpression (EAssignment (EVariable a) = (EVariable b)))\n"
+ " (SExpression (EAssignment (EVariable b) = (EBinary (EVariable tmp) + (EVariable b))))))\n"
+ " (SReturn (EVariable b)))",
"int itr = 2;\n"
+ "int a = 1;\n"
+ "int b = 1;\n"
+ "do {\n"
+ " itr++;\n"
+ " if (itr < 10000) {\n"
+ " continue\n"
+ " }\n"
+ " int tmp = a;\n"
+ " a = b;\n"
+ " b = tmp + b\n"
+ "} while (b < 1000);\n"
+ "return b");
}
public void testSDeclBlock() {
assertToString(
"(SSource\n"
+ " (SDeclBlock (SDeclaration def a))\n"
+ " (SExpression (EAssignment (EVariable a) = (ENumeric 10)))\n"
+ " (SReturn (EVariable a)))",
"def a;\n"
+ "a = 10;\n"
+ "return a");
assertToString(
"(SSource\n"
+ " (SDeclBlock (SDeclaration def a (ENumeric 10)))\n"
+ " (SReturn (EVariable a)))",
"def a = 10;\n"
+ "return a");
assertToString(
"(SSource\n"
+ " (SDeclBlock\n"
+ " (SDeclaration def a)\n"
+ " (SDeclaration def b)\n"
+ " (SDeclaration def c))\n"
+ " (SReturn (EVariable a)))",
"def a, b, c;\n"
+ "return a");
assertToString(
"(SSource\n"
+ " (SDeclBlock\n"
+ " (SDeclaration def a (ENumeric 10))\n"
+ " (SDeclaration def b (ENumeric 20))\n"
+ " (SDeclaration def c (ENumeric 100)))\n"
+ " (SReturn (EVariable a)))",
"def a = 10, b = 20, c = 100;\n"
+ "return a");
assertToString(
"(SSource\n"
+ " (SDeclBlock\n"
+ " (SDeclaration def a (ENumeric 10))\n"
+ " (SDeclaration def b)\n"
+ " (SDeclaration def c (ENumeric 100)))\n"
+ " (SReturn (EVariable a)))",
"def a = 10, b, c = 100;\n"
+ "return a");
assertToString(
"(SSource\n"
+ " (SIf (PField (EVariable params) a) (SBlock\n"
+ " (SDeclBlock\n"
+ " (SDeclaration def a (ENumeric 10))\n"
+ " (SDeclaration def b)\n"
+ " (SDeclaration def c (ENumeric 100)))\n"
+ " (SReturn (EVariable a))))\n"
+ " (SReturn (EBoolean false)))",
"if (params.a) {"
+ " def a = 10, b, c = 100;\n"
+ " return a\n"
+ "}\n"
+ "return false");
}
public void testSDo() {
assertToString(
"(SSource\n"
+ " (SDeclBlock (SDeclaration int itr (ENumeric 2)))\n"
+ " (SDeclBlock (SDeclaration int a (ENumeric 1)))\n"
+ " (SDeclBlock (SDeclaration int b (ENumeric 1)))\n"
+ " (SDo (EComp (EVariable b) < (ENumeric 1000)) (SBlock\n"
+ " (SExpression (EAssignment (EVariable itr) ++ post))\n"
+ " (SDeclBlock (SDeclaration int tmp (EVariable a)))\n"
+ " (SExpression (EAssignment (EVariable a) = (EVariable b)))\n"
+ " (SExpression (EAssignment (EVariable b) = (EBinary (EVariable tmp) + (EVariable b))))))\n"
+ " (SReturn (EVariable b)))",
"int itr = 2;\n"
+ "int a = 1;\n"
+ "int b = 1;\n"
+ "do {\n"
+ " itr++;\n"
+ " int tmp = a;\n"
+ " a = b;\n"
+ " b = tmp + b\n"
+ "} while (b < 1000);\n"
+ "return b");
}
public void testSEach() {
assertToString(
"(SSource\n"
+ " (SDeclBlock (SDeclaration int l (ENumeric 0)))\n"
+ " (SEach String s (EListInit (EString 'cat') (EString 'dog') (EString 'chicken')) (SBlock "
+ "(SExpression (EAssignment (EVariable l) += (PCallInvoke (EVariable s) length)))))\n"
+ " (SReturn (EVariable l)))",
"int l = 0;\n"
+ "for (String s : ['cat', 'dog', 'chicken']) {\n"
+ " l += s.length()\n"
+ "}\n"
+ "return l");
assertToString(
"(SSource\n"
+ " (SDeclBlock (SDeclaration int l (ENumeric 0)))\n"
+ " (SEach String s (EListInit (EString 'cat') (EString 'dog') (EString 'chicken')) (SBlock\n"
+ " (SDeclBlock (SDeclaration String s2 (EBinary (EString 'dire ') + (EVariable s))))\n"
+ " (SExpression (EAssignment (EVariable l) += (PCallInvoke (EVariable s2) length)))))\n"
+ " (SReturn (EVariable l)))",
"int l = 0;\n"
+ "for (String s : ['cat', 'dog', 'chicken']) {\n"
+ " String s2 = 'dire ' + s;\n"
+ " l += s2.length()\n"
+ "}\n"
+ "return l");
}
public void testSFor() {
assertToString(
"(SSource\n"
+ " (SDeclBlock (SDeclaration int sum (ENumeric 0)))\n"
+ " (SFor\n"
+ " (SDeclBlock (SDeclaration int i (ENumeric 0)))\n"
+ " (EComp (EVariable i) < (ENumeric 1000))\n"
+ " (EAssignment (EVariable i) ++ post)\n"
+ " (SBlock (SExpression (EAssignment (EVariable sum) += (EVariable i)))))\n"
+ " (SReturn (EVariable sum)))",
"int sum = 0;\n"
+ "for (int i = 0; i < 1000; i++) {\n"
+ " sum += i\n"
+ "}\n"
+ "return sum");
assertToString(
"(SSource\n"
+ " (SDeclBlock (SDeclaration int sum (ENumeric 0)))\n"
+ " (SFor\n"
+ " (SDeclBlock (SDeclaration int i (ENumeric 0)))\n"
+ " (EComp (EVariable i) < (ENumeric 1000))\n"
+ " (EAssignment (EVariable i) ++ post)\n"
+ " (SBlock (SFor\n"
+ " (SDeclBlock (SDeclaration int j (ENumeric 0)))\n"
+ " (EComp (EVariable j) < (ENumeric 1000))\n"
+ " (EAssignment (EVariable j) ++ post)\n"
+ " (SBlock (SExpression (EAssignment (EVariable sum) += (EBinary (EVariable i) * (EVariable j))))))))\n"
+ " (SReturn (EVariable sum)))",
"int sum = 0;\n"
+ "for (int i = 0; i < 1000; i++) {\n"
+ " for (int j = 0; j < 1000; j++) {\n"
+ " sum += i * j\n"
+ " }\n"
+ "}\n"
+ "return sum");
}
public void testSIf() {
assertToString(
"(SSource (SIf (PField (EVariable param) a) (SBlock (SReturn (EBoolean true)))))",
"if (param.a) {\n"
+ " return true\n"
+"}");
assertToString(
"(SSource (SIf (PField (EVariable param) a) (SBlock\n"
+ " (SIf (PField (EVariable param) b) (SBlock (SReturn (EBoolean true))))\n"
+ " (SReturn (EBoolean false)))))",
"if (param.a) {\n"
+ " if (param.b) {\n"
+ " return true\n"
+ " }\n"
+ " return false\n"
+"}");
}
public void testSIfElse() {
assertToString(
"(SSource (SIfElse (PField (EVariable param) a)\n"
+ " (SBlock (SReturn (EBoolean true)))\n"
+ " (SBlock (SReturn (EBoolean false)))))",
"if (param.a) {\n"
+ " return true\n"
+ "} else {\n"
+ " return false\n"
+ "}");
assertToString(
"(SSource\n"
+ " (SDeclBlock (SDeclaration int i (ENumeric 0)))\n"
+ " (SIfElse (PField (EVariable param) a)\n"
+ " (SBlock (SIfElse (PField (EVariable param) b)\n"
+ " (SBlock (SReturn (EBoolean true)))\n"
+ " (SBlock (SReturn (EString 'cat')))))\n"
+ " (SBlock (SReturn (EBoolean false)))))",
"int i = 0;\n"
+ "if (param.a) {\n"
+ " if (param.b) {\n"
+ " return true\n"
+ " } else {\n"
+ " return 'cat'\n"
+ " }\n"
+ "} else {"
+ " return false\n"
+ "}");
}
public void testSSubEachArray() {
Location l = new Location(getTestName(), 0);
Variable v = new Variable(l, "test", Definition.INT_TYPE, 5, false);
AExpression e = new ENewArray(l, "int", Arrays.asList(new EConstant(l, 1), new EConstant(l, 2), new EConstant(l, 3)), true);
SBlock b = new SBlock(l, singletonList(new SReturn(l, new EConstant(l, 5))));
SSubEachArray node = new SSubEachArray(l, v, e, b);
assertEquals(
"(SSubEachArray int test (ENewArray int init (Args (EConstant Integer 1) (EConstant Integer 2) (EConstant Integer 3))) "
+ "(SBlock (SReturn (EConstant Integer 5))))",
node.toString());
}
public void testSSubEachIterable() {
Location l = new Location(getTestName(), 0);
Variable v = new Variable(l, "test", Definition.INT_TYPE, 5, false);
AExpression e = new EListInit(l, Arrays.asList(new EConstant(l, 1), new EConstant(l, 2), new EConstant(l, 3)));
SBlock b = new SBlock(l, singletonList(new SReturn(l, new EConstant(l, 5))));
SSubEachIterable node = new SSubEachIterable(l, v, e, b);
assertEquals(
"(SSubEachIterable int test (EListInit (EConstant Integer 1) (EConstant Integer 2) (EConstant Integer 3)) (SBlock "
+ "(SReturn (EConstant Integer 5))))",
node.toString());
}
public void testSThrow() {
assertToString("(SSource (SThrow (ENewObj RuntimeException)))", "throw new RuntimeException()");
}
public void testSWhile() {
assertToString(
"(SSource\n"
+ " (SDeclBlock (SDeclaration int i (ENumeric 0)))\n"
+ " (SWhile (EComp (EVariable i) < (ENumeric 10)) (SBlock (SExpression (EAssignment (EVariable i) ++ post))))\n"
+ " (SReturn (EVariable i)))",
"int i = 0;\n"
+ "while (i < 10) {\n"
+ " i++\n"
+ "}\n"
+ "return i");
}
public void testSFunction() {
assertToString(
"(SSource\n"
+ " (SFunction def a\n"
+ " (SReturn (EBoolean true)))\n"
+ " (SReturn (EBoolean true)))",
"def a() {\n"
+ " return true\n"
+ "}\n"
+ "return true");
assertToString(
"(SSource\n"
+ " (SFunction def a (Args (Pair int i) (Pair int j))\n"
+ " (SReturn (EBoolean true)))\n"
+ " (SReturn (EBoolean true)))",
"def a(int i, int j) {\n"
+ " return true\n"
+ "}\n"
+ "return true");
assertToString(
"(SSource\n"
+ " (SFunction def a (Args (Pair int i) (Pair int j))\n"
+ " (SIf (EComp (EVariable i) < (EVariable j)) (SBlock (SReturn (EBoolean true))))\n"
+ " (SDeclBlock (SDeclaration int k (EBinary (EVariable i) + (EVariable j))))\n"
+ " (SReturn (EVariable k)))\n"
+ " (SReturn (EBoolean true)))",
"def a(int i, int j) {\n"
+ " if (i < j) {\n"
+ " return true\n"
+ " }\n"
+ " int k = i + j;\n"
+ " return k\n"
+ "}\n"
+ "return true");
assertToString(
"(SSource\n"
+ " (SFunction def a\n"
+ " (SReturn (EBoolean true)))\n"
+ " (SFunction def b\n"
+ " (SReturn (EBoolean false)))\n"
+ " (SReturn (EBoolean true)))",
"def a() {\n"
+ " return true\n"
+ "}\n"
+ "def b() {\n"
+ " return false\n"
+ "}\n"
+ "return true");
}
public void testSTryAndSCatch() {
assertToString(
"(SSource (STry (SBlock (SReturn (ENumeric 1)))\n"
+ " (SCatch Exception e (SBlock (SReturn (ENumeric 2))))))",
"try {\n"
+ " return 1\n"
+ "} catch (Exception e) {\n"
+ " return 2\n"
+ "}");
assertToString(
"(SSource (STry (SBlock\n"
+ " (SDeclBlock (SDeclaration int i (ENumeric 1)))\n"
+ " (SReturn (ENumeric 1)))\n"
+ " (SCatch Exception e (SBlock (SReturn (ENumeric 2))))))",
"try {\n"
+ " int i = 1;"
+ " return 1\n"
+ "} catch (Exception e) {\n"
+ " return 2\n"
+ "}");
assertToString(
"(SSource (STry (SBlock (SReturn (ENumeric 1)))\n"
+ " (SCatch Exception e (SBlock\n"
+ " (SDeclBlock (SDeclaration int i (ENumeric 1)))\n"
+ " (SReturn (ENumeric 2))))))",
"try {\n"
+ " return 1\n"
+ "} catch (Exception e) {"
+ " int i = 1;\n"
+ " return 2\n"
+ "}");
assertToString(
"(SSource (STry (SBlock (SReturn (ENumeric 1)))\n"
+ " (SCatch NullPointerException e (SBlock (SReturn (ENumeric 2))))\n"
+ " (SCatch Exception e (SBlock (SReturn (ENumeric 3))))))",
"try {\n"
+ " return 1\n"
+ "} catch (NullPointerException e) {\n"
+ " return 2\n"
+ "} catch (Exception e) {\n"
+ " return 3\n"
+ "}");
}
private void assertToString(String expected, String code) {
assertEquals(expected, walk(code).toString());
}
private SSource walk(String code) {
CompilerSettings compilerSettings = new CompilerSettings();
compilerSettings.setRegexesEnabled(true);
try {
return Walker.buildPainlessTree(getTestName(), code, compilerSettings, null);
} catch (Exception e) {
throw new AssertionError("Failed to compile: " + code, e);
}
}
}