Added initial infrastructure to allow for each to be made.

This commit is contained in:
Jack Conradson 2016-05-27 17:41:31 -07:00
parent 2d8030f09f
commit f5be0982d9
36 changed files with 250 additions and 100 deletions

View File

@ -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: ';';

View File

@ -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);
}
}

View File

@ -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.") ||

View File

@ -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;

View File

@ -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.

View File

@ -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();

View File

@ -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
}
}

View File

@ -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;

View File

@ -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();

View File

@ -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();

View File

@ -83,5 +83,4 @@ public final class LBrace extends ALink {
writer.writeDebugInfo(offset);
writer.arrayStore(after.type);
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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('(');

View File

@ -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);
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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);
}

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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);
}
}

View File

@ -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."));

View File

@ -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();

View File

@ -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

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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();