Added initial infrastructure to allow for each to be made.
This commit is contained in:
parent
2d8030f09f
commit
f5be0982d9
|
@ -32,6 +32,10 @@ LBRACE: '[';
|
|||
RBRACE: ']';
|
||||
LP: '(';
|
||||
RP: ')';
|
||||
// We switch modes after a dot to ensure there are not conflicts
|
||||
// between shortcuts and decimal values. Without the mode switch
|
||||
// shortcuts such as id.0.0 will fail because 0.0 will be interpreted
|
||||
// as a decimal value instead of two individual list-style shortcuts.
|
||||
DOT: '.' -> mode(AFTER_DOT);
|
||||
COMMA: ',';
|
||||
SEMICOLON: ';';
|
||||
|
|
|
@ -104,17 +104,16 @@ final class Compiler {
|
|||
SSource root = Walker.buildPainlessTree(source, reserved, settings);
|
||||
Variables variables = Analyzer.analyze(reserved, root);
|
||||
BitSet expressions = new BitSet(source.length());
|
||||
|
||||
byte[] bytes = Writer.write(settings, name, source, variables, root, expressions);
|
||||
|
||||
try {
|
||||
Class<? extends Executable> clazz = loader.define(CLASS_NAME, bytes);
|
||||
java.lang.reflect.Constructor<? extends Executable> constructor =
|
||||
java.lang.reflect.Constructor<? extends Executable> constructor =
|
||||
clazz.getConstructor(String.class, String.class, BitSet.class);
|
||||
|
||||
return constructor.newInstance(name, source, expressions);
|
||||
} catch (Exception exception) { // Catch everything to let the user know this is something caused internally.
|
||||
throw new IllegalStateException(
|
||||
"An internal error occurred attempting to define the script [" + name + "].", exception);
|
||||
throw new IllegalStateException("An internal error occurred attempting to define the script [" + name + "].", exception);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -123,7 +123,13 @@ final class ScriptImpl implements ExecutableScript, LeafSearchScript {
|
|||
throw convertToScriptException(t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds stack trace and other useful information to exceptiosn thrown
|
||||
* from a Painless script.
|
||||
* @param t The throwable to build an exception around.
|
||||
* @return The generated ScriptException.
|
||||
*/
|
||||
private ScriptException convertToScriptException(Throwable t) {
|
||||
// create a script stack: this is just the script portion
|
||||
List<String> scriptStack = new ArrayList<>();
|
||||
|
@ -169,7 +175,7 @@ final class ScriptImpl implements ExecutableScript, LeafSearchScript {
|
|||
}
|
||||
throw new ScriptException("runtime error", t, scriptStack, name, PainlessScriptEngineService.NAME);
|
||||
}
|
||||
|
||||
|
||||
/** returns true for methods that are part of the runtime */
|
||||
private static boolean shouldFilter(StackTraceElement element) {
|
||||
return element.getClassName().startsWith("org.elasticsearch.painless.") ||
|
||||
|
|
|
@ -122,9 +122,19 @@ public abstract class AExpression extends ANode {
|
|||
|
||||
if (cast == null) {
|
||||
if (constant == null || this instanceof EConstant) {
|
||||
// For the case where a cast is not required and a constant is not set
|
||||
// or the node is already an EConstant no changes are required to the tree.
|
||||
|
||||
return this;
|
||||
} else {
|
||||
final EConstant econstant = new EConstant(line, offset, location, constant);
|
||||
// For the case where a cast is not required but a
|
||||
// constant is set, an EConstant replaces this node
|
||||
// with the constant copied from this node. Note that
|
||||
// for constants output data does not need to be copied
|
||||
// from this node because the output data for the EConstant
|
||||
// will already be the same.
|
||||
|
||||
EConstant econstant = new EConstant(line, offset, location, constant);
|
||||
econstant.analyze(variables);
|
||||
|
||||
if (!expected.equals(econstant.actual)) {
|
||||
|
@ -135,7 +145,12 @@ public abstract class AExpression extends ANode {
|
|||
}
|
||||
} else {
|
||||
if (constant == null) {
|
||||
final ECast ecast = new ECast(line, offset, location, this, cast);
|
||||
// For the case where a cast is required and a constant is not set.
|
||||
// Modify the tree to add an ECast between this node and its parent.
|
||||
// The output data from this node is copied to the ECast for
|
||||
// further reads done by the parent.
|
||||
|
||||
ECast ecast = new ECast(line, offset, location, this, cast);
|
||||
ecast.statement = statement;
|
||||
ecast.actual = expected;
|
||||
ecast.isNull = isNull;
|
||||
|
@ -143,9 +158,17 @@ public abstract class AExpression extends ANode {
|
|||
return ecast;
|
||||
} else {
|
||||
if (expected.sort.constant) {
|
||||
// For the case where a cast is required, a constant is set,
|
||||
// and the constant can be immediately cast to the expected type.
|
||||
// An EConstant replaces this node with the constant cast appropriately
|
||||
// from the constant value defined by this node. Note that
|
||||
// for constants output data does not need to be copied
|
||||
// from this node because the output data for the EConstant
|
||||
// will already be the same.
|
||||
|
||||
constant = AnalyzerCaster.constCast(location, constant, cast);
|
||||
|
||||
final EConstant econstant = new EConstant(line, offset, location, constant);
|
||||
EConstant econstant = new EConstant(line, offset, location, constant);
|
||||
econstant.analyze(variables);
|
||||
|
||||
if (!expected.equals(econstant.actual)) {
|
||||
|
@ -154,19 +177,36 @@ public abstract class AExpression extends ANode {
|
|||
|
||||
return econstant;
|
||||
} else if (this instanceof EConstant) {
|
||||
final ECast ecast = new ECast(line, offset, location, this, cast);
|
||||
// For the case where a cast is required, a constant is set,
|
||||
// the constant cannot be immediately cast to the expected type,
|
||||
// and this node is already an EConstant. Modify the tree to add
|
||||
// an ECast between this node and its parent. Note that
|
||||
// for constants output data does not need to be copied
|
||||
// from this node because the output data for the EConstant
|
||||
// will already be the same.
|
||||
|
||||
ECast ecast = new ECast(line, offset, location, this, cast);
|
||||
ecast.actual = expected;
|
||||
|
||||
return ecast;
|
||||
} else {
|
||||
final EConstant econstant = new EConstant(line, offset, location, constant);
|
||||
// For the case where a cast is required, a constant is set,
|
||||
// the constant cannot be immediately cast to the expected type,
|
||||
// and this node is not an EConstant. Replace this node with
|
||||
// an Econstant node copying the constant from this node.
|
||||
// Modify the tree to add an ECast between the EConstant node
|
||||
// and its parent. Note that for constants output data does not
|
||||
// need to be copied from this node because the output data for
|
||||
// the EConstant will already be the same.
|
||||
|
||||
EConstant econstant = new EConstant(line, offset, location, constant);
|
||||
econstant.analyze(variables);
|
||||
|
||||
if (!actual.equals(econstant.actual)) {
|
||||
throw new IllegalStateException(error("Illegal tree structure."));
|
||||
}
|
||||
|
||||
final ECast ecast = new ECast(line, offset, location, econstant, cast);
|
||||
ECast ecast = new ECast(line, offset, location, econstant, cast);
|
||||
ecast.actual = expected;
|
||||
|
||||
return ecast;
|
||||
|
|
|
@ -113,8 +113,11 @@ public abstract class AStatement extends ANode {
|
|||
|
||||
/**
|
||||
* Checks for errors and collects data for the writing phase.
|
||||
* @return The new child node for the parent node calling this method.
|
||||
* Possibly returns a different {@link AStatement} node if a type is
|
||||
* def or if a different specialization is used. Otherwise, returns itself.
|
||||
*/
|
||||
abstract void analyze(Variables variables);
|
||||
abstract AStatement analyze(Variables variables);
|
||||
|
||||
/**
|
||||
* Writes ASM based on the data collected during the analysis phase.
|
||||
|
|
|
@ -471,6 +471,7 @@ public final class EBinary extends AExpression {
|
|||
@Override
|
||||
void write(MethodWriter writer) {
|
||||
writer.writeDebugInfo(offset);
|
||||
|
||||
if (actual.sort == Sort.STRING && operation == Operation.ADD) {
|
||||
if (!cat) {
|
||||
writer.writeNewStrings();
|
||||
|
|
|
@ -246,75 +246,115 @@ public final class EChain extends AExpression {
|
|||
actual = last.after;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles writing byte code for variable/method chains for all given possibilities
|
||||
* including String concatenation, compound assignment, regular assignment, and simple
|
||||
* reads. Includes proper duplication for chained assignments and assignments that are
|
||||
* also read from.
|
||||
*
|
||||
* Example given 'x[0] += 5;' where x is an array of shorts and x[0] is 1.
|
||||
* Note this example has two links -- x (LVariable) and [0] (LBrace).
|
||||
* The following steps occur:
|
||||
* 1. call link{x}.write(...) -- no op [...]
|
||||
* 2. call link{x}.load(...) -- loads the address of the x array onto the stack [..., address(x)]
|
||||
* 3. call writer.dup(...) -- dup's the address of the x array onto the stack for later use with store [..., address(x), address(x)]
|
||||
* 4. call link{[0]}.write(...) -- load the array index value of the constant int 0 onto the stack [..., address(x), address(x), int(0)]
|
||||
* 5. call link{[0]}.load(...) -- load the short value from x[0] onto the stack [..., address(x), short(1)]
|
||||
* 6. call writer.writeCast(there) -- casts the short on the stack to an int so it can be added with the rhs [..., address(x), int(1)]
|
||||
* 7. call expression.write(...) -- puts the expression's value of the constant int 5 onto the stack [..., address(x), int(1), int(5)]
|
||||
* 8. call writer.writeBinaryInstruction(operation) -- writes the int addition instruction [..., address(x), int(6)]
|
||||
* 9. call writer.writeCast(back) -- convert the value on the stack back into a short [..., address(x), short(6)]
|
||||
* 10. call link{[0]}.store(...) -- store the value on the stack into the 0th index of the array x [...]
|
||||
*/
|
||||
@Override
|
||||
void write(MethodWriter writer) {
|
||||
// For the case where the chain represents a String concatenation
|
||||
// we must first write debug information, and then depending on the
|
||||
// Java version write a StringBuilder or track types going onto the
|
||||
// stack. This must be done before the links in the chain are read
|
||||
// because we need the StringBuilder to be placed on the stack
|
||||
// ahead of any potential concatenation arguments.
|
||||
if (cat) {
|
||||
writer.writeDebugInfo(offset);
|
||||
}
|
||||
|
||||
if (cat) {
|
||||
writer.writeNewStrings();
|
||||
}
|
||||
|
||||
ALink last = links.get(links.size() - 1);
|
||||
|
||||
// Go through all the links in the chain first calling write
|
||||
// and then load, except for the final link which may be a store.
|
||||
// See individual links for more information on what each of the
|
||||
// write, load, and store methods do.
|
||||
for (ALink link : links) {
|
||||
link.write(writer);
|
||||
link.write(writer); // call the write method on the link to prepare for a load/store operation
|
||||
|
||||
if (link == last && link.store) {
|
||||
if (cat) {
|
||||
writer.writeDup(link.size, 1);
|
||||
link.load(writer);
|
||||
writer.writeAppendStrings(link.after);
|
||||
// Handle the case where we are doing a compound assignment
|
||||
// representing a String concatenation.
|
||||
|
||||
expression.write(writer);
|
||||
writer.writeDup(link.size, 1); // dup the StringBuilder
|
||||
link.load(writer); // read the current link's value
|
||||
writer.writeAppendStrings(link.after); // append the link's value using the StringBuilder
|
||||
|
||||
expression.write(writer); // write the bytecode for the rhs expression
|
||||
|
||||
if (!(expression instanceof EBinary) ||
|
||||
((EBinary)expression).operation != Operation.ADD || expression.actual.sort != Sort.STRING) {
|
||||
writer.writeAppendStrings(expression.actual);
|
||||
writer.writeAppendStrings(expression.actual); // append the expression's value unless its also a concatenation
|
||||
}
|
||||
|
||||
writer.writeToStrings();
|
||||
writer.writeCast(back);
|
||||
writer.writeToStrings(); // put the value of the StringBuilder on the stack
|
||||
writer.writeCast(back); // if necessary, cast the String to the lhs actual type
|
||||
|
||||
if (link.load) {
|
||||
writer.writeDup(link.after.sort.size, link.size);
|
||||
writer.writeDup(link.after.sort.size, link.size); // if this link is also read from dup the value onto the stack
|
||||
}
|
||||
|
||||
link.store(writer);
|
||||
link.store(writer); // store the link's value from the stack in its respective variable/field/array
|
||||
} else if (operation != null) {
|
||||
writer.writeDup(link.size, 0);
|
||||
link.load(writer);
|
||||
// Handle the case where we are doing a compound assignment that
|
||||
// does not represent a String concatenation.
|
||||
|
||||
writer.writeDup(link.size, 0); // if necessary, dup the previous link's value to be both loaded from and stored to
|
||||
link.load(writer); // load the current link's value
|
||||
|
||||
if (link.load && post) {
|
||||
writer.writeDup(link.after.sort.size, link.size);
|
||||
writer.writeDup(link.after.sort.size, link.size); // dup the value if the link is also
|
||||
// read from and is a post increment
|
||||
}
|
||||
|
||||
writer.writeCast(there);
|
||||
expression.write(writer);
|
||||
writer.writeBinaryInstruction(location, promote, operation);
|
||||
writer.writeCast(there); // if necessary cast the current link's value
|
||||
// to the promotion type between the lhs and rhs types
|
||||
expression.write(writer); // write the bytecode for the rhs expression
|
||||
writer.writeBinaryInstruction(location, promote, operation); // write the operation instruction for compound assignment
|
||||
|
||||
writer.writeCast(back);
|
||||
writer.writeCast(back); // if necessary cast the promotion type value back to the link's type
|
||||
|
||||
if (link.load && !post) {
|
||||
writer.writeDup(link.after.sort.size, link.size);
|
||||
writer.writeDup(link.after.sort.size, link.size); // dup the value if the link is also
|
||||
// read from and is not a post increment
|
||||
}
|
||||
|
||||
link.store(writer);
|
||||
link.store(writer); // store the link's value from the stack in its respective variable/field/array
|
||||
} else {
|
||||
expression.write(writer);
|
||||
// Handle the case for a simple write.
|
||||
|
||||
expression.write(writer); // write the bytecode for the rhs expression
|
||||
|
||||
if (link.load) {
|
||||
writer.writeDup(link.after.sort.size, link.size);
|
||||
writer.writeDup(link.after.sort.size, link.size); // dup the value if the link is also read from
|
||||
}
|
||||
|
||||
link.store(writer);
|
||||
link.store(writer); // store the link's value from the stack in its respective variable/field/array
|
||||
}
|
||||
} else {
|
||||
link.load(writer);
|
||||
// Handle the case for a simple read.
|
||||
|
||||
link.load(writer); // read the link's value onto the stack
|
||||
}
|
||||
}
|
||||
|
||||
writer.writeBranch(tru, fals);
|
||||
writer.writeBranch(tru, fals); // if this is a branch node, write the bytecode to make an appropiate jump
|
||||
}
|
||||
}
|
||||
|
|
|
@ -400,6 +400,7 @@ public final class EComp extends AExpression {
|
|||
@Override
|
||||
void write(MethodWriter writer) {
|
||||
writer.writeDebugInfo(offset);
|
||||
|
||||
boolean branch = tru != null || fals != null;
|
||||
org.objectweb.asm.Type rtype = right.actual.type;
|
||||
Sort rsort = right.actual.sort;
|
||||
|
|
|
@ -79,6 +79,7 @@ public final class EConditional extends AExpression {
|
|||
@Override
|
||||
void write(MethodWriter writer) {
|
||||
writer.writeDebugInfo(offset);
|
||||
|
||||
Label localfals = new Label();
|
||||
Label end = new Label();
|
||||
|
||||
|
|
|
@ -166,6 +166,7 @@ public final class EUnary extends AExpression {
|
|||
@Override
|
||||
void write(MethodWriter writer) {
|
||||
writer.writeDebugInfo(offset);
|
||||
|
||||
if (operation == Operation.NOT) {
|
||||
if (tru == null && fals == null) {
|
||||
Label localfals = new Label();
|
||||
|
|
|
@ -83,5 +83,4 @@ public final class LBrace extends ALink {
|
|||
writer.writeDebugInfo(offset);
|
||||
writer.arrayStore(after.type);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -92,6 +92,7 @@ public final class LCall extends ALink {
|
|||
@Override
|
||||
void load(MethodWriter writer) {
|
||||
writer.writeDebugInfo(offset);
|
||||
|
||||
for (AExpression argument : arguments) {
|
||||
argument.write(writer);
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ final class LDefArray extends ALink implements IDefLink {
|
|||
@Override
|
||||
void load(MethodWriter writer) {
|
||||
writer.writeDebugInfo(offset);
|
||||
|
||||
String desc = Type.getMethodDescriptor(after.type, Definition.DEF_TYPE.type, index.actual.type);
|
||||
writer.invokeDynamic("arrayLoad", desc, DEF_BOOTSTRAP_HANDLE, (Object)DefBootstrap.ARRAY_LOAD);
|
||||
}
|
||||
|
@ -66,6 +67,7 @@ final class LDefArray extends ALink implements IDefLink {
|
|||
@Override
|
||||
void store(MethodWriter writer) {
|
||||
writer.writeDebugInfo(offset);
|
||||
|
||||
String desc = Type.getMethodDescriptor(Definition.VOID_TYPE.type, Definition.DEF_TYPE.type, index.actual.type, after.type);
|
||||
writer.invokeDynamic("arrayStore", desc, DEF_BOOTSTRAP_HANDLE, (Object)DefBootstrap.ARRAY_STORE);
|
||||
}
|
||||
|
|
|
@ -68,6 +68,7 @@ final class LDefCall extends ALink implements IDefLink {
|
|||
@Override
|
||||
void load(MethodWriter writer) {
|
||||
writer.writeDebugInfo(offset);
|
||||
|
||||
StringBuilder signature = new StringBuilder();
|
||||
|
||||
signature.append('(');
|
||||
|
|
|
@ -56,6 +56,7 @@ final class LDefField extends ALink implements IDefLink {
|
|||
@Override
|
||||
void load(MethodWriter writer) {
|
||||
writer.writeDebugInfo(offset);
|
||||
|
||||
String desc = Type.getMethodDescriptor(after.type, Definition.DEF_TYPE.type);
|
||||
writer.invokeDynamic(value, desc, DEF_BOOTSTRAP_HANDLE, (Object)DefBootstrap.LOAD);
|
||||
}
|
||||
|
@ -63,6 +64,7 @@ final class LDefField extends ALink implements IDefLink {
|
|||
@Override
|
||||
void store(MethodWriter writer) {
|
||||
writer.writeDebugInfo(offset);
|
||||
|
||||
String desc = Type.getMethodDescriptor(Definition.VOID_TYPE.type, Definition.DEF_TYPE.type, after.type);
|
||||
writer.invokeDynamic(value, desc, DEF_BOOTSTRAP_HANDLE, (Object)DefBootstrap.STORE);
|
||||
}
|
||||
|
|
|
@ -106,6 +106,7 @@ public final class LField extends ALink {
|
|||
@Override
|
||||
void load(MethodWriter writer) {
|
||||
writer.writeDebugInfo(offset);
|
||||
|
||||
if (java.lang.reflect.Modifier.isStatic(field.modifiers)) {
|
||||
writer.getStatic(field.owner.type, field.javaName, field.type.type);
|
||||
} else {
|
||||
|
@ -116,6 +117,7 @@ public final class LField extends ALink {
|
|||
@Override
|
||||
void store(MethodWriter writer) {
|
||||
writer.writeDebugInfo(offset);
|
||||
|
||||
if (java.lang.reflect.Modifier.isStatic(field.modifiers)) {
|
||||
writer.putStatic(field.owner.type, field.javaName, field.type.type);
|
||||
} else {
|
||||
|
|
|
@ -80,6 +80,7 @@ final class LListShortcut extends ALink {
|
|||
@Override
|
||||
void load(MethodWriter writer) {
|
||||
writer.writeDebugInfo(offset);
|
||||
|
||||
if (java.lang.reflect.Modifier.isInterface(getter.owner.clazz.getModifiers())) {
|
||||
writer.invokeInterface(getter.owner.type, getter.method);
|
||||
} else {
|
||||
|
@ -94,6 +95,7 @@ final class LListShortcut extends ALink {
|
|||
@Override
|
||||
void store(MethodWriter writer) {
|
||||
writer.writeDebugInfo(offset);
|
||||
|
||||
if (java.lang.reflect.Modifier.isInterface(setter.owner.clazz.getModifiers())) {
|
||||
writer.invokeInterface(setter.owner.type, setter.method);
|
||||
} else {
|
||||
|
|
|
@ -79,6 +79,7 @@ final class LMapShortcut extends ALink {
|
|||
@Override
|
||||
void load(MethodWriter writer) {
|
||||
writer.writeDebugInfo(offset);
|
||||
|
||||
if (java.lang.reflect.Modifier.isInterface(getter.owner.clazz.getModifiers())) {
|
||||
writer.invokeInterface(getter.owner.type, getter.method);
|
||||
} else {
|
||||
|
@ -93,6 +94,7 @@ final class LMapShortcut extends ALink {
|
|||
@Override
|
||||
void store(MethodWriter writer) {
|
||||
writer.writeDebugInfo(offset);
|
||||
|
||||
if (java.lang.reflect.Modifier.isInterface(setter.owner.clazz.getModifiers())) {
|
||||
writer.invokeInterface(setter.owner.type, setter.method);
|
||||
} else {
|
||||
|
|
|
@ -80,6 +80,7 @@ public final class LNewArray extends ALink {
|
|||
@Override
|
||||
void load(MethodWriter writer) {
|
||||
writer.writeDebugInfo(offset);
|
||||
|
||||
for (AExpression argument : arguments) {
|
||||
argument.write(writer);
|
||||
}
|
||||
|
|
|
@ -85,6 +85,7 @@ final class LShortcut extends ALink {
|
|||
@Override
|
||||
void load(MethodWriter writer) {
|
||||
writer.writeDebugInfo(offset);
|
||||
|
||||
if (java.lang.reflect.Modifier.isInterface(getter.owner.clazz.getModifiers())) {
|
||||
writer.invokeInterface(getter.owner.type, getter.method);
|
||||
} else {
|
||||
|
@ -99,6 +100,7 @@ final class LShortcut extends ALink {
|
|||
@Override
|
||||
void store(MethodWriter writer) {
|
||||
writer.writeDebugInfo(offset);
|
||||
|
||||
if (java.lang.reflect.Modifier.isInterface(setter.owner.clazz.getModifiers())) {
|
||||
writer.invokeInterface(setter.owner.type, setter.method);
|
||||
} else {
|
||||
|
|
|
@ -22,7 +22,6 @@ package org.elasticsearch.painless.node;
|
|||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -35,27 +34,27 @@ public final class SBlock extends AStatement {
|
|||
public SBlock(int line, int offset, String location, List<AStatement> statements) {
|
||||
super(line, offset, location);
|
||||
|
||||
this.statements = Collections.unmodifiableList(statements);
|
||||
this.statements = statements;
|
||||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
AStatement analyze(Variables variables) {
|
||||
if (statements == null || statements.isEmpty()) {
|
||||
throw new IllegalArgumentException(error("A block must contain at least one statement."));
|
||||
}
|
||||
|
||||
final AStatement last = statements.get(statements.size() - 1);
|
||||
|
||||
for (AStatement statement : statements) {
|
||||
for (int index = 0; index < statements.size(); ++index) {
|
||||
if (allEscape) {
|
||||
throw new IllegalArgumentException(error("Unreachable statement."));
|
||||
}
|
||||
|
||||
statement.inLoop = inLoop;
|
||||
statement.lastSource = lastSource && statement == last;
|
||||
statement.lastLoop = (beginLoop || lastLoop) && statement == last;
|
||||
AStatement statement = statements.get(index);
|
||||
|
||||
statement.analyze(variables);
|
||||
statement.inLoop = inLoop;
|
||||
statement.lastSource = lastSource && index == statements.size() - 1;
|
||||
statement.lastLoop = (beginLoop || lastLoop) && index == statements.size() - 1;
|
||||
|
||||
statements.set(index, statement.analyze(variables));
|
||||
|
||||
methodEscape = statement.methodEscape;
|
||||
loopEscape = statement.loopEscape;
|
||||
|
@ -64,6 +63,8 @@ public final class SBlock extends AStatement {
|
|||
anyBreak |= statement.anyBreak;
|
||||
statementCount += statement.statementCount;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -32,7 +32,7 @@ public final class SBreak extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
AStatement analyze(Variables variables) {
|
||||
if (!inLoop) {
|
||||
throw new IllegalArgumentException(error("Break statement outside of a loop."));
|
||||
}
|
||||
|
@ -41,6 +41,8 @@ public final class SBreak extends AStatement {
|
|||
allEscape = true;
|
||||
anyBreak = true;
|
||||
statementCount = 1;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -34,7 +34,7 @@ public final class SCatch extends AStatement {
|
|||
|
||||
final String type;
|
||||
final String name;
|
||||
final SBlock block;
|
||||
AStatement block;
|
||||
|
||||
Variable variable;
|
||||
|
||||
|
@ -51,7 +51,7 @@ public final class SCatch extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
AStatement analyze(Variables variables) {
|
||||
final Type type;
|
||||
|
||||
try {
|
||||
|
@ -71,7 +71,7 @@ public final class SCatch extends AStatement {
|
|||
block.inLoop = inLoop;
|
||||
block.lastLoop = lastLoop;
|
||||
|
||||
block.analyze(variables);
|
||||
block = block.analyze(variables);
|
||||
|
||||
methodEscape = block.methodEscape;
|
||||
loopEscape = block.loopEscape;
|
||||
|
@ -80,11 +80,14 @@ public final class SCatch extends AStatement {
|
|||
anyBreak = block.anyBreak;
|
||||
statementCount = block.statementCount;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(MethodWriter writer) {
|
||||
writer.writeStatementOffset(offset);
|
||||
|
||||
Label jump = new Label();
|
||||
|
||||
writer.mark(jump);
|
||||
|
|
|
@ -32,7 +32,7 @@ public final class SContinue extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
AStatement analyze(Variables variables) {
|
||||
if (!inLoop) {
|
||||
throw new IllegalArgumentException(error("Continue statement outside of a loop."));
|
||||
}
|
||||
|
@ -44,6 +44,8 @@ public final class SContinue extends AStatement {
|
|||
allEscape = true;
|
||||
anyContinue = true;
|
||||
statementCount = 1;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -22,7 +22,6 @@ package org.elasticsearch.painless.node;
|
|||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -35,21 +34,25 @@ public final class SDeclBlock extends AStatement {
|
|||
public SDeclBlock(int line, int offset, String location, List<SDeclaration> declarations) {
|
||||
super(line, offset, location);
|
||||
|
||||
this.declarations = Collections.unmodifiableList(declarations);
|
||||
this.declarations = declarations;
|
||||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
for (SDeclaration declaration : declarations) {
|
||||
declaration.analyze(variables);
|
||||
AStatement analyze(Variables variables) {
|
||||
for (int index = 0; index < declarations.size(); ++index) {
|
||||
AStatement declaration = declarations.get(index);
|
||||
|
||||
declarations.set(index, (SDeclaration)declaration.analyze(variables));
|
||||
}
|
||||
|
||||
statementCount = declarations.size();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(MethodWriter writer) {
|
||||
for (SDeclaration declaration : declarations) {
|
||||
for (AStatement declaration : declarations) {
|
||||
declaration.write(writer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ public final class SDeclaration extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
AStatement analyze(Variables variables) {
|
||||
final Type type;
|
||||
|
||||
try {
|
||||
|
@ -62,11 +62,14 @@ public final class SDeclaration extends AStatement {
|
|||
}
|
||||
|
||||
variable = variables.addVariable(location, type, name, false, false);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(MethodWriter writer) {
|
||||
writer.writeStatementOffset(offset);
|
||||
|
||||
if (expression == null) {
|
||||
switch (variable.type.sort) {
|
||||
case VOID: throw new IllegalStateException(error("Illegal tree structure."));
|
||||
|
|
|
@ -30,7 +30,7 @@ import org.elasticsearch.painless.MethodWriter;
|
|||
public final class SDo extends AStatement {
|
||||
|
||||
final int maxLoopCounter;
|
||||
final SBlock block;
|
||||
AStatement block;
|
||||
AExpression condition;
|
||||
|
||||
public SDo(int line, int offset, String location, int maxLoopCounter, SBlock block, AExpression condition) {
|
||||
|
@ -42,7 +42,7 @@ public final class SDo extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
AStatement analyze(Variables variables) {
|
||||
variables.incrementScope();
|
||||
|
||||
if (block == null) {
|
||||
|
@ -52,7 +52,7 @@ public final class SDo extends AStatement {
|
|||
block.beginLoop = true;
|
||||
block.inLoop = true;
|
||||
|
||||
block.analyze(variables);
|
||||
block = block.analyze(variables);
|
||||
|
||||
if (block.loopEscape && !block.anyContinue) {
|
||||
throw new IllegalArgumentException(error("Extraneous do while loop."));
|
||||
|
@ -82,11 +82,14 @@ public final class SDo extends AStatement {
|
|||
}
|
||||
|
||||
variables.decrementScope();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(MethodWriter writer) {
|
||||
writer.writeStatementOffset(offset);
|
||||
|
||||
Label start = new Label();
|
||||
Label begin = new Label();
|
||||
Label end = new Label();
|
||||
|
|
|
@ -38,7 +38,7 @@ public final class SExpression extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
AStatement analyze(Variables variables) {
|
||||
expression.read = lastSource;
|
||||
expression.analyze(variables);
|
||||
|
||||
|
@ -56,6 +56,8 @@ public final class SExpression extends AStatement {
|
|||
loopEscape = rtn;
|
||||
allEscape = rtn;
|
||||
statementCount = 1;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -33,7 +33,7 @@ public final class SFor extends AStatement {
|
|||
ANode initializer;
|
||||
AExpression condition;
|
||||
AExpression afterthought;
|
||||
final SBlock block;
|
||||
AStatement block;
|
||||
|
||||
public SFor(int line, int offset, String location, int maxLoopCounter,
|
||||
ANode initializer, AExpression condition, AExpression afterthought, SBlock block) {
|
||||
|
@ -47,14 +47,14 @@ public final class SFor extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
AStatement analyze(Variables variables) {
|
||||
variables.incrementScope();
|
||||
|
||||
boolean continuous = false;
|
||||
|
||||
if (initializer != null) {
|
||||
if (initializer instanceof SDeclBlock) {
|
||||
((SDeclBlock)initializer).analyze(variables);
|
||||
if (initializer instanceof AStatement) {
|
||||
initializer = ((AStatement)initializer).analyze(variables);
|
||||
} else if (initializer instanceof AExpression) {
|
||||
AExpression initializer = (AExpression)this.initializer;
|
||||
|
||||
|
@ -102,7 +102,7 @@ public final class SFor extends AStatement {
|
|||
block.beginLoop = true;
|
||||
block.inLoop = true;
|
||||
|
||||
block.analyze(variables);
|
||||
block = block.analyze(variables);
|
||||
|
||||
if (block.loopEscape && !block.anyContinue) {
|
||||
throw new IllegalArgumentException(error("Extraneous for loop."));
|
||||
|
@ -123,11 +123,14 @@ public final class SFor extends AStatement {
|
|||
}
|
||||
|
||||
variables.decrementScope();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(MethodWriter writer) {
|
||||
writer.writeStatementOffset(offset);
|
||||
|
||||
Label start = new Label();
|
||||
Label begin = afterthought == null ? start : new Label();
|
||||
Label end = new Label();
|
||||
|
|
|
@ -30,7 +30,7 @@ import org.elasticsearch.painless.MethodWriter;
|
|||
public final class SIf extends AStatement {
|
||||
|
||||
AExpression condition;
|
||||
final SBlock ifblock;
|
||||
AStatement ifblock;
|
||||
|
||||
public SIf(int line, int offset, String location, AExpression condition, SBlock ifblock) {
|
||||
super(line, offset, location);
|
||||
|
@ -40,7 +40,7 @@ public final class SIf extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
AStatement analyze(Variables variables) {
|
||||
condition.expected = Definition.BOOLEAN_TYPE;
|
||||
condition.analyze(variables);
|
||||
condition = condition.cast(variables);
|
||||
|
@ -58,17 +58,20 @@ public final class SIf extends AStatement {
|
|||
ifblock.lastLoop = lastLoop;
|
||||
|
||||
variables.incrementScope();
|
||||
ifblock.analyze(variables);
|
||||
ifblock = ifblock.analyze(variables);
|
||||
variables.decrementScope();
|
||||
|
||||
anyContinue = ifblock.anyContinue;
|
||||
anyBreak = ifblock.anyBreak;
|
||||
statementCount = ifblock.statementCount;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(MethodWriter writer) {
|
||||
writer.writeStatementOffset(offset);
|
||||
|
||||
Label fals = new Label();
|
||||
|
||||
condition.fals = fals;
|
||||
|
|
|
@ -30,8 +30,8 @@ import org.elasticsearch.painless.MethodWriter;
|
|||
public final class SIfElse extends AStatement {
|
||||
|
||||
AExpression condition;
|
||||
final SBlock ifblock;
|
||||
final SBlock elseblock;
|
||||
AStatement ifblock;
|
||||
AStatement elseblock;
|
||||
|
||||
public SIfElse(int line, int offset, String location, AExpression condition, SBlock ifblock, SBlock elseblock) {
|
||||
super(line, offset, location);
|
||||
|
@ -42,7 +42,7 @@ public final class SIfElse extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
AStatement analyze(Variables variables) {
|
||||
condition.expected = Definition.BOOLEAN_TYPE;
|
||||
condition.analyze(variables);
|
||||
condition = condition.cast(variables);
|
||||
|
@ -60,7 +60,7 @@ public final class SIfElse extends AStatement {
|
|||
ifblock.lastLoop = lastLoop;
|
||||
|
||||
variables.incrementScope();
|
||||
ifblock.analyze(variables);
|
||||
ifblock = ifblock.analyze(variables);
|
||||
variables.decrementScope();
|
||||
|
||||
anyContinue = ifblock.anyContinue;
|
||||
|
@ -76,7 +76,7 @@ public final class SIfElse extends AStatement {
|
|||
elseblock.lastLoop = lastLoop;
|
||||
|
||||
variables.incrementScope();
|
||||
elseblock.analyze(variables);
|
||||
elseblock = elseblock.analyze(variables);
|
||||
variables.decrementScope();
|
||||
|
||||
methodEscape = ifblock.methodEscape && elseblock.methodEscape;
|
||||
|
@ -85,11 +85,14 @@ public final class SIfElse extends AStatement {
|
|||
anyContinue |= elseblock.anyContinue;
|
||||
anyBreak |= elseblock.anyBreak;
|
||||
statementCount = Math.max(ifblock.statementCount, elseblock.statementCount);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(MethodWriter writer) {
|
||||
writer.writeStatementOffset(offset);
|
||||
|
||||
Label end = new Label();
|
||||
Label fals = elseblock != null ? new Label() : end;
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ public final class SReturn extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
AStatement analyze(Variables variables) {
|
||||
expression.expected = Definition.OBJECT_TYPE;
|
||||
expression.internal = true;
|
||||
expression.analyze(variables);
|
||||
|
@ -48,6 +48,8 @@ public final class SReturn extends AStatement {
|
|||
allEscape = true;
|
||||
|
||||
statementCount = 1;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -23,7 +23,6 @@ import org.elasticsearch.painless.Variables;
|
|||
import org.objectweb.asm.Opcodes;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -36,32 +35,34 @@ public final class SSource extends AStatement {
|
|||
public SSource(int line, int offset, String location, List<AStatement> statements) {
|
||||
super(line, offset, location);
|
||||
|
||||
this.statements = Collections.unmodifiableList(statements);
|
||||
this.statements = statements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void analyze(Variables variables) {
|
||||
public AStatement analyze(Variables variables) {
|
||||
if (statements == null || statements.isEmpty()) {
|
||||
throw new IllegalArgumentException(error("Cannot generate an empty script."));
|
||||
}
|
||||
|
||||
variables.incrementScope();
|
||||
|
||||
final AStatement last = statements.get(statements.size() - 1);
|
||||
for (int index = 0; index < statements.size(); ++index) {
|
||||
AStatement statement = statements.get(index);
|
||||
|
||||
for (AStatement statement : statements) {
|
||||
if (allEscape) {
|
||||
throw new IllegalArgumentException(error("Unreachable statement."));
|
||||
}
|
||||
|
||||
statement.lastSource = statement == last;
|
||||
statement.analyze(variables);
|
||||
statement.lastSource = index == statements.size() - 1;
|
||||
statements.set(index, statement.analyze(variables));
|
||||
|
||||
methodEscape = statement.methodEscape;
|
||||
allEscape = statement.allEscape;
|
||||
}
|
||||
|
||||
variables.decrementScope();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -37,7 +37,7 @@ public final class SThrow extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
AStatement analyze(Variables variables) {
|
||||
expression.expected = Definition.EXCEPTION_TYPE;
|
||||
expression.analyze(variables);
|
||||
expression = expression.cast(variables);
|
||||
|
@ -46,6 +46,8 @@ public final class SThrow extends AStatement {
|
|||
loopEscape = true;
|
||||
allEscape = true;
|
||||
statementCount = 1;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -23,7 +23,6 @@ import org.elasticsearch.painless.Variables;
|
|||
import org.objectweb.asm.Label;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -31,18 +30,18 @@ import java.util.List;
|
|||
*/
|
||||
public final class STry extends AStatement {
|
||||
|
||||
final SBlock block;
|
||||
AStatement block;
|
||||
final List<SCatch> catches;
|
||||
|
||||
public STry(int line, int offset, String location, SBlock block, List<SCatch> traps) {
|
||||
public STry(int line, int offset, String location, SBlock block, List<SCatch> catches) {
|
||||
super(line, offset, location);
|
||||
|
||||
this.block = block;
|
||||
this.catches = Collections.unmodifiableList(traps);
|
||||
this.catches = catches;
|
||||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
AStatement analyze(Variables variables) {
|
||||
if (block == null) {
|
||||
throw new IllegalArgumentException(error("Extraneous try statement."));
|
||||
}
|
||||
|
@ -52,7 +51,7 @@ public final class STry extends AStatement {
|
|||
block.lastLoop = lastLoop;
|
||||
|
||||
variables.incrementScope();
|
||||
block.analyze(variables);
|
||||
block = block.analyze(variables);
|
||||
variables.decrementScope();
|
||||
|
||||
methodEscape = block.methodEscape;
|
||||
|
@ -63,13 +62,15 @@ public final class STry extends AStatement {
|
|||
|
||||
int statementCount = 0;
|
||||
|
||||
for (SCatch catc : catches) {
|
||||
for (int index = 0; index < catches.size(); ++index) {
|
||||
SCatch catc = catches.get(index);
|
||||
|
||||
catc.lastSource = lastSource;
|
||||
catc.inLoop = inLoop;
|
||||
catc.lastLoop = lastLoop;
|
||||
|
||||
variables.incrementScope();
|
||||
catc.analyze(variables);
|
||||
catches.set(index, (SCatch)catc.analyze(variables));
|
||||
variables.decrementScope();
|
||||
|
||||
methodEscape &= catc.methodEscape;
|
||||
|
@ -82,11 +83,14 @@ public final class STry extends AStatement {
|
|||
}
|
||||
|
||||
this.statementCount = block.statementCount + statementCount;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(MethodWriter writer) {
|
||||
writer.writeStatementOffset(offset);
|
||||
|
||||
Label begin = new Label();
|
||||
Label end = new Label();
|
||||
Label exception = new Label();
|
||||
|
|
|
@ -31,7 +31,7 @@ public final class SWhile extends AStatement {
|
|||
|
||||
final int maxLoopCounter;
|
||||
AExpression condition;
|
||||
final SBlock block;
|
||||
AStatement block;
|
||||
|
||||
public SWhile(int line, int offset, String location, int maxLoopCounter, AExpression condition, SBlock block) {
|
||||
super(line, offset, location);
|
||||
|
@ -42,7 +42,7 @@ public final class SWhile extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
AStatement analyze(Variables variables) {
|
||||
variables.incrementScope();
|
||||
|
||||
condition.expected = Definition.BOOLEAN_TYPE;
|
||||
|
@ -67,7 +67,7 @@ public final class SWhile extends AStatement {
|
|||
block.beginLoop = true;
|
||||
block.inLoop = true;
|
||||
|
||||
block.analyze(variables);
|
||||
block = block.analyze(variables);
|
||||
|
||||
if (block.loopEscape && !block.anyContinue) {
|
||||
throw new IllegalArgumentException(error("Extraneous while loop."));
|
||||
|
@ -88,11 +88,14 @@ public final class SWhile extends AStatement {
|
|||
}
|
||||
|
||||
variables.decrementScope();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(MethodWriter writer) {
|
||||
writer.writeStatementOffset(offset);
|
||||
|
||||
Label begin = new Label();
|
||||
Label end = new Label();
|
||||
|
||||
|
|
Loading…
Reference in New Issue