Reverted S-node design change.
This commit is contained in:
parent
8db9a971e5
commit
6dace47c1f
|
@ -74,7 +74,7 @@ decltype
|
|||
;
|
||||
|
||||
funcref
|
||||
: ( TYPE | ID ) REF ID
|
||||
: TYPE REF ID
|
||||
;
|
||||
|
||||
declvar
|
||||
|
|
|
@ -114,33 +114,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 AStatement analyze(Variables variables);
|
||||
abstract void analyze(Variables variables);
|
||||
|
||||
/**
|
||||
* Writes ASM based on the data collected during the analysis phase.
|
||||
*/
|
||||
abstract void write(MethodWriter writer);
|
||||
|
||||
/**
|
||||
* Used to copy statement data from one to another during analysis in the case of replacement.
|
||||
*/
|
||||
final AStatement copy(AStatement statement) {
|
||||
lastSource = statement.lastSource;
|
||||
beginLoop = statement.beginLoop;
|
||||
inLoop = statement.inLoop;
|
||||
lastLoop = statement.lastLoop;
|
||||
methodEscape = statement.methodEscape;
|
||||
loopEscape = statement.loopEscape;
|
||||
allEscape = statement.allEscape;
|
||||
anyContinue = statement.anyContinue;
|
||||
anyBreak = statement.anyBreak;
|
||||
loopCounterSlot = statement.loopCounterSlot;
|
||||
statementCount = statement.statementCount;
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,135 +0,0 @@
|
|||
/*
|
||||
* 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.AnalyzerCaster;
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Definition.Cast;
|
||||
import org.elasticsearch.painless.Definition.Type;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Variables.Variable;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
/**
|
||||
* Represents a for-each loop shortcut for arrays. (Internal only.)
|
||||
*/
|
||||
class SArrayEach extends AStatement {
|
||||
final int maxLoopCounter;
|
||||
final String type;
|
||||
final String name;
|
||||
AExpression expression;
|
||||
AStatement block;
|
||||
|
||||
Variable variable = null;
|
||||
Variable array = null;
|
||||
Variable index = null;
|
||||
Type indexed = null;
|
||||
Cast cast = null;
|
||||
|
||||
public SArrayEach(Location location, int maxLoopCounter, String type, String name, AExpression expression, SBlock block) {
|
||||
super(location);
|
||||
|
||||
this.maxLoopCounter = maxLoopCounter;
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.expression = expression;
|
||||
this.block = block;
|
||||
}
|
||||
|
||||
@Override
|
||||
AStatement analyze(Variables variables) {
|
||||
// Note that we do not need to analyze the expression as this must already be done
|
||||
// in the parent to determine that the for each target type is an array.
|
||||
|
||||
final Type type;
|
||||
|
||||
try {
|
||||
type = Definition.getType(this.type);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
||||
}
|
||||
|
||||
variables.incrementScope();
|
||||
|
||||
variable = variables.addVariable(location, type, name, true, false);
|
||||
array = variables.addVariable(location, expression.actual, "#array" + location.getOffset(), true, false);
|
||||
index = variables.addVariable(location, Definition.INT_TYPE, "#index" + location.getOffset(), true, false);
|
||||
indexed = Definition.getType(expression.actual.struct, expression.actual.dimensions - 1);
|
||||
cast = AnalyzerCaster.getLegalCast(location, indexed, type, true, true);
|
||||
|
||||
if (block == null) {
|
||||
throw location.createError(new IllegalArgumentException("Extraneous for each loop."));
|
||||
}
|
||||
|
||||
block.beginLoop = true;
|
||||
block.inLoop = true;
|
||||
block = block.analyze(variables);
|
||||
block.statementCount = Math.max(1, block.statementCount);
|
||||
|
||||
if (block.loopEscape && !block.anyContinue) {
|
||||
throw createError(new IllegalArgumentException("Extraneous for loop."));
|
||||
}
|
||||
|
||||
statementCount = 1;
|
||||
|
||||
if (maxLoopCounter > 0) {
|
||||
loopCounterSlot = variables.getVariable(location, "#loop").slot;
|
||||
}
|
||||
|
||||
variables.decrementScope();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(MethodWriter writer) {
|
||||
writer.writeStatementOffset(location);
|
||||
|
||||
expression.write(writer);
|
||||
writer.visitVarInsn(array.type.type.getOpcode(Opcodes.ISTORE), array.slot);
|
||||
writer.push(-1);
|
||||
writer.visitVarInsn(index.type.type.getOpcode(Opcodes.ISTORE), index.slot);
|
||||
|
||||
Label begin = new Label();
|
||||
Label end = new Label();
|
||||
|
||||
writer.mark(begin);
|
||||
|
||||
writer.visitIincInsn(index.slot, 1);
|
||||
writer.visitVarInsn(index.type.type.getOpcode(Opcodes.ILOAD), index.slot);
|
||||
writer.visitVarInsn(array.type.type.getOpcode(Opcodes.ILOAD), array.slot);
|
||||
writer.arrayLength();
|
||||
writer.ifICmp(MethodWriter.GE, end);
|
||||
|
||||
writer.visitVarInsn(array.type.type.getOpcode(Opcodes.ILOAD), array.slot);
|
||||
writer.visitVarInsn(index.type.type.getOpcode(Opcodes.ILOAD), index.slot);
|
||||
writer.arrayLoad(indexed.type);
|
||||
writer.writeCast(cast);
|
||||
writer.visitVarInsn(variable.type.type.getOpcode(Opcodes.ISTORE), variable.slot);
|
||||
|
||||
block.write(writer);
|
||||
|
||||
writer.goTo(begin);
|
||||
writer.mark(end);
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ import org.elasticsearch.painless.Variables;
|
|||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -35,27 +36,29 @@ public final class SBlock extends AStatement {
|
|||
public SBlock(Location location, List<AStatement> statements) {
|
||||
super(location);
|
||||
|
||||
this.statements = statements;
|
||||
this.statements = Collections.unmodifiableList(statements);
|
||||
}
|
||||
|
||||
@Override
|
||||
AStatement analyze(Variables variables) {
|
||||
void analyze(Variables variables) {
|
||||
if (statements == null || statements.isEmpty()) {
|
||||
throw createError(new IllegalArgumentException("A block must contain at least one statement."));
|
||||
}
|
||||
|
||||
for (int index = 0; index < statements.size(); ++index) {
|
||||
AStatement last = statements.get(statements.size() - 1);
|
||||
|
||||
for (AStatement statement : statements) {
|
||||
// Note that we do not need to check after the last statement because
|
||||
// there is no statement that can be unreachable after the last.
|
||||
if (allEscape) {
|
||||
throw createError(new IllegalArgumentException("Unreachable statement."));
|
||||
}
|
||||
|
||||
AStatement statement = statements.get(index);
|
||||
|
||||
statement.inLoop = inLoop;
|
||||
statement.lastSource = lastSource && index == statements.size() - 1;
|
||||
statement.lastLoop = (beginLoop || lastLoop) && index == statements.size() - 1;
|
||||
statement.lastSource = lastSource && statement == last;
|
||||
statement.lastLoop = (beginLoop || lastLoop) && statement == last;
|
||||
|
||||
statements.set(index, statement.analyze(variables));
|
||||
statement.analyze(variables);
|
||||
|
||||
methodEscape = statement.methodEscape;
|
||||
loopEscape = statement.loopEscape;
|
||||
|
@ -64,8 +67,6 @@ public final class SBlock extends AStatement {
|
|||
anyBreak |= statement.anyBreak;
|
||||
statementCount += statement.statementCount;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -33,7 +33,7 @@ public final class SBreak extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
AStatement analyze(Variables variables) {
|
||||
void analyze(Variables variables) {
|
||||
if (!inLoop) {
|
||||
throw createError(new IllegalArgumentException("Break statement outside of a loop."));
|
||||
}
|
||||
|
@ -42,8 +42,6 @@ public final class SBreak extends AStatement {
|
|||
allEscape = true;
|
||||
anyBreak = true;
|
||||
statementCount = 1;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -35,7 +35,7 @@ public final class SCatch extends AStatement {
|
|||
|
||||
final String type;
|
||||
final String name;
|
||||
AStatement block;
|
||||
final SBlock block;
|
||||
|
||||
Variable variable;
|
||||
|
||||
|
@ -52,7 +52,7 @@ public final class SCatch extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
AStatement analyze(Variables variables) {
|
||||
void analyze(Variables variables) {
|
||||
final Type type;
|
||||
|
||||
try {
|
||||
|
@ -72,7 +72,7 @@ public final class SCatch extends AStatement {
|
|||
block.inLoop = inLoop;
|
||||
block.lastLoop = lastLoop;
|
||||
|
||||
block = block.analyze(variables);
|
||||
block.analyze(variables);
|
||||
|
||||
methodEscape = block.methodEscape;
|
||||
loopEscape = block.loopEscape;
|
||||
|
@ -81,8 +81,6 @@ public final class SCatch extends AStatement {
|
|||
anyBreak = block.anyBreak;
|
||||
statementCount = block.statementCount;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -33,7 +33,7 @@ public final class SContinue extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
AStatement analyze(Variables variables) {
|
||||
void analyze(Variables variables) {
|
||||
if (!inLoop) {
|
||||
throw createError(new IllegalArgumentException("Continue statement outside of a loop."));
|
||||
}
|
||||
|
@ -45,8 +45,6 @@ public final class SContinue extends AStatement {
|
|||
allEscape = true;
|
||||
anyContinue = true;
|
||||
statementCount = 1;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.elasticsearch.painless.Variables;
|
|||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -35,20 +36,16 @@ public final class SDeclBlock extends AStatement {
|
|||
public SDeclBlock(Location location, List<SDeclaration> declarations) {
|
||||
super(location);
|
||||
|
||||
this.declarations = declarations;
|
||||
this.declarations = Collections.unmodifiableList(declarations);
|
||||
}
|
||||
|
||||
@Override
|
||||
AStatement analyze(Variables variables) {
|
||||
for (int index = 0; index < declarations.size(); ++index) {
|
||||
AStatement declaration = declarations.get(index);
|
||||
|
||||
declarations.set(index, (SDeclaration)declaration.analyze(variables));
|
||||
void analyze(Variables variables) {
|
||||
for (SDeclaration declaration : declarations) {
|
||||
declaration.analyze(variables);
|
||||
}
|
||||
|
||||
statementCount = declarations.size();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -47,7 +47,7 @@ public final class SDeclaration extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
AStatement analyze(Variables variables) {
|
||||
void analyze(Variables variables) {
|
||||
final Type type;
|
||||
|
||||
try {
|
||||
|
@ -63,8 +63,6 @@ public final class SDeclaration extends AStatement {
|
|||
}
|
||||
|
||||
variable = variables.addVariable(location, type, name, false, false);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -31,7 +31,7 @@ import org.elasticsearch.painless.MethodWriter;
|
|||
public final class SDo extends AStatement {
|
||||
|
||||
final int maxLoopCounter;
|
||||
AStatement block;
|
||||
final SBlock block;
|
||||
AExpression condition;
|
||||
|
||||
public SDo(Location location, int maxLoopCounter, SBlock block, AExpression condition) {
|
||||
|
@ -43,7 +43,7 @@ public final class SDo extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
AStatement analyze(Variables variables) {
|
||||
void analyze(Variables variables) {
|
||||
variables.incrementScope();
|
||||
|
||||
if (block == null) {
|
||||
|
@ -53,7 +53,7 @@ public final class SDo extends AStatement {
|
|||
block.beginLoop = true;
|
||||
block.inLoop = true;
|
||||
|
||||
block = block.analyze(variables);
|
||||
block.analyze(variables);
|
||||
|
||||
if (block.loopEscape && !block.anyContinue) {
|
||||
throw createError(new IllegalArgumentException("Extraneous do while loop."));
|
||||
|
@ -83,8 +83,6 @@ public final class SDo extends AStatement {
|
|||
}
|
||||
|
||||
variables.decrementScope();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -45,14 +45,22 @@ public class SEach extends AStatement {
|
|||
final String type;
|
||||
final String name;
|
||||
AExpression expression;
|
||||
AStatement block;
|
||||
final SBlock block;
|
||||
|
||||
// Members for all cases.
|
||||
Variable variable = null;
|
||||
Cast cast = null;
|
||||
|
||||
// Members for the array case.
|
||||
Variable array = null;
|
||||
Variable index = null;
|
||||
Type indexed = null;
|
||||
|
||||
// Members for the iterable case.
|
||||
Variable iterator = null;
|
||||
Method method = null;
|
||||
Method hasNext = null;
|
||||
Method next = null;
|
||||
Cast cast = null;
|
||||
|
||||
public SEach(Location location, int maxLoopCounter, String type, String name, AExpression expression, SBlock block) {
|
||||
super(location);
|
||||
|
@ -65,92 +73,172 @@ public class SEach extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
AStatement analyze(Variables variables) {
|
||||
void analyze(Variables variables) {
|
||||
expression.analyze(variables);
|
||||
expression.expected = expression.actual;
|
||||
expression = expression.cast(variables);
|
||||
|
||||
Sort sort = expression.actual.sort;
|
||||
|
||||
if (sort == Sort.ARRAY) {
|
||||
return new SArrayEach(location, maxLoopCounter, type, name, expression, (SBlock)block).copy(this).analyze(variables);
|
||||
} else if (sort == Sort.DEF || Iterable.class.isAssignableFrom(expression.actual.clazz)) {
|
||||
final Type type;
|
||||
|
||||
try {
|
||||
type = Definition.getType(this.type);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
||||
}
|
||||
|
||||
variables.incrementScope();
|
||||
|
||||
Type itr = Definition.getType("Iterator");
|
||||
|
||||
variable = variables.addVariable(location, type, name, true, false);
|
||||
|
||||
// We must store the iterator as a variable for securing a slot on the stack, and
|
||||
// also add the location offset to make the name unique in case of nested for each loops.
|
||||
iterator = variables.addVariable(location, itr, "#itr" + location.getOffset(), true, false);
|
||||
|
||||
if (sort == Sort.DEF) {
|
||||
method = null;
|
||||
} else {
|
||||
method = expression.actual.struct.methods.get(new MethodKey("iterator", 0));
|
||||
|
||||
if (method == null) {
|
||||
throw location.createError(new IllegalArgumentException(
|
||||
"Unable to create iterator for the type [" + expression.actual.name + "]."));
|
||||
}
|
||||
}
|
||||
|
||||
hasNext = itr.struct.methods.get(new MethodKey("hasNext", 0));
|
||||
|
||||
if (hasNext == null) {
|
||||
throw location.createError(new IllegalArgumentException("Method [hasNext] does not exist for type [Iterator]."));
|
||||
} else if (hasNext.rtn.sort != Sort.BOOL) {
|
||||
throw location.createError(new IllegalArgumentException("Method [hasNext] does not return type [boolean]."));
|
||||
}
|
||||
|
||||
next = itr.struct.methods.get(new MethodKey("next", 0));
|
||||
|
||||
if (next == null) {
|
||||
throw location.createError(new IllegalArgumentException("Method [next] does not exist for type [Iterator]."));
|
||||
} else if (next.rtn.sort != Sort.DEF) {
|
||||
throw location.createError(new IllegalArgumentException("Method [next] does not return type [def]."));
|
||||
}
|
||||
|
||||
cast = AnalyzerCaster.getLegalCast(location, Definition.DEF_TYPE, type, true, true);
|
||||
|
||||
if (block == null) {
|
||||
throw location.createError(new IllegalArgumentException("Extraneous for each loop."));
|
||||
}
|
||||
|
||||
block.beginLoop = true;
|
||||
block.inLoop = true;
|
||||
block = block.analyze(variables);
|
||||
block.statementCount = Math.max(1, block.statementCount);
|
||||
|
||||
if (block.loopEscape && !block.anyContinue) {
|
||||
throw createError(new IllegalArgumentException("Extraneous for loop."));
|
||||
}
|
||||
|
||||
statementCount = 1;
|
||||
|
||||
if (maxLoopCounter > 0) {
|
||||
loopCounterSlot = variables.getVariable(location, "#loop").slot;
|
||||
}
|
||||
|
||||
variables.decrementScope();
|
||||
|
||||
return this;
|
||||
if (expression.actual.sort == Sort.ARRAY) {
|
||||
analyzeArray(variables);
|
||||
} else if (expression.actual.sort == Sort.DEF || Iterable.class.isAssignableFrom(expression.actual.clazz)) {
|
||||
analyzeIterable(variables);
|
||||
} else {
|
||||
throw location.createError(new IllegalArgumentException("Illegal for each type [" + expression.actual.name + "]."));
|
||||
}
|
||||
}
|
||||
|
||||
void analyzeArray(Variables variables) {
|
||||
final Type type;
|
||||
|
||||
try {
|
||||
type = Definition.getType(this.type);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
||||
}
|
||||
|
||||
variables.incrementScope();
|
||||
|
||||
variable = variables.addVariable(location, type, name, true, false);
|
||||
array = variables.addVariable(location, expression.actual, "#array" + location.getOffset(), true, false);
|
||||
index = variables.addVariable(location, Definition.INT_TYPE, "#index" + location.getOffset(), true, false);
|
||||
indexed = Definition.getType(expression.actual.struct, expression.actual.dimensions - 1);
|
||||
cast = AnalyzerCaster.getLegalCast(location, indexed, type, true, true);
|
||||
|
||||
if (block == null) {
|
||||
throw location.createError(new IllegalArgumentException("Extraneous for each loop."));
|
||||
}
|
||||
|
||||
block.beginLoop = true;
|
||||
block.inLoop = true;
|
||||
block.analyze(variables);
|
||||
block.statementCount = Math.max(1, block.statementCount);
|
||||
|
||||
if (block.loopEscape && !block.anyContinue) {
|
||||
throw createError(new IllegalArgumentException("Extraneous for loop."));
|
||||
}
|
||||
|
||||
statementCount = 1;
|
||||
|
||||
if (maxLoopCounter > 0) {
|
||||
loopCounterSlot = variables.getVariable(location, "#loop").slot;
|
||||
}
|
||||
|
||||
variables.decrementScope();
|
||||
}
|
||||
|
||||
void analyzeIterable(Variables variables) {
|
||||
final Type type;
|
||||
|
||||
try {
|
||||
type = Definition.getType(this.type);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
||||
}
|
||||
|
||||
variables.incrementScope();
|
||||
|
||||
Type itr = Definition.getType("Iterator");
|
||||
|
||||
variable = variables.addVariable(location, type, name, true, false);
|
||||
|
||||
// We must store the iterator as a variable for securing a slot on the stack, and
|
||||
// also add the location offset to make the name unique in case of nested for each loops.
|
||||
iterator = variables.addVariable(location, itr, "#itr" + location.getOffset(), true, false);
|
||||
|
||||
if (expression.actual.sort == Sort.DEF) {
|
||||
method = null;
|
||||
} else {
|
||||
method = expression.actual.struct.methods.get(new MethodKey("iterator", 0));
|
||||
|
||||
if (method == null) {
|
||||
throw location.createError(new IllegalArgumentException(
|
||||
"Unable to create iterator for the type [" + expression.actual.name + "]."));
|
||||
}
|
||||
}
|
||||
|
||||
hasNext = itr.struct.methods.get(new MethodKey("hasNext", 0));
|
||||
|
||||
if (hasNext == null) {
|
||||
throw location.createError(new IllegalArgumentException("Method [hasNext] does not exist for type [Iterator]."));
|
||||
} else if (hasNext.rtn.sort != Sort.BOOL) {
|
||||
throw location.createError(new IllegalArgumentException("Method [hasNext] does not return type [boolean]."));
|
||||
}
|
||||
|
||||
next = itr.struct.methods.get(new MethodKey("next", 0));
|
||||
|
||||
if (next == null) {
|
||||
throw location.createError(new IllegalArgumentException("Method [next] does not exist for type [Iterator]."));
|
||||
} else if (next.rtn.sort != Sort.DEF) {
|
||||
throw location.createError(new IllegalArgumentException("Method [next] does not return type [def]."));
|
||||
}
|
||||
|
||||
cast = AnalyzerCaster.getLegalCast(location, Definition.DEF_TYPE, type, true, true);
|
||||
|
||||
if (block == null) {
|
||||
throw location.createError(new IllegalArgumentException("Extraneous for each loop."));
|
||||
}
|
||||
|
||||
block.beginLoop = true;
|
||||
block.inLoop = true;
|
||||
block.analyze(variables);
|
||||
block.statementCount = Math.max(1, block.statementCount);
|
||||
|
||||
if (block.loopEscape && !block.anyContinue) {
|
||||
throw createError(new IllegalArgumentException("Extraneous for loop."));
|
||||
}
|
||||
|
||||
statementCount = 1;
|
||||
|
||||
if (maxLoopCounter > 0) {
|
||||
loopCounterSlot = variables.getVariable(location, "#loop").slot;
|
||||
}
|
||||
|
||||
variables.decrementScope();
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(MethodWriter writer) {
|
||||
if (array != null) {
|
||||
writeArray(writer);
|
||||
} else if (iterator != null) {
|
||||
writeIterable(writer);
|
||||
} else {
|
||||
throw location.createError(new IllegalStateException("Illegal tree structure."));
|
||||
}
|
||||
}
|
||||
|
||||
void writeArray(MethodWriter writer) {
|
||||
writer.writeStatementOffset(location);
|
||||
|
||||
expression.write(writer);
|
||||
writer.visitVarInsn(array.type.type.getOpcode(Opcodes.ISTORE), array.slot);
|
||||
writer.push(-1);
|
||||
writer.visitVarInsn(index.type.type.getOpcode(Opcodes.ISTORE), index.slot);
|
||||
|
||||
Label begin = new Label();
|
||||
Label end = new Label();
|
||||
|
||||
writer.mark(begin);
|
||||
|
||||
writer.visitIincInsn(index.slot, 1);
|
||||
writer.visitVarInsn(index.type.type.getOpcode(Opcodes.ILOAD), index.slot);
|
||||
writer.visitVarInsn(array.type.type.getOpcode(Opcodes.ILOAD), array.slot);
|
||||
writer.arrayLength();
|
||||
writer.ifICmp(MethodWriter.GE, end);
|
||||
|
||||
writer.visitVarInsn(array.type.type.getOpcode(Opcodes.ILOAD), array.slot);
|
||||
writer.visitVarInsn(index.type.type.getOpcode(Opcodes.ILOAD), index.slot);
|
||||
writer.arrayLoad(indexed.type);
|
||||
writer.writeCast(cast);
|
||||
writer.visitVarInsn(variable.type.type.getOpcode(Opcodes.ISTORE), variable.slot);
|
||||
|
||||
block.write(writer);
|
||||
|
||||
writer.goTo(begin);
|
||||
writer.mark(end);
|
||||
}
|
||||
|
||||
void writeIterable(MethodWriter writer) {
|
||||
writer.writeStatementOffset(location);
|
||||
|
||||
expression.write(writer);
|
||||
|
|
|
@ -39,7 +39,7 @@ public final class SExpression extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
AStatement analyze(Variables variables) {
|
||||
void analyze(Variables variables) {
|
||||
expression.read = lastSource;
|
||||
expression.analyze(variables);
|
||||
|
||||
|
@ -57,8 +57,6 @@ public final class SExpression extends AStatement {
|
|||
loopEscape = rtn;
|
||||
allEscape = rtn;
|
||||
statementCount = 1;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -34,7 +34,7 @@ public final class SFor extends AStatement {
|
|||
ANode initializer;
|
||||
AExpression condition;
|
||||
AExpression afterthought;
|
||||
AStatement block;
|
||||
final SBlock block;
|
||||
|
||||
public SFor(Location location, int maxLoopCounter,
|
||||
ANode initializer, AExpression condition, AExpression afterthought, SBlock block) {
|
||||
|
@ -48,14 +48,14 @@ public final class SFor extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
AStatement analyze(Variables variables) {
|
||||
void analyze(Variables variables) {
|
||||
variables.incrementScope();
|
||||
|
||||
boolean continuous = false;
|
||||
|
||||
if (initializer != null) {
|
||||
if (initializer instanceof AStatement) {
|
||||
initializer = ((AStatement)initializer).analyze(variables);
|
||||
((AStatement)initializer).analyze(variables);
|
||||
} else if (initializer instanceof AExpression) {
|
||||
AExpression initializer = (AExpression)this.initializer;
|
||||
|
||||
|
@ -103,7 +103,7 @@ public final class SFor extends AStatement {
|
|||
block.beginLoop = true;
|
||||
block.inLoop = true;
|
||||
|
||||
block = block.analyze(variables);
|
||||
block.analyze(variables);
|
||||
|
||||
if (block.loopEscape && !block.anyContinue) {
|
||||
throw createError(new IllegalArgumentException("Extraneous for loop."));
|
||||
|
@ -124,8 +124,6 @@ public final class SFor extends AStatement {
|
|||
}
|
||||
|
||||
variables.decrementScope();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -31,7 +31,7 @@ import org.elasticsearch.painless.MethodWriter;
|
|||
public final class SIf extends AStatement {
|
||||
|
||||
AExpression condition;
|
||||
AStatement ifblock;
|
||||
final SBlock ifblock;
|
||||
|
||||
public SIf(Location location, AExpression condition, SBlock ifblock) {
|
||||
super(location);
|
||||
|
@ -41,7 +41,7 @@ public final class SIf extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
AStatement analyze(Variables variables) {
|
||||
void analyze(Variables variables) {
|
||||
condition.expected = Definition.BOOLEAN_TYPE;
|
||||
condition.analyze(variables);
|
||||
condition = condition.cast(variables);
|
||||
|
@ -59,14 +59,12 @@ public final class SIf extends AStatement {
|
|||
ifblock.lastLoop = lastLoop;
|
||||
|
||||
variables.incrementScope();
|
||||
ifblock = ifblock.analyze(variables);
|
||||
ifblock.analyze(variables);
|
||||
variables.decrementScope();
|
||||
|
||||
anyContinue = ifblock.anyContinue;
|
||||
anyBreak = ifblock.anyBreak;
|
||||
statementCount = ifblock.statementCount;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -31,8 +31,8 @@ import org.elasticsearch.painless.MethodWriter;
|
|||
public final class SIfElse extends AStatement {
|
||||
|
||||
AExpression condition;
|
||||
AStatement ifblock;
|
||||
AStatement elseblock;
|
||||
final SBlock ifblock;
|
||||
final SBlock elseblock;
|
||||
|
||||
public SIfElse(Location location, AExpression condition, SBlock ifblock, SBlock elseblock) {
|
||||
super(location);
|
||||
|
@ -43,7 +43,7 @@ public final class SIfElse extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
AStatement analyze(Variables variables) {
|
||||
void analyze(Variables variables) {
|
||||
condition.expected = Definition.BOOLEAN_TYPE;
|
||||
condition.analyze(variables);
|
||||
condition = condition.cast(variables);
|
||||
|
@ -61,7 +61,7 @@ public final class SIfElse extends AStatement {
|
|||
ifblock.lastLoop = lastLoop;
|
||||
|
||||
variables.incrementScope();
|
||||
ifblock = ifblock.analyze(variables);
|
||||
ifblock.analyze(variables);
|
||||
variables.decrementScope();
|
||||
|
||||
anyContinue = ifblock.anyContinue;
|
||||
|
@ -77,7 +77,7 @@ public final class SIfElse extends AStatement {
|
|||
elseblock.lastLoop = lastLoop;
|
||||
|
||||
variables.incrementScope();
|
||||
elseblock = elseblock.analyze(variables);
|
||||
elseblock.analyze(variables);
|
||||
variables.decrementScope();
|
||||
|
||||
methodEscape = ifblock.methodEscape && elseblock.methodEscape;
|
||||
|
@ -86,8 +86,6 @@ public final class SIfElse extends AStatement {
|
|||
anyContinue |= elseblock.anyContinue;
|
||||
anyBreak |= elseblock.anyBreak;
|
||||
statementCount = Math.max(ifblock.statementCount, elseblock.statementCount);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -38,7 +38,7 @@ public final class SReturn extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
AStatement analyze(Variables variables) {
|
||||
void analyze(Variables variables) {
|
||||
expression.expected = Definition.OBJECT_TYPE;
|
||||
expression.internal = true;
|
||||
expression.analyze(variables);
|
||||
|
@ -49,8 +49,6 @@ public final class SReturn extends AStatement {
|
|||
allEscape = true;
|
||||
|
||||
statementCount = 1;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.objectweb.asm.Opcodes;
|
|||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -36,36 +37,35 @@ public final class SSource extends AStatement {
|
|||
public SSource(Location location, List<AStatement> statements) {
|
||||
super(location);
|
||||
|
||||
this.statements = statements;
|
||||
this.statements = Collections.unmodifiableList(statements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AStatement analyze(Variables variables) {
|
||||
public void analyze(Variables variables) {
|
||||
if (statements == null || statements.isEmpty()) {
|
||||
throw createError(new IllegalArgumentException("Cannot generate an empty script."));
|
||||
}
|
||||
|
||||
variables.incrementScope();
|
||||
|
||||
for (int index = 0; index < statements.size(); ++index) {
|
||||
AStatement statement = statements.get(index);
|
||||
AStatement last = statements.get(statements.size() - 1);
|
||||
|
||||
for (AStatement statement : statements) {
|
||||
// Note that we do not need to check after the last statement because
|
||||
// there is no statement that can be unreachable after the last.
|
||||
if (allEscape) {
|
||||
throw createError(new IllegalArgumentException("Unreachable statement."));
|
||||
}
|
||||
|
||||
statement.lastSource = index == statements.size() - 1;
|
||||
statements.set(index, statement.analyze(variables));
|
||||
statement.lastSource = statement == last;
|
||||
|
||||
statement.analyze(variables);
|
||||
|
||||
methodEscape = statement.methodEscape;
|
||||
allEscape = statement.allEscape;
|
||||
}
|
||||
|
||||
variables.decrementScope();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -38,7 +38,7 @@ public final class SThrow extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
AStatement analyze(Variables variables) {
|
||||
void analyze(Variables variables) {
|
||||
expression.expected = Definition.EXCEPTION_TYPE;
|
||||
expression.analyze(variables);
|
||||
expression = expression.cast(variables);
|
||||
|
@ -47,8 +47,6 @@ public final class SThrow extends AStatement {
|
|||
loopEscape = true;
|
||||
allEscape = true;
|
||||
statementCount = 1;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.objectweb.asm.Label;
|
|||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -31,18 +32,18 @@ import java.util.List;
|
|||
*/
|
||||
public final class STry extends AStatement {
|
||||
|
||||
AStatement block;
|
||||
final SBlock block;
|
||||
final List<SCatch> catches;
|
||||
|
||||
public STry(Location location, SBlock block, List<SCatch> catches) {
|
||||
super(location);
|
||||
|
||||
this.block = block;
|
||||
this.catches = catches;
|
||||
this.catches = Collections.unmodifiableList(catches);
|
||||
}
|
||||
|
||||
@Override
|
||||
AStatement analyze(Variables variables) {
|
||||
void analyze(Variables variables) {
|
||||
if (block == null) {
|
||||
throw createError(new IllegalArgumentException("Extraneous try statement."));
|
||||
}
|
||||
|
@ -52,7 +53,7 @@ public final class STry extends AStatement {
|
|||
block.lastLoop = lastLoop;
|
||||
|
||||
variables.incrementScope();
|
||||
block = block.analyze(variables);
|
||||
block.analyze(variables);
|
||||
variables.decrementScope();
|
||||
|
||||
methodEscape = block.methodEscape;
|
||||
|
@ -63,15 +64,13 @@ public final class STry extends AStatement {
|
|||
|
||||
int statementCount = 0;
|
||||
|
||||
for (int index = 0; index < catches.size(); ++index) {
|
||||
SCatch catc = catches.get(index);
|
||||
|
||||
for (SCatch catc : catches) {
|
||||
catc.lastSource = lastSource;
|
||||
catc.inLoop = inLoop;
|
||||
catc.lastLoop = lastLoop;
|
||||
|
||||
variables.incrementScope();
|
||||
catches.set(index, (SCatch)catc.analyze(variables));
|
||||
catc.analyze(variables);
|
||||
variables.decrementScope();
|
||||
|
||||
methodEscape &= catc.methodEscape;
|
||||
|
@ -84,8 +83,6 @@ public final class STry extends AStatement {
|
|||
}
|
||||
|
||||
this.statementCount = block.statementCount + statementCount;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -32,7 +32,7 @@ public final class SWhile extends AStatement {
|
|||
|
||||
final int maxLoopCounter;
|
||||
AExpression condition;
|
||||
AStatement block;
|
||||
final SBlock block;
|
||||
|
||||
public SWhile(Location location, int maxLoopCounter, AExpression condition, SBlock block) {
|
||||
super(location);
|
||||
|
@ -43,7 +43,7 @@ public final class SWhile extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
AStatement analyze(Variables variables) {
|
||||
void analyze(Variables variables) {
|
||||
variables.incrementScope();
|
||||
|
||||
condition.expected = Definition.BOOLEAN_TYPE;
|
||||
|
@ -68,7 +68,7 @@ public final class SWhile extends AStatement {
|
|||
block.beginLoop = true;
|
||||
block.inLoop = true;
|
||||
|
||||
block = block.analyze(variables);
|
||||
block.analyze(variables);
|
||||
|
||||
if (block.loopEscape && !block.anyContinue) {
|
||||
throw createError(new IllegalArgumentException("Extraneous while loop."));
|
||||
|
@ -89,8 +89,6 @@ public final class SWhile extends AStatement {
|
|||
}
|
||||
|
||||
variables.decrementScope();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -63,7 +63,6 @@
|
|||
* {@link org.elasticsearch.painless.node.LStatic} - Represents a static type target.
|
||||
* {@link org.elasticsearch.painless.node.LString} - Represents a string constant.
|
||||
* {@link org.elasticsearch.painless.node.LVariable} - Represents a variable load/store.
|
||||
* {@link org.elasticsearch.painless.node.SArrayEach} - Represents a for each loop shortcut for arrays. (Internal only.)
|
||||
* {@link org.elasticsearch.painless.node.SBlock} - Represents a set of statements as a branch of control-flow.
|
||||
* {@link org.elasticsearch.painless.node.SBreak} - Represents a break statement.
|
||||
* {@link org.elasticsearch.painless.node.SCatch} - Represents a catch block as part of a try-catch block.
|
||||
|
@ -92,8 +91,7 @@
|
|||
* <p>
|
||||
* Generally, statement nodes have member data that evaluate legal control-flow during the analysis phase.
|
||||
* The typical order for statement nodes is for each node to call analyze on it's children during the analysis phase
|
||||
* and write on it's children during the writing phase. Upon analysis completion, a statement will return either
|
||||
* itself or another statement node depending on if a shortcut or def type was found.
|
||||
* and write on it's children during the writing phase.
|
||||
* <p>
|
||||
* Generally, expression nodes have member data that evaluate static types. The typical order for an expression node
|
||||
* during the analysis phase looks like the following:
|
||||
|
|
Loading…
Reference in New Issue