parent
86f1bedaab
commit
f98be6fa06
|
@ -22,7 +22,15 @@ parser grammar PainlessParser;
|
|||
options { tokenVocab=PainlessLexer; }
|
||||
|
||||
source
|
||||
: statement* EOF
|
||||
: function* statement* EOF
|
||||
;
|
||||
|
||||
function
|
||||
: decltype ID parameters block
|
||||
;
|
||||
|
||||
parameters
|
||||
: LP ( decltype ID ( COMMA decltype ID )* )? RP
|
||||
;
|
||||
|
||||
// Note we use a predicate on the if/else case here to prevent the
|
||||
|
@ -143,6 +151,7 @@ primary[boolean c] returns [boolean s = true]
|
|||
| { $c }? LP unary[true] RP # chainprec
|
||||
| STRING # string
|
||||
| ID # variable
|
||||
| ID arguments # calllocal
|
||||
| NEW TYPE arguments # newobject
|
||||
;
|
||||
|
||||
|
|
|
@ -1,37 +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;
|
||||
|
||||
import org.elasticsearch.painless.Variables.Reserved;
|
||||
import org.elasticsearch.painless.node.SSource;
|
||||
|
||||
/**
|
||||
* Runs the analysis phase of compilation using the Painless AST.
|
||||
*/
|
||||
final class Analyzer {
|
||||
static Variables analyze(Reserved shortcut, SSource root) {
|
||||
Variables variables = new Variables(shortcut);
|
||||
root.analyze(variables);
|
||||
|
||||
return variables;
|
||||
}
|
||||
|
||||
private Analyzer() {}
|
||||
}
|
|
@ -20,7 +20,6 @@
|
|||
package org.elasticsearch.painless;
|
||||
|
||||
import org.elasticsearch.bootstrap.BootstrapInfo;
|
||||
import org.elasticsearch.painless.Variables.Reserved;
|
||||
import org.elasticsearch.painless.antlr.Walker;
|
||||
import org.elasticsearch.painless.node.SSource;
|
||||
|
||||
|
@ -36,8 +35,7 @@ import static org.elasticsearch.painless.WriterConstants.CLASS_NAME;
|
|||
/**
|
||||
* The Compiler is the entry point for generating a Painless script. The compiler will receive a Painless
|
||||
* tree based on the type of input passed in (currently only ANTLR). Two passes will then be run over the tree,
|
||||
* one for analysis using the {@link Analyzer} and another to generate the actual byte code using ASM in
|
||||
* the {@link Writer}.
|
||||
* one for analysis and another to generate the actual byte code using ASM using the root of the tree {@link SSource}.
|
||||
*/
|
||||
final class Compiler {
|
||||
|
||||
|
@ -100,18 +98,17 @@ final class Compiler {
|
|||
" plugin if a script longer than this length is a requirement.");
|
||||
}
|
||||
|
||||
Reserved reserved = new Reserved();
|
||||
SSource root = Walker.buildPainlessTree(name, 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);
|
||||
SSource root = Walker.buildPainlessTree(name, source, settings);
|
||||
|
||||
root.analyze();
|
||||
root.write();
|
||||
|
||||
try {
|
||||
Class<? extends Executable> clazz = loader.define(CLASS_NAME, bytes);
|
||||
Class<? extends Executable> clazz = loader.define(CLASS_NAME, root.getBytes());
|
||||
java.lang.reflect.Constructor<? extends Executable> constructor =
|
||||
clazz.getConstructor(String.class, String.class, BitSet.class);
|
||||
|
||||
return constructor.newInstance(name, source, expressions);
|
||||
return constructor.newInstance(name, source, root.getExpressions());
|
||||
} 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);
|
||||
}
|
||||
|
@ -130,11 +127,12 @@ final class Compiler {
|
|||
" plugin if a script longer than this length is a requirement.");
|
||||
}
|
||||
|
||||
Reserved reserved = new Reserved();
|
||||
SSource root = Walker.buildPainlessTree(name, source, reserved, settings);
|
||||
Variables variables = Analyzer.analyze(reserved, root);
|
||||
SSource root = Walker.buildPainlessTree(name, source, settings);
|
||||
|
||||
return Writer.write(settings, name, source, variables, root, new BitSet(source.length()));
|
||||
root.analyze();
|
||||
root.write();
|
||||
|
||||
return root.getBytes();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -45,9 +45,9 @@ import java.util.Spliterator;
|
|||
* methods and fields during at both compile-time and runtime.
|
||||
*/
|
||||
public final class Definition {
|
||||
|
||||
|
||||
private static final List<String> DEFINITION_FILES = Collections.unmodifiableList(
|
||||
Arrays.asList("org.elasticsearch.txt",
|
||||
Arrays.asList("org.elasticsearch.txt",
|
||||
"java.lang.txt",
|
||||
"java.math.txt",
|
||||
"java.text.txt",
|
||||
|
@ -188,8 +188,8 @@ public final class Definition {
|
|||
public final int modifiers;
|
||||
public final MethodHandle handle;
|
||||
|
||||
private Method(String name, Struct owner, Type rtn, List<Type> arguments,
|
||||
org.objectweb.asm.commons.Method method, int modifiers, MethodHandle handle) {
|
||||
public Method(String name, Struct owner, Type rtn, List<Type> arguments,
|
||||
org.objectweb.asm.commons.Method method, int modifiers, MethodHandle handle) {
|
||||
this.name = name;
|
||||
this.owner = owner;
|
||||
this.rtn = rtn;
|
||||
|
@ -286,7 +286,7 @@ public final class Definition {
|
|||
|
||||
public final Map<String, Field> staticMembers;
|
||||
public final Map<String, Field> members;
|
||||
|
||||
|
||||
private final SetOnce<Method> functionalMethod;
|
||||
|
||||
private Struct(final String name, final Class<?> clazz, final org.objectweb.asm.Type type) {
|
||||
|
@ -300,8 +300,8 @@ public final class Definition {
|
|||
|
||||
staticMembers = new HashMap<>();
|
||||
members = new HashMap<>();
|
||||
|
||||
functionalMethod = new SetOnce<Method>();
|
||||
|
||||
functionalMethod = new SetOnce<>();
|
||||
}
|
||||
|
||||
private Struct(final Struct struct) {
|
||||
|
@ -315,7 +315,7 @@ public final class Definition {
|
|||
|
||||
staticMembers = Collections.unmodifiableMap(struct.staticMembers);
|
||||
members = Collections.unmodifiableMap(struct.members);
|
||||
|
||||
|
||||
functionalMethod = struct.functionalMethod;
|
||||
}
|
||||
|
||||
|
@ -342,8 +342,8 @@ public final class Definition {
|
|||
public int hashCode() {
|
||||
return name.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* If this class is a functional interface according to JLS, returns its method.
|
||||
* Otherwise returns null.
|
||||
*/
|
||||
|
@ -637,7 +637,7 @@ public final class Definition {
|
|||
final org.objectweb.asm.commons.Method asm = org.objectweb.asm.commons.Method.getMethod(reflect);
|
||||
final Type returnType = getTypeInternal("void");
|
||||
final MethodHandle handle;
|
||||
|
||||
|
||||
try {
|
||||
handle = MethodHandles.publicLookup().in(owner.clazz).unreflectConstructor(reflect);
|
||||
} catch (final IllegalAccessException exception) {
|
||||
|
@ -645,7 +645,7 @@ public final class Definition {
|
|||
" not found for class [" + owner.clazz.getName() + "]" +
|
||||
" with arguments " + Arrays.toString(classes) + ".");
|
||||
}
|
||||
|
||||
|
||||
final Method constructor = new Method(name, owner, returnType, Arrays.asList(args), asm, reflect.getModifiers(), handle);
|
||||
|
||||
owner.constructors.put(methodKey, constructor);
|
||||
|
@ -755,7 +755,7 @@ public final class Definition {
|
|||
" method [" + name + "]" +
|
||||
" within the struct [" + owner.name + "].");
|
||||
}
|
||||
|
||||
|
||||
final org.objectweb.asm.commons.Method asm = org.objectweb.asm.commons.Method.getMethod(reflect);
|
||||
|
||||
MethodHandle handle;
|
||||
|
@ -856,7 +856,7 @@ public final class Definition {
|
|||
throw new ClassCastException("Child struct [" + child.name + "]" +
|
||||
" is not a super type of owner struct [" + owner.name + "] in copy.");
|
||||
}
|
||||
|
||||
|
||||
for (Map.Entry<MethodKey,Method> kvPair : child.methods.entrySet()) {
|
||||
MethodKey methodKey = kvPair.getKey();
|
||||
Method method = kvPair.getValue();
|
||||
|
@ -954,7 +954,7 @@ public final class Definition {
|
|||
|
||||
runtimeMap.put(struct.clazz, new RuntimeClass(methods, getters, setters));
|
||||
}
|
||||
|
||||
|
||||
/** computes the functional interface method for a class, or returns null */
|
||||
private Method computeFunctionalInterfaceMethod(Struct clazz) {
|
||||
if (!clazz.clazz.isInterface()) {
|
||||
|
|
|
@ -19,16 +19,21 @@
|
|||
|
||||
package org.elasticsearch.painless;
|
||||
|
||||
import org.elasticsearch.painless.Definition.Method;
|
||||
import org.elasticsearch.painless.Definition.MethodKey;
|
||||
import org.elasticsearch.painless.Definition.Type;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Tracks variables across compilation phases.
|
||||
* Tracks user defined methods and variables across compilation phases.
|
||||
*/
|
||||
public final class Variables {
|
||||
public final class Locals {
|
||||
|
||||
/**
|
||||
* Tracks reserved variables. Must be given to any source of input
|
||||
|
@ -36,7 +41,15 @@ public final class Variables {
|
|||
* are known ahead of time to assign appropriate slots without
|
||||
* being wasteful.
|
||||
*/
|
||||
public static final class Reserved {
|
||||
public interface Reserved {
|
||||
void markReserved(String name);
|
||||
boolean isReserved(String name);
|
||||
|
||||
void setMaxLoopCounter(int max);
|
||||
int getMaxLoopCounter();
|
||||
}
|
||||
|
||||
public static final class ExecuteReserved implements Reserved {
|
||||
public static final String THIS = "#this";
|
||||
public static final String PARAMS = "params";
|
||||
public static final String SCORER = "#scorer";
|
||||
|
@ -46,10 +59,11 @@ public final class Variables {
|
|||
public static final String CTX = "ctx";
|
||||
public static final String LOOP = "#loop";
|
||||
|
||||
boolean score = false;
|
||||
boolean ctx = false;
|
||||
boolean loop = false;
|
||||
private boolean score = false;
|
||||
private boolean ctx = false;
|
||||
private int maxLoopCounter = 0;
|
||||
|
||||
@Override
|
||||
public void markReserved(String name) {
|
||||
if (SCORE.equals(name)) {
|
||||
score = true;
|
||||
|
@ -58,13 +72,53 @@ public final class Variables {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReserved(String name) {
|
||||
return name.equals(THIS) || name.equals(PARAMS) || name.equals(SCORER) || name.equals(DOC) ||
|
||||
name.equals(VALUE) || name.equals(SCORE) || name.equals(CTX) || name.equals(LOOP);
|
||||
}
|
||||
}
|
||||
|
||||
public void usesLoop() {
|
||||
loop = true;
|
||||
public boolean usesScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
public boolean usesCtx() {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxLoopCounter(int max) {
|
||||
maxLoopCounter = max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxLoopCounter() {
|
||||
return maxLoopCounter;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class FunctionReserved implements Reserved {
|
||||
public static final String THIS = "#this";
|
||||
public static final String LOOP = "#loop";
|
||||
|
||||
private int maxLoopCounter = 0;
|
||||
|
||||
public void markReserved(String name) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
public boolean isReserved(String name) {
|
||||
return name.equals(THIS) || name.equals(LOOP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxLoopCounter(int max) {
|
||||
maxLoopCounter = max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxLoopCounter() {
|
||||
return maxLoopCounter;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,52 +140,97 @@ public final class Variables {
|
|||
}
|
||||
}
|
||||
|
||||
final Reserved reserved;
|
||||
public static final class Parameter {
|
||||
public final Location location;
|
||||
public final String name;
|
||||
public final Type type;
|
||||
|
||||
public Parameter(Location location, String name, Type type) {
|
||||
this.location = location;
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
private final Reserved reserved;
|
||||
private final Map<MethodKey, Method> methods;
|
||||
private final Type rtnType;
|
||||
|
||||
// TODO: this datastructure runs in linear time for nearly all operations. use linkedhashset instead?
|
||||
private final Deque<Integer> scopes = new ArrayDeque<>();
|
||||
private final Deque<Variable> variables = new ArrayDeque<>();
|
||||
|
||||
public Variables(Reserved reserved) {
|
||||
public Locals(ExecuteReserved reserved, Map<MethodKey, Method> methods) {
|
||||
this.reserved = reserved;
|
||||
this.methods = Collections.unmodifiableMap(methods);
|
||||
this.rtnType = Definition.OBJECT_TYPE;
|
||||
|
||||
incrementScope();
|
||||
|
||||
// Method variables.
|
||||
|
||||
// This reference. Internal use only.
|
||||
addVariable(null, Definition.getType("Object"), Reserved.THIS, true, true);
|
||||
addVariable(null, Definition.getType("Object"), ExecuteReserved.THIS, true, true);
|
||||
|
||||
// Input map of variables passed to the script.
|
||||
addVariable(null, Definition.getType("Map"), Reserved.PARAMS, true, true);
|
||||
addVariable(null, Definition.getType("Map"), ExecuteReserved.PARAMS, true, true);
|
||||
|
||||
// Scorer parameter passed to the script. Internal use only.
|
||||
addVariable(null, Definition.DEF_TYPE, Reserved.SCORER, true, true);
|
||||
addVariable(null, Definition.DEF_TYPE, ExecuteReserved.SCORER, true, true);
|
||||
|
||||
// Doc parameter passed to the script. TODO: Currently working as a Map, we can do better?
|
||||
addVariable(null, Definition.getType("Map"), Reserved.DOC, true, true);
|
||||
addVariable(null, Definition.getType("Map"), ExecuteReserved.DOC, true, true);
|
||||
|
||||
// Aggregation _value parameter passed to the script.
|
||||
addVariable(null, Definition.DEF_TYPE, Reserved.VALUE, true, true);
|
||||
addVariable(null, Definition.DEF_TYPE, ExecuteReserved.VALUE, true, true);
|
||||
|
||||
// Shortcut variables.
|
||||
|
||||
// Document's score as a read-only double.
|
||||
if (reserved.score) {
|
||||
addVariable(null, Definition.DOUBLE_TYPE, Reserved.SCORE, true, true);
|
||||
if (reserved.usesScore()) {
|
||||
addVariable(null, Definition.DOUBLE_TYPE, ExecuteReserved.SCORE, true, true);
|
||||
}
|
||||
|
||||
// The ctx map set by executable scripts as a read-only map.
|
||||
if (reserved.ctx) {
|
||||
addVariable(null, Definition.getType("Map"), Reserved.CTX, true, true);
|
||||
if (reserved.usesCtx()) {
|
||||
addVariable(null, Definition.getType("Map"), ExecuteReserved.CTX, true, true);
|
||||
}
|
||||
|
||||
// Loop counter to catch infinite loops. Internal use only.
|
||||
if (reserved.loop) {
|
||||
addVariable(null, Definition.INT_TYPE, Reserved.LOOP, true, true);
|
||||
if (reserved.getMaxLoopCounter() > 0) {
|
||||
addVariable(null, Definition.INT_TYPE, ExecuteReserved.LOOP, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
public Locals(FunctionReserved reserved, Locals locals, Type rtnType, List<Parameter> parameters) {
|
||||
this.reserved = reserved;
|
||||
this.methods = locals.methods;
|
||||
this.rtnType = rtnType;
|
||||
|
||||
incrementScope();
|
||||
|
||||
for (Parameter parameter : parameters) {
|
||||
addVariable(parameter.location, parameter.type, parameter.name, false, false);
|
||||
}
|
||||
|
||||
// Loop counter to catch infinite loops. Internal use only.
|
||||
if (reserved.getMaxLoopCounter() > 0) {
|
||||
addVariable(null, Definition.INT_TYPE, ExecuteReserved.LOOP, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
public int getMaxLoopCounter() {
|
||||
return reserved.getMaxLoopCounter();
|
||||
}
|
||||
|
||||
public Method getMethod(MethodKey key) {
|
||||
return methods.get(key);
|
||||
}
|
||||
|
||||
public Type getReturnType() {
|
||||
return rtnType;
|
||||
}
|
||||
|
||||
public void incrementScope() {
|
||||
scopes.push(0);
|
||||
}
|
||||
|
@ -142,9 +241,9 @@ public final class Variables {
|
|||
while (remove > 0) {
|
||||
Variable variable = variables.pop();
|
||||
|
||||
// TODO: is this working? the code reads backwards...
|
||||
// This checks whether or not a variable is used when exiting a local scope.
|
||||
if (variable.read) {
|
||||
throw variable.location.createError(new IllegalArgumentException("Variable [" + variable.name + "] never used."));
|
||||
throw variable.location.createError(new IllegalArgumentException("Variable [" + variable.name + "] is never used."));
|
||||
}
|
||||
|
||||
--remove;
|
||||
|
@ -162,26 +261,30 @@ public final class Variables {
|
|||
}
|
||||
}
|
||||
|
||||
throw location.createError(new IllegalArgumentException("Variable [" + name + "] not defined."));
|
||||
throw location.createError(new IllegalArgumentException("Variable [" + name + "] is not defined."));
|
||||
}
|
||||
|
||||
private boolean variableExists(String name) {
|
||||
return variables.contains(name);
|
||||
public boolean isVariable(String name) {
|
||||
Iterator<Variable> itr = variables.iterator();
|
||||
|
||||
while (itr.hasNext()) {
|
||||
Variable variable = itr.next();
|
||||
|
||||
if (variable.name.equals(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public Variable addVariable(Location location, Type type, String name, boolean readonly, boolean reserved) {
|
||||
if (!reserved && this.reserved.isReserved(name)) {
|
||||
throw location.createError(new IllegalArgumentException("Variable name [" + name + "] is reserved."));
|
||||
throw location.createError(new IllegalArgumentException("Variable [" + name + "] is reserved."));
|
||||
}
|
||||
|
||||
if (variableExists(name)) {
|
||||
throw new IllegalArgumentException("Variable name [" + name + "] already defined.");
|
||||
}
|
||||
|
||||
try {
|
||||
Definition.getType(name);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
// Do nothing.
|
||||
if (isVariable(name)) {
|
||||
throw location.createError(new IllegalArgumentException("Variable [" + name + "] is already defined."));
|
||||
}
|
||||
|
||||
Variable previous = variables.peekFirst();
|
|
@ -91,10 +91,10 @@ public final class MethodWriter extends GeneratorAdapter {
|
|||
private final ClassWriter parent;
|
||||
private final BitSet statements;
|
||||
|
||||
private final Deque<List<org.objectweb.asm.Type>> stringConcatArgs = (INDY_STRING_CONCAT_BOOTSTRAP_HANDLE == null) ?
|
||||
null : new ArrayDeque<>();
|
||||
private final Deque<List<org.objectweb.asm.Type>> stringConcatArgs =
|
||||
(INDY_STRING_CONCAT_BOOTSTRAP_HANDLE == null) ? null : new ArrayDeque<>();
|
||||
|
||||
MethodWriter(int access, Method method, ClassWriter cw, BitSet statements) {
|
||||
public MethodWriter(int access, Method method, ClassWriter cw, BitSet statements) {
|
||||
super(Opcodes.ASM5, cw.visitMethod(access, method.getName(), method.getDescriptor(), null, null),
|
||||
access, method.getName(), method.getDescriptor());
|
||||
|
||||
|
@ -105,7 +105,7 @@ public final class MethodWriter extends GeneratorAdapter {
|
|||
/**
|
||||
* @return A new {@link MethodWriter} with the specified access and signature.
|
||||
*/
|
||||
MethodWriter newMethodWriter(int access, Method method) {
|
||||
public MethodWriter newMethodWriter(int access, Method method) {
|
||||
return new MethodWriter(access, method, parent, statements);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,144 +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;
|
||||
|
||||
import org.elasticsearch.painless.Variables.Reserved;
|
||||
import org.elasticsearch.painless.Variables.Variable;
|
||||
import org.elasticsearch.painless.node.SSource;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.commons.GeneratorAdapter;
|
||||
|
||||
import static org.elasticsearch.painless.WriterConstants.BASE_CLASS_TYPE;
|
||||
import static org.elasticsearch.painless.WriterConstants.CLASS_TYPE;
|
||||
import static org.elasticsearch.painless.WriterConstants.CONSTRUCTOR;
|
||||
import static org.elasticsearch.painless.WriterConstants.EXECUTE;
|
||||
import static org.elasticsearch.painless.WriterConstants.MAP_GET;
|
||||
import static org.elasticsearch.painless.WriterConstants.MAP_TYPE;
|
||||
|
||||
import java.util.BitSet;
|
||||
|
||||
/**
|
||||
* Runs the writing phase of compilation using the Painless AST.
|
||||
*/
|
||||
final class Writer {
|
||||
|
||||
static byte[] write(CompilerSettings settings, String name, String source, Variables variables, SSource root, BitSet expressions) {
|
||||
return new Writer(settings, name, source, variables, root, expressions).getBytes();
|
||||
}
|
||||
|
||||
private final CompilerSettings settings;
|
||||
private final String scriptName;
|
||||
private final String source;
|
||||
private final Variables variables;
|
||||
private final SSource root;
|
||||
|
||||
private final ClassWriter writer;
|
||||
private final MethodWriter adapter;
|
||||
|
||||
private Writer(CompilerSettings settings, String name, String source, Variables variables, SSource root, BitSet expressions) {
|
||||
this.settings = settings;
|
||||
this.scriptName = name;
|
||||
this.source = source;
|
||||
this.variables = variables;
|
||||
this.root = root;
|
||||
|
||||
writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||
|
||||
writeBegin();
|
||||
writeConstructor();
|
||||
|
||||
adapter = new MethodWriter(Opcodes.ACC_PUBLIC, EXECUTE, writer, expressions);
|
||||
|
||||
writeExecute();
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
private void writeBegin() {
|
||||
final int version = Opcodes.V1_8;
|
||||
final int access = Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER | Opcodes.ACC_FINAL;
|
||||
final String base = BASE_CLASS_TYPE.getInternalName();
|
||||
final String name = CLASS_TYPE.getInternalName();
|
||||
|
||||
// apply marker interface NeedsScore if we use the score!
|
||||
final String interfaces[] = variables.reserved.score ?
|
||||
new String[] { WriterConstants.NEEDS_SCORE_TYPE.getInternalName() } : null;
|
||||
|
||||
writer.visit(version, access, name, null, base, interfaces);
|
||||
writer.visitSource(Location.computeSourceName(scriptName,source), null);
|
||||
}
|
||||
|
||||
private void writeConstructor() {
|
||||
final GeneratorAdapter constructor = new GeneratorAdapter(Opcodes.ACC_PUBLIC, CONSTRUCTOR, null, null, writer);
|
||||
constructor.loadThis();
|
||||
constructor.loadArgs();
|
||||
constructor.invokeConstructor(org.objectweb.asm.Type.getType(Executable.class), CONSTRUCTOR);
|
||||
constructor.returnValue();
|
||||
constructor.endMethod();
|
||||
}
|
||||
|
||||
private void writeExecute() {
|
||||
if (variables.reserved.score) {
|
||||
// if the _score value is used, we do this once:
|
||||
// final double _score = scorer.score();
|
||||
final Variable scorer = variables.getVariable(null, Reserved.SCORER);
|
||||
final Variable score = variables.getVariable(null, Reserved.SCORE);
|
||||
|
||||
adapter.visitVarInsn(Opcodes.ALOAD, scorer.slot);
|
||||
adapter.invokeVirtual(WriterConstants.SCORER_TYPE, WriterConstants.SCORER_SCORE);
|
||||
adapter.visitInsn(Opcodes.F2D);
|
||||
adapter.visitVarInsn(Opcodes.DSTORE, score.slot);
|
||||
}
|
||||
|
||||
if (variables.reserved.ctx) {
|
||||
// if the _ctx value is used, we do this once:
|
||||
// final Map<String,Object> ctx = input.get("ctx");
|
||||
|
||||
final Variable input = variables.getVariable(null, Reserved.PARAMS);
|
||||
final Variable ctx = variables.getVariable(null, Reserved.CTX);
|
||||
|
||||
adapter.visitVarInsn(Opcodes.ALOAD, input.slot);
|
||||
adapter.push(Reserved.CTX);
|
||||
adapter.invokeInterface(MAP_TYPE, MAP_GET);
|
||||
adapter.visitVarInsn(Opcodes.ASTORE, ctx.slot);
|
||||
}
|
||||
|
||||
if (variables.reserved.loop) {
|
||||
// if there is infinite loop protection, we do this once:
|
||||
// int #loop = settings.getMaxLoopCounter()
|
||||
|
||||
final Variable loop = variables.getVariable(null, Reserved.LOOP);
|
||||
|
||||
adapter.push(settings.getMaxLoopCounter());
|
||||
adapter.visitVarInsn(Opcodes.ISTORE, loop.slot);
|
||||
}
|
||||
|
||||
root.write(adapter);
|
||||
adapter.endMethod();
|
||||
}
|
||||
|
||||
private void writeEnd() {
|
||||
writer.visitEnd();
|
||||
}
|
||||
|
||||
private byte[] getBytes() {
|
||||
return writer.toByteArray();
|
||||
}
|
||||
}
|
|
@ -107,15 +107,15 @@ public final class WriterConstants {
|
|||
public final static Method DEF_LTE_CALL = getAsmMethod(boolean.class, "lte", Object.class, Object.class);
|
||||
public final static Method DEF_GT_CALL = getAsmMethod(boolean.class, "gt" , Object.class, Object.class);
|
||||
public final static Method DEF_GTE_CALL = getAsmMethod(boolean.class, "gte", Object.class, Object.class);
|
||||
|
||||
|
||||
/** invokedynamic bootstrap for lambda expression/method references */
|
||||
public final static MethodType LAMBDA_BOOTSTRAP_TYPE =
|
||||
MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
|
||||
MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
|
||||
MethodType.class, Object[].class);
|
||||
public final static Handle LAMBDA_BOOTSTRAP_HANDLE =
|
||||
new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(LambdaMetafactory.class),
|
||||
"altMetafactory", LAMBDA_BOOTSTRAP_TYPE.toMethodDescriptorString());
|
||||
|
||||
|
||||
/** dynamic invokedynamic bootstrap for indy string concats (Java 9+) */
|
||||
public final static Handle INDY_STRING_CONCAT_BOOTSTRAP_HANDLE;
|
||||
static {
|
||||
|
@ -152,7 +152,7 @@ public final class WriterConstants {
|
|||
|
||||
public final static Method CHECKEQUALS = getAsmMethod(boolean.class, "checkEquals", Object.class, Object.class);
|
||||
|
||||
public static Method getAsmMethod(final Class<?> rtype, final String name, final Class<?>... ptypes) {
|
||||
private static Method getAsmMethod(final Class<?> rtype, final String name, final Class<?>... ptypes) {
|
||||
return new Method(name, MethodType.methodType(rtype, ptypes).toMethodDescriptorString());
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -18,6 +18,20 @@ class PainlessParserBaseVisitor<T> extends AbstractParseTreeVisitor<T> implement
|
|||
* {@link #visitChildren} on {@code ctx}.</p>
|
||||
*/
|
||||
@Override public T visitSource(PainlessParser.SourceContext ctx) { return visitChildren(ctx); }
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The default implementation returns the result of calling
|
||||
* {@link #visitChildren} on {@code ctx}.</p>
|
||||
*/
|
||||
@Override public T visitFunction(PainlessParser.FunctionContext ctx) { return visitChildren(ctx); }
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The default implementation returns the result of calling
|
||||
* {@link #visitChildren} on {@code ctx}.</p>
|
||||
*/
|
||||
@Override public T visitParameters(PainlessParser.ParametersContext ctx) { return visitChildren(ctx); }
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
|
@ -333,6 +347,13 @@ class PainlessParserBaseVisitor<T> extends AbstractParseTreeVisitor<T> implement
|
|||
* {@link #visitChildren} on {@code ctx}.</p>
|
||||
*/
|
||||
@Override public T visitVariable(PainlessParser.VariableContext ctx) { return visitChildren(ctx); }
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The default implementation returns the result of calling
|
||||
* {@link #visitChildren} on {@code ctx}.</p>
|
||||
*/
|
||||
@Override public T visitCalllocal(PainlessParser.CalllocalContext ctx) { return visitChildren(ctx); }
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
|
|
|
@ -16,6 +16,18 @@ interface PainlessParserVisitor<T> extends ParseTreeVisitor<T> {
|
|||
* @return the visitor result
|
||||
*/
|
||||
T visitSource(PainlessParser.SourceContext ctx);
|
||||
/**
|
||||
* Visit a parse tree produced by {@link PainlessParser#function}.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
T visitFunction(PainlessParser.FunctionContext ctx);
|
||||
/**
|
||||
* Visit a parse tree produced by {@link PainlessParser#parameters}.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
T visitParameters(PainlessParser.ParametersContext ctx);
|
||||
/**
|
||||
* Visit a parse tree produced by the {@code if}
|
||||
* labeled alternative in {@link PainlessParser#statement}.
|
||||
|
@ -320,6 +332,13 @@ interface PainlessParserVisitor<T> extends ParseTreeVisitor<T> {
|
|||
* @return the visitor result
|
||||
*/
|
||||
T visitVariable(PainlessParser.VariableContext ctx);
|
||||
/**
|
||||
* Visit a parse tree produced by the {@code calllocal}
|
||||
* labeled alternative in {@link PainlessParser#primary}.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
T visitCalllocal(PainlessParser.CalllocalContext ctx);
|
||||
/**
|
||||
* Visit a parse tree produced by the {@code newobject}
|
||||
* labeled alternative in {@link PainlessParser#primary}.
|
||||
|
|
|
@ -27,10 +27,13 @@ import org.antlr.v4.runtime.ParserRuleContext;
|
|||
import org.antlr.v4.runtime.RecognitionException;
|
||||
import org.antlr.v4.runtime.Recognizer;
|
||||
import org.antlr.v4.runtime.atn.PredictionMode;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
import org.elasticsearch.painless.CompilerSettings;
|
||||
import org.elasticsearch.painless.Locals.ExecuteReserved;
|
||||
import org.elasticsearch.painless.Locals.FunctionReserved;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Operation;
|
||||
import org.elasticsearch.painless.Variables.Reserved;
|
||||
import org.elasticsearch.painless.Locals.Reserved;
|
||||
import org.elasticsearch.painless.antlr.PainlessParser.AfterthoughtContext;
|
||||
import org.elasticsearch.painless.antlr.PainlessParser.ArgumentContext;
|
||||
import org.elasticsearch.painless.antlr.PainlessParser.ArgumentsContext;
|
||||
|
@ -41,6 +44,7 @@ import org.elasticsearch.painless.antlr.PainlessParser.BoolContext;
|
|||
import org.elasticsearch.painless.antlr.PainlessParser.BraceaccessContext;
|
||||
import org.elasticsearch.painless.antlr.PainlessParser.BreakContext;
|
||||
import org.elasticsearch.painless.antlr.PainlessParser.CallinvokeContext;
|
||||
import org.elasticsearch.painless.antlr.PainlessParser.CalllocalContext;
|
||||
import org.elasticsearch.painless.antlr.PainlessParser.CastContext;
|
||||
import org.elasticsearch.painless.antlr.PainlessParser.ChainprecContext;
|
||||
import org.elasticsearch.painless.antlr.PainlessParser.CompContext;
|
||||
|
@ -62,6 +66,7 @@ import org.elasticsearch.painless.antlr.PainlessParser.FalseContext;
|
|||
import org.elasticsearch.painless.antlr.PainlessParser.FieldaccessContext;
|
||||
import org.elasticsearch.painless.antlr.PainlessParser.ForContext;
|
||||
import org.elasticsearch.painless.antlr.PainlessParser.FuncrefContext;
|
||||
import org.elasticsearch.painless.antlr.PainlessParser.FunctionContext;
|
||||
import org.elasticsearch.painless.antlr.PainlessParser.IfContext;
|
||||
import org.elasticsearch.painless.antlr.PainlessParser.InitializerContext;
|
||||
import org.elasticsearch.painless.antlr.PainlessParser.NewarrayContext;
|
||||
|
@ -69,6 +74,7 @@ import org.elasticsearch.painless.antlr.PainlessParser.NewobjectContext;
|
|||
import org.elasticsearch.painless.antlr.PainlessParser.NullContext;
|
||||
import org.elasticsearch.painless.antlr.PainlessParser.NumericContext;
|
||||
import org.elasticsearch.painless.antlr.PainlessParser.OperatorContext;
|
||||
import org.elasticsearch.painless.antlr.PainlessParser.ParametersContext;
|
||||
import org.elasticsearch.painless.antlr.PainlessParser.PostContext;
|
||||
import org.elasticsearch.painless.antlr.PainlessParser.PreContext;
|
||||
import org.elasticsearch.painless.antlr.PainlessParser.ReadContext;
|
||||
|
@ -104,7 +110,8 @@ import org.elasticsearch.painless.node.ENull;
|
|||
import org.elasticsearch.painless.node.ENumeric;
|
||||
import org.elasticsearch.painless.node.EUnary;
|
||||
import org.elasticsearch.painless.node.LBrace;
|
||||
import org.elasticsearch.painless.node.LCall;
|
||||
import org.elasticsearch.painless.node.LCallInvoke;
|
||||
import org.elasticsearch.painless.node.LCallLocal;
|
||||
import org.elasticsearch.painless.node.LCast;
|
||||
import org.elasticsearch.painless.node.LField;
|
||||
import org.elasticsearch.painless.node.LNewArray;
|
||||
|
@ -122,6 +129,7 @@ import org.elasticsearch.painless.node.SDo;
|
|||
import org.elasticsearch.painless.node.SEach;
|
||||
import org.elasticsearch.painless.node.SExpression;
|
||||
import org.elasticsearch.painless.node.SFor;
|
||||
import org.elasticsearch.painless.node.SFunction;
|
||||
import org.elasticsearch.painless.node.SIf;
|
||||
import org.elasticsearch.painless.node.SIfElse;
|
||||
import org.elasticsearch.painless.node.SReturn;
|
||||
|
@ -130,7 +138,9 @@ import org.elasticsearch.painless.node.SThrow;
|
|||
import org.elasticsearch.painless.node.STry;
|
||||
import org.elasticsearch.painless.node.SWhile;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -138,19 +148,21 @@ import java.util.List;
|
|||
*/
|
||||
public final class Walker extends PainlessParserBaseVisitor<Object> {
|
||||
|
||||
public static SSource buildPainlessTree(String name, String sourceText, Reserved reserved, CompilerSettings settings) {
|
||||
return new Walker(name, sourceText, reserved, settings).source;
|
||||
public static SSource buildPainlessTree(String sourceName, String sourceText, CompilerSettings settings) {
|
||||
return new Walker(sourceName, sourceText, settings).source;
|
||||
}
|
||||
|
||||
private final Reserved reserved;
|
||||
private final SSource source;
|
||||
private final CompilerSettings settings;
|
||||
private final String sourceName;
|
||||
private final String sourceText;
|
||||
|
||||
private Walker(String name, String sourceText, Reserved reserved, CompilerSettings settings) {
|
||||
this.reserved = reserved;
|
||||
private final Deque<Reserved> reserved = new ArrayDeque<>();
|
||||
|
||||
private Walker(String sourceName, String sourceText, CompilerSettings settings) {
|
||||
this.settings = settings;
|
||||
this.sourceName = Location.computeSourceName(name, sourceText);
|
||||
this.sourceName = Location.computeSourceName(sourceName, sourceText);
|
||||
this.sourceText = sourceText;
|
||||
this.source = (SSource)visit(buildAntlrTree(sourceText));
|
||||
}
|
||||
|
||||
|
@ -196,13 +208,51 @@ public final class Walker extends PainlessParserBaseVisitor<Object> {
|
|||
|
||||
@Override
|
||||
public Object visitSource(SourceContext ctx) {
|
||||
reserved.push(new ExecuteReserved());
|
||||
|
||||
List<SFunction> functions = new ArrayList<>();
|
||||
|
||||
for (FunctionContext function : ctx.function()) {
|
||||
functions.add((SFunction)visit(function));
|
||||
}
|
||||
|
||||
List<AStatement> statements = new ArrayList<>();
|
||||
|
||||
for (StatementContext statement : ctx.statement()) {
|
||||
statements.add((AStatement)visit(statement));
|
||||
}
|
||||
|
||||
return new SSource(location(ctx), statements);
|
||||
return new SSource(sourceName, sourceText, (ExecuteReserved)reserved.pop(), location(ctx), functions, statements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitFunction(FunctionContext ctx) {
|
||||
reserved.push(new FunctionReserved());
|
||||
|
||||
String rtnType = ctx.decltype().getText();
|
||||
String name = ctx.ID().getText();
|
||||
List<String> paramTypes = new ArrayList<>();
|
||||
List<String> paramNames = new ArrayList<>();
|
||||
List<AStatement> statements = new ArrayList<>();
|
||||
|
||||
for (DecltypeContext decltype : ctx.parameters().decltype()) {
|
||||
paramTypes.add(decltype.getText());
|
||||
}
|
||||
|
||||
for (TerminalNode id : ctx.parameters().ID()) {
|
||||
paramNames.add(id.getText());
|
||||
}
|
||||
|
||||
for (StatementContext statement : ctx.block().statement()) {
|
||||
statements.add((AStatement)visit(statement));
|
||||
}
|
||||
|
||||
return new SFunction((FunctionReserved)reserved.pop(), location(ctx), rtnType, name, paramTypes, paramNames, statements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitParameters(ParametersContext ctx) {
|
||||
throw location(ctx).createError(new IllegalStateException("Illegal tree structure."));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -221,18 +271,16 @@ public final class Walker extends PainlessParserBaseVisitor<Object> {
|
|||
|
||||
@Override
|
||||
public Object visitWhile(WhileContext ctx) {
|
||||
if (settings.getMaxLoopCounter() > 0) {
|
||||
reserved.usesLoop();
|
||||
}
|
||||
reserved.peek().setMaxLoopCounter(settings.getMaxLoopCounter());
|
||||
|
||||
AExpression expression = (AExpression)visitExpression(ctx.expression());
|
||||
|
||||
if (ctx.trailer() != null) {
|
||||
SBlock block = (SBlock)visit(ctx.trailer());
|
||||
|
||||
return new SWhile(location(ctx), settings.getMaxLoopCounter(), expression, block);
|
||||
return new SWhile(location(ctx), expression, block);
|
||||
} else if (ctx.empty() != null) {
|
||||
return new SWhile(location(ctx), settings.getMaxLoopCounter(), expression, null);
|
||||
return new SWhile(location(ctx), expression, null);
|
||||
} else {
|
||||
throw location(ctx).createError(new IllegalStateException(" Illegal tree structure."));
|
||||
}
|
||||
|
@ -240,21 +288,17 @@ public final class Walker extends PainlessParserBaseVisitor<Object> {
|
|||
|
||||
@Override
|
||||
public Object visitDo(DoContext ctx) {
|
||||
if (settings.getMaxLoopCounter() > 0) {
|
||||
reserved.usesLoop();
|
||||
}
|
||||
reserved.peek().setMaxLoopCounter(settings.getMaxLoopCounter());
|
||||
|
||||
AExpression expression = (AExpression)visitExpression(ctx.expression());
|
||||
SBlock block = (SBlock)visit(ctx.block());
|
||||
|
||||
return new SDo(location(ctx), settings.getMaxLoopCounter(), block, expression);
|
||||
return new SDo(location(ctx), block, expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitFor(ForContext ctx) {
|
||||
if (settings.getMaxLoopCounter() > 0) {
|
||||
reserved.usesLoop();
|
||||
}
|
||||
reserved.peek().setMaxLoopCounter(settings.getMaxLoopCounter());
|
||||
|
||||
ANode initializer = ctx.initializer() == null ? null : (ANode)visit(ctx.initializer());
|
||||
AExpression expression = ctx.expression() == null ? null : (AExpression)visitExpression(ctx.expression());
|
||||
|
@ -263,9 +307,9 @@ public final class Walker extends PainlessParserBaseVisitor<Object> {
|
|||
if (ctx.trailer() != null) {
|
||||
SBlock block = (SBlock)visit(ctx.trailer());
|
||||
|
||||
return new SFor(location(ctx), settings.getMaxLoopCounter(), initializer, expression, afterthought, block);
|
||||
return new SFor(location(ctx), initializer, expression, afterthought, block);
|
||||
} else if (ctx.empty() != null) {
|
||||
return new SFor(location(ctx), settings.getMaxLoopCounter(), initializer, expression, afterthought, null);
|
||||
return new SFor(location(ctx), initializer, expression, afterthought, null);
|
||||
} else {
|
||||
throw location(ctx).createError(new IllegalStateException("Illegal tree structure."));
|
||||
}
|
||||
|
@ -273,16 +317,14 @@ public final class Walker extends PainlessParserBaseVisitor<Object> {
|
|||
|
||||
@Override
|
||||
public Object visitEach(EachContext ctx) {
|
||||
if (settings.getMaxLoopCounter() > 0) {
|
||||
reserved.usesLoop();
|
||||
}
|
||||
reserved.peek().setMaxLoopCounter(settings.getMaxLoopCounter());
|
||||
|
||||
String type = ctx.decltype().getText();
|
||||
String name = ctx.ID().getText();
|
||||
AExpression expression = (AExpression)visitExpression(ctx.expression());
|
||||
SBlock block = (SBlock)visit(ctx.trailer());
|
||||
|
||||
return new SEach(location(ctx), settings.getMaxLoopCounter(), type, name, expression, block);
|
||||
return new SEach(location(ctx), type, name, expression, block);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -791,7 +833,18 @@ public final class Walker extends PainlessParserBaseVisitor<Object> {
|
|||
List<ALink> links = new ArrayList<>();
|
||||
links.add(new LVariable(location(ctx), name));
|
||||
|
||||
reserved.markReserved(name);
|
||||
reserved.peek().markReserved(name);
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitCalllocal(CalllocalContext ctx) {
|
||||
String name = ctx.ID().getText();
|
||||
@SuppressWarnings("unchecked")
|
||||
List<AExpression> arguments = (List<AExpression>)visit(ctx.arguments());
|
||||
List<ALink> links = new ArrayList<>();
|
||||
links.add(new LCallLocal(location(ctx), name, arguments));
|
||||
|
||||
return links;
|
||||
}
|
||||
|
@ -825,7 +878,7 @@ public final class Walker extends PainlessParserBaseVisitor<Object> {
|
|||
@SuppressWarnings("unchecked")
|
||||
List<AExpression> arguments = (List<AExpression>)visit(ctx.arguments());
|
||||
|
||||
return new LCall(location(ctx), name, arguments);
|
||||
return new LCallInvoke(location(ctx), name, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -23,7 +23,7 @@ import org.elasticsearch.painless.Definition.Cast;
|
|||
import org.elasticsearch.painless.Definition.Type;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.AnalyzerCaster;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
|
@ -106,7 +106,7 @@ public abstract class AExpression extends ANode {
|
|||
/**
|
||||
* Checks for errors and collects data for the writing phase.
|
||||
*/
|
||||
abstract void analyze(Variables variables);
|
||||
abstract void analyze(Locals locals);
|
||||
|
||||
/**
|
||||
* Writes ASM based on the data collected during the analysis phase.
|
||||
|
@ -118,7 +118,7 @@ public abstract class AExpression extends ANode {
|
|||
* nodes with the constant variable set to a non-null value with {@link EConstant}.
|
||||
* @return The new child node for the parent node calling this method.
|
||||
*/
|
||||
AExpression cast(Variables variables) {
|
||||
AExpression cast(Locals locals) {
|
||||
final Cast cast = AnalyzerCaster.getLegalCast(location, actual, expected, explicit, internal);
|
||||
|
||||
if (cast == null) {
|
||||
|
@ -136,7 +136,7 @@ public abstract class AExpression extends ANode {
|
|||
// will already be the same.
|
||||
|
||||
EConstant econstant = new EConstant(location, constant);
|
||||
econstant.analyze(variables);
|
||||
econstant.analyze(locals);
|
||||
|
||||
if (!expected.equals(econstant.actual)) {
|
||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||
|
@ -170,7 +170,7 @@ public abstract class AExpression extends ANode {
|
|||
constant = AnalyzerCaster.constCast(location, constant, cast);
|
||||
|
||||
EConstant econstant = new EConstant(location, constant);
|
||||
econstant.analyze(variables);
|
||||
econstant.analyze(locals);
|
||||
|
||||
if (!expected.equals(econstant.actual)) {
|
||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||
|
@ -201,7 +201,7 @@ public abstract class AExpression extends ANode {
|
|||
// the EConstant will already be the same.
|
||||
|
||||
EConstant econstant = new EConstant(location, constant);
|
||||
econstant.analyze(variables);
|
||||
econstant.analyze(locals);
|
||||
|
||||
if (!actual.equals(econstant.actual)) {
|
||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||
|
|
|
@ -21,7 +21,7 @@ package org.elasticsearch.painless.node;
|
|||
|
||||
import org.elasticsearch.painless.Definition.Type;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
/**
|
||||
|
@ -86,7 +86,7 @@ public abstract class ALink extends ANode {
|
|||
* def or a shortcut is used. Otherwise, returns itself. This will be
|
||||
* updated into the {@link EChain} node's list of links.
|
||||
*/
|
||||
abstract ALink analyze(Variables variables);
|
||||
abstract ALink analyze(Locals locals);
|
||||
|
||||
/**
|
||||
* Write values before a load/store occurs such as an array index.
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
package org.elasticsearch.painless.node;
|
||||
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
@ -115,7 +115,7 @@ public abstract class AStatement extends ANode {
|
|||
/**
|
||||
* Checks for errors and collects data for the writing phase.
|
||||
*/
|
||||
abstract void analyze(Variables variables);
|
||||
abstract void analyze(Locals locals);
|
||||
|
||||
/**
|
||||
* Writes ASM based on the data collected during the analysis phase.
|
||||
|
|
|
@ -26,7 +26,7 @@ import org.elasticsearch.painless.Definition.Type;
|
|||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
import org.elasticsearch.painless.Operation;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
|
||||
/**
|
||||
* Represents a binary math expression.
|
||||
|
@ -48,35 +48,35 @@ public final class EBinary extends AExpression {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
void analyze(Locals locals) {
|
||||
if (operation == Operation.MUL) {
|
||||
analyzeMul(variables);
|
||||
analyzeMul(locals);
|
||||
} else if (operation == Operation.DIV) {
|
||||
analyzeDiv(variables);
|
||||
analyzeDiv(locals);
|
||||
} else if (operation == Operation.REM) {
|
||||
analyzeRem(variables);
|
||||
analyzeRem(locals);
|
||||
} else if (operation == Operation.ADD) {
|
||||
analyzeAdd(variables);
|
||||
analyzeAdd(locals);
|
||||
} else if (operation == Operation.SUB) {
|
||||
analyzeSub(variables);
|
||||
analyzeSub(locals);
|
||||
} else if (operation == Operation.LSH) {
|
||||
analyzeLSH(variables);
|
||||
analyzeLSH(locals);
|
||||
} else if (operation == Operation.RSH) {
|
||||
analyzeRSH(variables);
|
||||
analyzeRSH(locals);
|
||||
} else if (operation == Operation.USH) {
|
||||
analyzeUSH(variables);
|
||||
analyzeUSH(locals);
|
||||
} else if (operation == Operation.BWAND) {
|
||||
analyzeBWAnd(variables);
|
||||
analyzeBWAnd(locals);
|
||||
} else if (operation == Operation.XOR) {
|
||||
analyzeXor(variables);
|
||||
analyzeXor(locals);
|
||||
} else if (operation == Operation.BWOR) {
|
||||
analyzeBWOr(variables);
|
||||
analyzeBWOr(locals);
|
||||
} else {
|
||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||
}
|
||||
}
|
||||
|
||||
private void analyzeMul(Variables variables) {
|
||||
private void analyzeMul(Locals variables) {
|
||||
left.analyze(variables);
|
||||
right.analyze(variables);
|
||||
|
||||
|
@ -112,7 +112,7 @@ public final class EBinary extends AExpression {
|
|||
actual = promote;
|
||||
}
|
||||
|
||||
private void analyzeDiv(Variables variables) {
|
||||
private void analyzeDiv(Locals variables) {
|
||||
left.analyze(variables);
|
||||
right.analyze(variables);
|
||||
|
||||
|
@ -152,7 +152,7 @@ public final class EBinary extends AExpression {
|
|||
actual = promote;
|
||||
}
|
||||
|
||||
private void analyzeRem(Variables variables) {
|
||||
private void analyzeRem(Locals variables) {
|
||||
left.analyze(variables);
|
||||
right.analyze(variables);
|
||||
|
||||
|
@ -192,7 +192,7 @@ public final class EBinary extends AExpression {
|
|||
actual = promote;
|
||||
}
|
||||
|
||||
private void analyzeAdd(Variables variables) {
|
||||
private void analyzeAdd(Locals variables) {
|
||||
left.analyze(variables);
|
||||
right.analyze(variables);
|
||||
|
||||
|
@ -244,7 +244,7 @@ public final class EBinary extends AExpression {
|
|||
actual = promote;
|
||||
}
|
||||
|
||||
private void analyzeSub(Variables variables) {
|
||||
private void analyzeSub(Locals variables) {
|
||||
left.analyze(variables);
|
||||
right.analyze(variables);
|
||||
|
||||
|
@ -280,7 +280,7 @@ public final class EBinary extends AExpression {
|
|||
actual = promote;
|
||||
}
|
||||
|
||||
private void analyzeLSH(Variables variables) {
|
||||
private void analyzeLSH(Locals variables) {
|
||||
left.analyze(variables);
|
||||
right.analyze(variables);
|
||||
|
||||
|
@ -313,7 +313,7 @@ public final class EBinary extends AExpression {
|
|||
actual = promote;
|
||||
}
|
||||
|
||||
private void analyzeRSH(Variables variables) {
|
||||
private void analyzeRSH(Locals variables) {
|
||||
left.analyze(variables);
|
||||
right.analyze(variables);
|
||||
|
||||
|
@ -346,7 +346,7 @@ public final class EBinary extends AExpression {
|
|||
actual = promote;
|
||||
}
|
||||
|
||||
private void analyzeUSH(Variables variables) {
|
||||
private void analyzeUSH(Locals variables) {
|
||||
left.analyze(variables);
|
||||
right.analyze(variables);
|
||||
|
||||
|
@ -379,7 +379,7 @@ public final class EBinary extends AExpression {
|
|||
actual = promote;
|
||||
}
|
||||
|
||||
private void analyzeBWAnd(Variables variables) {
|
||||
private void analyzeBWAnd(Locals variables) {
|
||||
left.analyze(variables);
|
||||
right.analyze(variables);
|
||||
|
||||
|
@ -411,7 +411,7 @@ public final class EBinary extends AExpression {
|
|||
actual = promote;
|
||||
}
|
||||
|
||||
private void analyzeXor(Variables variables) {
|
||||
private void analyzeXor(Locals variables) {
|
||||
left.analyze(variables);
|
||||
right.analyze(variables);
|
||||
|
||||
|
@ -445,7 +445,7 @@ public final class EBinary extends AExpression {
|
|||
actual = promote;
|
||||
}
|
||||
|
||||
private void analyzeBWOr(Variables variables) {
|
||||
private void analyzeBWOr(Locals variables) {
|
||||
left.analyze(variables);
|
||||
right.analyze(variables);
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ package org.elasticsearch.painless.node;
|
|||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Operation;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
|
@ -44,14 +44,14 @@ public final class EBool extends AExpression {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
void analyze(Locals locals) {
|
||||
left.expected = Definition.BOOLEAN_TYPE;
|
||||
left.analyze(variables);
|
||||
left = left.cast(variables);
|
||||
left.analyze(locals);
|
||||
left = left.cast(locals);
|
||||
|
||||
right.expected = Definition.BOOLEAN_TYPE;
|
||||
right.analyze(variables);
|
||||
right = right.cast(variables);
|
||||
right.analyze(locals);
|
||||
right = right.cast(locals);
|
||||
|
||||
if (left.constant != null && right.constant != null) {
|
||||
if (operation == Operation.AND) {
|
||||
|
|
|
@ -21,7 +21,7 @@ package org.elasticsearch.painless.node;
|
|||
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
/**
|
||||
|
@ -36,7 +36,7 @@ public final class EBoolean extends AExpression {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
void analyze(Locals locals) {
|
||||
actual = Definition.BOOLEAN_TYPE;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ package org.elasticsearch.painless.node;
|
|||
|
||||
import org.elasticsearch.painless.Definition.Cast;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
/**
|
||||
|
@ -45,7 +45,7 @@ final class ECast extends AExpression {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
void analyze(Locals locals) {
|
||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ import org.elasticsearch.painless.Definition.Type;
|
|||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.AnalyzerCaster;
|
||||
import org.elasticsearch.painless.Operation;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -59,20 +59,20 @@ public final class EChain extends AExpression {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
analyzeLinks(variables);
|
||||
void analyze(Locals locals) {
|
||||
analyzeLinks(locals);
|
||||
analyzeIncrDecr();
|
||||
|
||||
if (operation != null) {
|
||||
analyzeCompound(variables);
|
||||
analyzeCompound(locals);
|
||||
} else if (expression != null) {
|
||||
analyzeWrite(variables);
|
||||
analyzeWrite(locals);
|
||||
} else {
|
||||
analyzeRead();
|
||||
}
|
||||
}
|
||||
|
||||
private void analyzeLinks(Variables variables) {
|
||||
private void analyzeLinks(Locals variables) {
|
||||
ALink previous = null;
|
||||
int index = 0;
|
||||
|
||||
|
@ -153,7 +153,7 @@ public final class EChain extends AExpression {
|
|||
}
|
||||
}
|
||||
|
||||
private void analyzeCompound(Variables variables) {
|
||||
private void analyzeCompound(Locals variables) {
|
||||
ALink last = links.get(links.size() - 1);
|
||||
|
||||
expression.analyze(variables);
|
||||
|
@ -214,7 +214,7 @@ public final class EChain extends AExpression {
|
|||
this.actual = read ? last.after : Definition.VOID_TYPE;
|
||||
}
|
||||
|
||||
private void analyzeWrite(Variables variables) {
|
||||
private void analyzeWrite(Locals variables) {
|
||||
ALink last = links.get(links.size() - 1);
|
||||
|
||||
// If the store node is a def node, we remove the cast to def from the expression
|
||||
|
|
|
@ -25,7 +25,7 @@ import org.elasticsearch.painless.Definition.Type;
|
|||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.AnalyzerCaster;
|
||||
import org.elasticsearch.painless.Operation;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
|
@ -56,29 +56,29 @@ public final class EComp extends AExpression {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
void analyze(Locals locals) {
|
||||
if (operation == Operation.EQ) {
|
||||
analyzeEq(variables);
|
||||
analyzeEq(locals);
|
||||
} else if (operation == Operation.EQR) {
|
||||
analyzeEqR(variables);
|
||||
analyzeEqR(locals);
|
||||
} else if (operation == Operation.NE) {
|
||||
analyzeNE(variables);
|
||||
analyzeNE(locals);
|
||||
} else if (operation == Operation.NER) {
|
||||
analyzeNER(variables);
|
||||
analyzeNER(locals);
|
||||
} else if (operation == Operation.GTE) {
|
||||
analyzeGTE(variables);
|
||||
analyzeGTE(locals);
|
||||
} else if (operation == Operation.GT) {
|
||||
analyzeGT(variables);
|
||||
analyzeGT(locals);
|
||||
} else if (operation == Operation.LTE) {
|
||||
analyzeLTE(variables);
|
||||
analyzeLTE(locals);
|
||||
} else if (operation == Operation.LT) {
|
||||
analyzeLT(variables);
|
||||
analyzeLT(locals);
|
||||
} else {
|
||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||
}
|
||||
}
|
||||
|
||||
private void analyzeEq(Variables variables) {
|
||||
private void analyzeEq(Locals variables) {
|
||||
left.analyze(variables);
|
||||
right.analyze(variables);
|
||||
|
||||
|
@ -124,7 +124,7 @@ public final class EComp extends AExpression {
|
|||
actual = Definition.BOOLEAN_TYPE;
|
||||
}
|
||||
|
||||
private void analyzeEqR(Variables variables) {
|
||||
private void analyzeEqR(Locals variables) {
|
||||
left.analyze(variables);
|
||||
right.analyze(variables);
|
||||
|
||||
|
@ -166,7 +166,7 @@ public final class EComp extends AExpression {
|
|||
actual = Definition.BOOLEAN_TYPE;
|
||||
}
|
||||
|
||||
private void analyzeNE(Variables variables) {
|
||||
private void analyzeNE(Locals variables) {
|
||||
left.analyze(variables);
|
||||
right.analyze(variables);
|
||||
|
||||
|
@ -212,7 +212,7 @@ public final class EComp extends AExpression {
|
|||
actual = Definition.BOOLEAN_TYPE;
|
||||
}
|
||||
|
||||
private void analyzeNER(Variables variables) {
|
||||
private void analyzeNER(Locals variables) {
|
||||
left.analyze(variables);
|
||||
right.analyze(variables);
|
||||
|
||||
|
@ -254,7 +254,7 @@ public final class EComp extends AExpression {
|
|||
actual = Definition.BOOLEAN_TYPE;
|
||||
}
|
||||
|
||||
private void analyzeGTE(Variables variables) {
|
||||
private void analyzeGTE(Locals variables) {
|
||||
left.analyze(variables);
|
||||
right.analyze(variables);
|
||||
|
||||
|
@ -290,7 +290,7 @@ public final class EComp extends AExpression {
|
|||
actual = Definition.BOOLEAN_TYPE;
|
||||
}
|
||||
|
||||
private void analyzeGT(Variables variables) {
|
||||
private void analyzeGT(Locals variables) {
|
||||
left.analyze(variables);
|
||||
right.analyze(variables);
|
||||
|
||||
|
@ -326,7 +326,7 @@ public final class EComp extends AExpression {
|
|||
actual = Definition.BOOLEAN_TYPE;
|
||||
}
|
||||
|
||||
private void analyzeLTE(Variables variables) {
|
||||
private void analyzeLTE(Locals variables) {
|
||||
left.analyze(variables);
|
||||
right.analyze(variables);
|
||||
|
||||
|
@ -362,7 +362,7 @@ public final class EComp extends AExpression {
|
|||
actual = Definition.BOOLEAN_TYPE;
|
||||
}
|
||||
|
||||
private void analyzeLT(Variables variables) {
|
||||
private void analyzeLT(Locals variables) {
|
||||
left.analyze(variables);
|
||||
right.analyze(variables);
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import org.elasticsearch.painless.Definition;
|
|||
import org.elasticsearch.painless.Definition.Type;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.AnalyzerCaster;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
|
@ -45,10 +45,10 @@ public final class EConditional extends AExpression {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
void analyze(Locals locals) {
|
||||
condition.expected = Definition.BOOLEAN_TYPE;
|
||||
condition.analyze(variables);
|
||||
condition = condition.cast(variables);
|
||||
condition.analyze(locals);
|
||||
condition = condition.cast(locals);
|
||||
|
||||
if (condition.constant != null) {
|
||||
throw createError(new IllegalArgumentException("Extraneous conditional statement."));
|
||||
|
@ -62,8 +62,8 @@ public final class EConditional extends AExpression {
|
|||
right.internal = internal;
|
||||
actual = expected;
|
||||
|
||||
left.analyze(variables);
|
||||
right.analyze(variables);
|
||||
left.analyze(locals);
|
||||
right.analyze(locals);
|
||||
|
||||
if (expected == null) {
|
||||
final Type promote = AnalyzerCaster.promoteConditional(left.actual, right.actual, left.constant, right.constant);
|
||||
|
@ -73,8 +73,8 @@ public final class EConditional extends AExpression {
|
|||
actual = promote;
|
||||
}
|
||||
|
||||
left = left.cast(variables);
|
||||
right = right.cast(variables);
|
||||
left = left.cast(locals);
|
||||
right = right.cast(locals);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -22,7 +22,7 @@ package org.elasticsearch.painless.node;
|
|||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Definition.Sort;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
/**
|
||||
|
@ -38,7 +38,7 @@ final class EConstant extends AExpression {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
void analyze(Locals locals) {
|
||||
if (constant instanceof String) {
|
||||
actual = Definition.STRING_TYPE;
|
||||
} else if (constant instanceof Double) {
|
||||
|
|
|
@ -21,7 +21,7 @@ package org.elasticsearch.painless.node;
|
|||
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
/**
|
||||
|
@ -38,7 +38,7 @@ public final class EDecimal extends AExpression {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
void analyze(Locals locals) {
|
||||
if (value.endsWith("f") || value.endsWith("F")) {
|
||||
try {
|
||||
constant = Float.parseFloat(value.substring(0, value.length() - 1));
|
||||
|
|
|
@ -21,7 +21,7 @@ package org.elasticsearch.painless.node;
|
|||
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
/**
|
||||
|
@ -40,7 +40,7 @@ public final class EExplicit extends AExpression {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
void analyze(Locals locals) {
|
||||
try {
|
||||
actual = Definition.getType(this.type);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
|
@ -49,8 +49,8 @@ public final class EExplicit extends AExpression {
|
|||
|
||||
child.expected = actual;
|
||||
child.explicit = true;
|
||||
child.analyze(variables);
|
||||
child = child.cast(variables);
|
||||
child.analyze(locals);
|
||||
child = child.cast(locals);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -58,11 +58,11 @@ public final class EExplicit extends AExpression {
|
|||
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||
}
|
||||
|
||||
AExpression cast(Variables variables) {
|
||||
AExpression cast(Locals locals) {
|
||||
child.expected = expected;
|
||||
child.explicit = explicit;
|
||||
child.internal = internal;
|
||||
|
||||
return child.cast(variables);
|
||||
return child.cast(locals);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ import org.elasticsearch.painless.Definition;
|
|||
import org.elasticsearch.painless.FunctionRef;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import static org.elasticsearch.painless.WriterConstants.LAMBDA_BOOTSTRAP_HANDLE;
|
||||
|
@ -36,7 +36,7 @@ import java.lang.invoke.LambdaMetafactory;
|
|||
public class EFunctionRef extends AExpression {
|
||||
public final String type;
|
||||
public final String call;
|
||||
|
||||
|
||||
private FunctionRef ref;
|
||||
|
||||
public EFunctionRef(Location location, String type, String call) {
|
||||
|
@ -47,7 +47,7 @@ public class EFunctionRef extends AExpression {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
void analyze(Locals locals) {
|
||||
if (expected == null) {
|
||||
ref = null;
|
||||
actual = Definition.getType("String");
|
||||
|
@ -72,22 +72,22 @@ public class EFunctionRef extends AExpression {
|
|||
Type samMethodType = Type.getMethodType(ref.samMethodType.toMethodDescriptorString());
|
||||
Type interfaceType = Type.getMethodType(ref.interfaceMethodType.toMethodDescriptorString());
|
||||
if (ref.needsBridges()) {
|
||||
writer.invokeDynamic(ref.invokedName,
|
||||
invokedType,
|
||||
LAMBDA_BOOTSTRAP_HANDLE,
|
||||
samMethodType,
|
||||
ref.implMethodASM,
|
||||
samMethodType,
|
||||
LambdaMetafactory.FLAG_BRIDGES,
|
||||
1,
|
||||
writer.invokeDynamic(ref.invokedName,
|
||||
invokedType,
|
||||
LAMBDA_BOOTSTRAP_HANDLE,
|
||||
samMethodType,
|
||||
ref.implMethodASM,
|
||||
samMethodType,
|
||||
LambdaMetafactory.FLAG_BRIDGES,
|
||||
1,
|
||||
interfaceType);
|
||||
} else {
|
||||
writer.invokeDynamic(ref.invokedName,
|
||||
invokedType,
|
||||
LAMBDA_BOOTSTRAP_HANDLE,
|
||||
samMethodType,
|
||||
ref.implMethodASM,
|
||||
samMethodType,
|
||||
writer.invokeDynamic(ref.invokedName,
|
||||
invokedType,
|
||||
LAMBDA_BOOTSTRAP_HANDLE,
|
||||
samMethodType,
|
||||
ref.implMethodASM,
|
||||
samMethodType,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ package org.elasticsearch.painless.node;
|
|||
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
|
@ -35,7 +35,7 @@ public final class ENull extends AExpression {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
void analyze(Locals locals) {
|
||||
isNull = true;
|
||||
|
||||
if (expected != null) {
|
||||
|
|
|
@ -22,7 +22,7 @@ package org.elasticsearch.painless.node;
|
|||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Definition.Sort;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
/**
|
||||
|
@ -41,7 +41,7 @@ public final class ENumeric extends AExpression {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
void analyze(Locals locals) {
|
||||
if (value.endsWith("d") || value.endsWith("D")) {
|
||||
if (radix != 10) {
|
||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||
|
|
|
@ -25,7 +25,7 @@ import org.elasticsearch.painless.Definition.Sort;
|
|||
import org.elasticsearch.painless.Definition.Type;
|
||||
import org.elasticsearch.painless.AnalyzerCaster;
|
||||
import org.elasticsearch.painless.Operation;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
|
@ -49,21 +49,21 @@ public final class EUnary extends AExpression {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
void analyze(Locals locals) {
|
||||
if (operation == Operation.NOT) {
|
||||
analyzeNot(variables);
|
||||
analyzeNot(locals);
|
||||
} else if (operation == Operation.BWNOT) {
|
||||
analyzeBWNot(variables);
|
||||
analyzeBWNot(locals);
|
||||
} else if (operation == Operation.ADD) {
|
||||
analyzerAdd(variables);
|
||||
analyzerAdd(locals);
|
||||
} else if (operation == Operation.SUB) {
|
||||
analyzerSub(variables);
|
||||
analyzerSub(locals);
|
||||
} else {
|
||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||
}
|
||||
}
|
||||
|
||||
void analyzeNot(Variables variables) {
|
||||
void analyzeNot(Locals variables) {
|
||||
child.expected = Definition.BOOLEAN_TYPE;
|
||||
child.analyze(variables);
|
||||
child = child.cast(variables);
|
||||
|
@ -75,7 +75,7 @@ public final class EUnary extends AExpression {
|
|||
actual = Definition.BOOLEAN_TYPE;
|
||||
}
|
||||
|
||||
void analyzeBWNot(Variables variables) {
|
||||
void analyzeBWNot(Locals variables) {
|
||||
child.analyze(variables);
|
||||
|
||||
Type promote = AnalyzerCaster.promoteNumeric(child.actual, false);
|
||||
|
@ -102,7 +102,7 @@ public final class EUnary extends AExpression {
|
|||
actual = promote;
|
||||
}
|
||||
|
||||
void analyzerAdd(Variables variables) {
|
||||
void analyzerAdd(Locals variables) {
|
||||
child.analyze(variables);
|
||||
|
||||
Type promote = AnalyzerCaster.promoteNumeric(child.actual, true);
|
||||
|
@ -133,7 +133,7 @@ public final class EUnary extends AExpression {
|
|||
actual = promote;
|
||||
}
|
||||
|
||||
void analyzerSub(Variables variables) {
|
||||
void analyzerSub(Locals variables) {
|
||||
child.analyze(variables);
|
||||
|
||||
Type promote = AnalyzerCaster.promoteNumeric(child.actual, true);
|
||||
|
|
|
@ -21,7 +21,7 @@ package org.elasticsearch.painless.node;
|
|||
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
/**
|
||||
|
@ -38,7 +38,7 @@ public final class LArrayLength extends ALink {
|
|||
}
|
||||
|
||||
@Override
|
||||
ALink analyze(Variables variables) {
|
||||
ALink analyze(Locals locals) {
|
||||
if ("length".equals(value)) {
|
||||
if (!load) {
|
||||
throw createError(new IllegalArgumentException("Must read array field [length]."));
|
||||
|
|
|
@ -22,7 +22,7 @@ package org.elasticsearch.painless.node;
|
|||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Definition.Sort;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -42,7 +42,7 @@ public final class LBrace extends ALink {
|
|||
}
|
||||
|
||||
@Override
|
||||
ALink analyze(Variables variables) {
|
||||
ALink analyze(Locals locals) {
|
||||
if (before == null) {
|
||||
throw createError(new IllegalArgumentException("Illegal array access made without target."));
|
||||
}
|
||||
|
@ -51,18 +51,18 @@ public final class LBrace extends ALink {
|
|||
|
||||
if (sort == Sort.ARRAY) {
|
||||
index.expected = Definition.INT_TYPE;
|
||||
index.analyze(variables);
|
||||
index = index.cast(variables);
|
||||
index.analyze(locals);
|
||||
index = index.cast(locals);
|
||||
|
||||
after = Definition.getType(before.struct, before.dimensions - 1);
|
||||
|
||||
return this;
|
||||
} else if (sort == Sort.DEF) {
|
||||
return new LDefArray(location, index).copy(this).analyze(variables);
|
||||
return new LDefArray(location, index).copy(this).analyze(locals);
|
||||
} else if (Map.class.isAssignableFrom(before.clazz)) {
|
||||
return new LMapShortcut(location, index).copy(this).analyze(variables);
|
||||
return new LMapShortcut(location, index).copy(this).analyze(locals);
|
||||
} else if (List.class.isAssignableFrom(before.clazz)) {
|
||||
return new LListShortcut(location, index).copy(this).analyze(variables);
|
||||
return new LListShortcut(location, index).copy(this).analyze(locals);
|
||||
}
|
||||
|
||||
throw createError(new IllegalArgumentException("Illegal array access on type [" + before.name + "]."));
|
||||
|
|
|
@ -19,12 +19,12 @@
|
|||
|
||||
package org.elasticsearch.painless.node;
|
||||
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Definition.MethodKey;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Definition.Method;
|
||||
import org.elasticsearch.painless.Definition.Sort;
|
||||
import org.elasticsearch.painless.Definition.Struct;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -32,14 +32,14 @@ import java.util.List;
|
|||
/**
|
||||
* Represents a method call or deferes to a def call.
|
||||
*/
|
||||
public final class LCall extends ALink {
|
||||
public final class LCallInvoke extends ALink {
|
||||
|
||||
final String name;
|
||||
final List<AExpression> arguments;
|
||||
|
||||
Method method = null;
|
||||
|
||||
public LCall(Location location, String name, List<AExpression> arguments) {
|
||||
public LCallInvoke(Location location, String name, List<AExpression> arguments) {
|
||||
super(location, -1);
|
||||
|
||||
this.name = name;
|
||||
|
@ -47,7 +47,7 @@ public final class LCall extends ALink {
|
|||
}
|
||||
|
||||
@Override
|
||||
ALink analyze(Variables variables) {
|
||||
ALink analyze(Locals locals) {
|
||||
if (before == null) {
|
||||
throw createError(new IllegalArgumentException("Illegal call [" + name + "] made without target."));
|
||||
} else if (before.sort == Sort.ARRAY) {
|
||||
|
@ -56,7 +56,7 @@ public final class LCall extends ALink {
|
|||
throw createError(new IllegalArgumentException("Cannot assign a value to a call [" + name + "]."));
|
||||
}
|
||||
|
||||
Definition.MethodKey methodKey = new Definition.MethodKey(name, arguments.size());
|
||||
MethodKey methodKey = new MethodKey(name, arguments.size());
|
||||
Struct struct = before.struct;
|
||||
method = statik ? struct.staticMethods.get(methodKey) : struct.methods.get(methodKey);
|
||||
|
||||
|
@ -66,8 +66,8 @@ public final class LCall extends ALink {
|
|||
|
||||
expression.expected = method.arguments.get(argument);
|
||||
expression.internal = true;
|
||||
expression.analyze(variables);
|
||||
arguments.set(argument, expression.cast(variables));
|
||||
expression.analyze(locals);
|
||||
arguments.set(argument, expression.cast(locals));
|
||||
}
|
||||
|
||||
statement = true;
|
||||
|
@ -78,11 +78,11 @@ public final class LCall extends ALink {
|
|||
ALink link = new LDefCall(location, name, arguments);
|
||||
link.copy(this);
|
||||
|
||||
return link.analyze(variables);
|
||||
return link.analyze(locals);
|
||||
}
|
||||
|
||||
throw createError(new IllegalArgumentException("Unknown call [" + name + "] with [" + arguments.size() +
|
||||
"] arguments on type [" + struct.name + "]."));
|
||||
throw createError(new IllegalArgumentException(
|
||||
"Unknown call [" + name + "] with [" + arguments.size() + "] arguments on type [" + struct.name + "]."));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -105,10 +105,6 @@ public final class LCall extends ALink {
|
|||
} else {
|
||||
writer.invokeVirtual(method.owner.type, method.method);
|
||||
}
|
||||
|
||||
if (!method.rtn.clazz.equals(method.handle.type().returnType())) {
|
||||
writer.checkCast(method.rtn.type);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* 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.Definition.Method;
|
||||
import org.elasticsearch.painless.Definition.MethodKey;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.elasticsearch.painless.WriterConstants.CLASS_TYPE;
|
||||
|
||||
/**
|
||||
* Represents a user-defined call.
|
||||
*/
|
||||
public class LCallLocal extends ALink {
|
||||
|
||||
final String name;
|
||||
final List<AExpression> arguments;
|
||||
|
||||
Method method = null;
|
||||
|
||||
public LCallLocal(Location location, String name, List<AExpression> arguments) {
|
||||
super(location, -1);
|
||||
|
||||
this.name = name;
|
||||
this.arguments = arguments;
|
||||
}
|
||||
|
||||
@Override
|
||||
ALink analyze(Locals locals) {
|
||||
if (before != null) {
|
||||
throw createError(new IllegalArgumentException("Illegal call [" + name + "] against an existing target."));
|
||||
} else if (store) {
|
||||
throw createError(new IllegalArgumentException("Cannot assign a value to a call [" + name + "]."));
|
||||
}
|
||||
|
||||
MethodKey methodKey = new MethodKey(name, arguments.size());
|
||||
method = locals.getMethod(methodKey);
|
||||
|
||||
if (method != null) {
|
||||
for (int argument = 0; argument < arguments.size(); ++argument) {
|
||||
AExpression expression = arguments.get(argument);
|
||||
|
||||
expression.expected = method.arguments.get(argument);
|
||||
expression.internal = true;
|
||||
expression.analyze(locals);
|
||||
arguments.set(argument, expression.cast(locals));
|
||||
}
|
||||
|
||||
statement = true;
|
||||
after = method.rtn;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
throw createError(new IllegalArgumentException("Unknown call [" + name + "] with [" + arguments.size() + "] arguments."));
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(MethodWriter writer) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
void load(MethodWriter writer) {
|
||||
writer.writeDebugInfo(location);
|
||||
|
||||
for (AExpression argument : arguments) {
|
||||
argument.write(writer);
|
||||
}
|
||||
|
||||
writer.invokeStatic(CLASS_TYPE, method.method);
|
||||
}
|
||||
|
||||
@Override
|
||||
void store(MethodWriter writer) {
|
||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||
}
|
||||
}
|
|
@ -23,7 +23,7 @@ import org.elasticsearch.painless.Definition;
|
|||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Definition.Cast;
|
||||
import org.elasticsearch.painless.AnalyzerCaster;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
/**
|
||||
|
@ -42,7 +42,7 @@ public final class LCast extends ALink {
|
|||
}
|
||||
|
||||
@Override
|
||||
ALink analyze(Variables variables) {
|
||||
ALink analyze(Locals locals) {
|
||||
if (before == null) {
|
||||
throw createError(new IllegalStateException("Illegal cast without a target."));
|
||||
} else if (store) {
|
||||
|
|
|
@ -22,7 +22,7 @@ package org.elasticsearch.painless.node;
|
|||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.DefBootstrap;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
|
@ -42,10 +42,10 @@ final class LDefArray extends ALink implements IDefLink {
|
|||
}
|
||||
|
||||
@Override
|
||||
ALink analyze(Variables variables) {
|
||||
index.analyze(variables);
|
||||
ALink analyze(Locals locals) {
|
||||
index.analyze(locals);
|
||||
index.expected = index.actual;
|
||||
index = index.cast(variables);
|
||||
index = index.cast(locals);
|
||||
|
||||
after = Definition.DEF_TYPE;
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ package org.elasticsearch.painless.node;
|
|||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.DefBootstrap;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -46,14 +46,14 @@ final class LDefCall extends ALink implements IDefLink {
|
|||
}
|
||||
|
||||
@Override
|
||||
ALink analyze(Variables variables) {
|
||||
ALink analyze(Locals locals) {
|
||||
if (arguments.size() > 63) {
|
||||
// technically, the limitation is just methods with > 63 params, containing method references.
|
||||
// this is because we are lazy and use a long as a bitset. we can always change to a "string" if need be.
|
||||
// but NEED NOT BE. nothing with this many parameters is in the whitelist and we do not support varargs.
|
||||
throw new UnsupportedOperationException("methods with > 63 arguments are currently not supported");
|
||||
}
|
||||
|
||||
|
||||
recipe = 0;
|
||||
for (int argument = 0; argument < arguments.size(); ++argument) {
|
||||
AExpression expression = arguments.get(argument);
|
||||
|
@ -62,9 +62,9 @@ final class LDefCall extends ALink implements IDefLink {
|
|||
recipe |= (1L << argument); // mark argument as deferred reference
|
||||
}
|
||||
expression.internal = true;
|
||||
expression.analyze(variables);
|
||||
expression.analyze(locals);
|
||||
expression.expected = expression.actual;
|
||||
arguments.set(argument, expression.cast(variables));
|
||||
arguments.set(argument, expression.cast(locals));
|
||||
}
|
||||
|
||||
statement = true;
|
||||
|
|
|
@ -22,7 +22,7 @@ package org.elasticsearch.painless.node;
|
|||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.DefBootstrap;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
|
@ -43,7 +43,7 @@ final class LDefField extends ALink implements IDefLink {
|
|||
|
||||
|
||||
@Override
|
||||
ALink analyze(Variables variables) {
|
||||
ALink analyze(Locals locals) {
|
||||
after = Definition.DEF_TYPE;
|
||||
|
||||
return this;
|
||||
|
|
|
@ -24,7 +24,7 @@ import org.elasticsearch.painless.Location;
|
|||
import org.elasticsearch.painless.Definition.Field;
|
||||
import org.elasticsearch.painless.Definition.Sort;
|
||||
import org.elasticsearch.painless.Definition.Struct;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -46,7 +46,7 @@ public final class LField extends ALink {
|
|||
}
|
||||
|
||||
@Override
|
||||
ALink analyze(Variables variables) {
|
||||
ALink analyze(Locals locals) {
|
||||
if (before == null) {
|
||||
throw createError(new IllegalArgumentException("Illegal field [" + value + "] access made without target."));
|
||||
}
|
||||
|
@ -54,9 +54,9 @@ public final class LField extends ALink {
|
|||
Sort sort = before.sort;
|
||||
|
||||
if (sort == Sort.ARRAY) {
|
||||
return new LArrayLength(location, value).copy(this).analyze(variables);
|
||||
return new LArrayLength(location, value).copy(this).analyze(locals);
|
||||
} else if (sort == Sort.DEF) {
|
||||
return new LDefField(location, value).copy(this).analyze(variables);
|
||||
return new LDefField(location, value).copy(this).analyze(locals);
|
||||
}
|
||||
|
||||
Struct struct = before.struct;
|
||||
|
@ -81,17 +81,17 @@ public final class LField extends ALink {
|
|||
Character.toUpperCase(value.charAt(0)) + value.substring(1), 1));
|
||||
|
||||
if (shortcut) {
|
||||
return new LShortcut(location, value).copy(this).analyze(variables);
|
||||
return new LShortcut(location, value).copy(this).analyze(locals);
|
||||
} else {
|
||||
EConstant index = new EConstant(location, value);
|
||||
index.analyze(variables);
|
||||
index.analyze(locals);
|
||||
|
||||
if (Map.class.isAssignableFrom(before.clazz)) {
|
||||
return new LMapShortcut(location, index).copy(this).analyze(variables);
|
||||
return new LMapShortcut(location, index).copy(this).analyze(locals);
|
||||
}
|
||||
|
||||
if (List.class.isAssignableFrom(before.clazz)) {
|
||||
return new LListShortcut(location, index).copy(this).analyze(variables);
|
||||
return new LListShortcut(location, index).copy(this).analyze(locals);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ import org.elasticsearch.painless.Definition;
|
|||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Definition.Method;
|
||||
import org.elasticsearch.painless.Definition.Sort;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
/**
|
||||
|
@ -42,7 +42,7 @@ final class LListShortcut extends ALink {
|
|||
}
|
||||
|
||||
@Override
|
||||
ALink analyze(Variables variables) {
|
||||
ALink analyze(Locals locals) {
|
||||
getter = before.struct.methods.get(new Definition.MethodKey("get", 1));
|
||||
setter = before.struct.methods.get(new Definition.MethodKey("set", 2));
|
||||
|
||||
|
@ -62,8 +62,8 @@ final class LListShortcut extends ALink {
|
|||
|
||||
if ((load || store) && (!load || getter != null) && (!store || setter != null)) {
|
||||
index.expected = Definition.INT_TYPE;
|
||||
index.analyze(variables);
|
||||
index = index.cast(variables);
|
||||
index.analyze(locals);
|
||||
index = index.cast(locals);
|
||||
|
||||
after = setter != null ? setter.arguments.get(1) : getter.rtn;
|
||||
} else {
|
||||
|
|
|
@ -23,7 +23,7 @@ import org.elasticsearch.painless.Definition;
|
|||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Definition.Method;
|
||||
import org.elasticsearch.painless.Definition.Sort;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
/**
|
||||
|
@ -42,7 +42,7 @@ final class LMapShortcut extends ALink {
|
|||
}
|
||||
|
||||
@Override
|
||||
ALink analyze(Variables variables) {
|
||||
ALink analyze(Locals locals) {
|
||||
getter = before.struct.methods.get(new Definition.MethodKey("get", 1));
|
||||
setter = before.struct.methods.get(new Definition.MethodKey("put", 2));
|
||||
|
||||
|
@ -61,8 +61,8 @@ final class LMapShortcut extends ALink {
|
|||
|
||||
if ((load || store) && (!load || getter != null) && (!store || setter != null)) {
|
||||
index.expected = setter != null ? setter.arguments.get(0) : getter.arguments.get(0);
|
||||
index.analyze(variables);
|
||||
index = index.cast(variables);
|
||||
index.analyze(locals);
|
||||
index = index.cast(locals);
|
||||
|
||||
after = setter != null ? setter.arguments.get(1) : getter.rtn;
|
||||
} else {
|
||||
|
|
|
@ -22,7 +22,7 @@ package org.elasticsearch.painless.node;
|
|||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Definition.Type;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -43,7 +43,7 @@ public final class LNewArray extends ALink {
|
|||
}
|
||||
|
||||
@Override
|
||||
ALink analyze(Variables variables) {
|
||||
ALink analyze(Locals locals) {
|
||||
if (before != null) {
|
||||
throw createError(new IllegalArgumentException("Cannot create a new array with a target already defined."));
|
||||
} else if (store) {
|
||||
|
@ -64,8 +64,8 @@ public final class LNewArray extends ALink {
|
|||
AExpression expression = arguments.get(argument);
|
||||
|
||||
expression.expected = Definition.INT_TYPE;
|
||||
expression.analyze(variables);
|
||||
arguments.set(argument, expression.cast(variables));
|
||||
expression.analyze(locals);
|
||||
arguments.set(argument, expression.cast(locals));
|
||||
}
|
||||
|
||||
after = Definition.getType(type.struct, arguments.size());
|
||||
|
|
|
@ -24,7 +24,7 @@ import org.elasticsearch.painless.Location;
|
|||
import org.elasticsearch.painless.Definition.Method;
|
||||
import org.elasticsearch.painless.Definition.Struct;
|
||||
import org.elasticsearch.painless.Definition.Type;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -47,7 +47,7 @@ public final class LNewObj extends ALink {
|
|||
}
|
||||
|
||||
@Override
|
||||
ALink analyze(Variables variables) {
|
||||
ALink analyze(Locals locals) {
|
||||
if (before != null) {
|
||||
throw createError(new IllegalArgumentException("Illegal new call with a target already defined."));
|
||||
} else if (store) {
|
||||
|
@ -79,8 +79,8 @@ public final class LNewObj extends ALink {
|
|||
|
||||
expression.expected = types[argument];
|
||||
expression.internal = true;
|
||||
expression.analyze(variables);
|
||||
arguments.set(argument, expression.cast(variables));
|
||||
expression.analyze(locals);
|
||||
arguments.set(argument, expression.cast(locals));
|
||||
}
|
||||
|
||||
statement = true;
|
||||
|
|
|
@ -24,7 +24,7 @@ import org.elasticsearch.painless.Location;
|
|||
import org.elasticsearch.painless.Definition.Method;
|
||||
import org.elasticsearch.painless.Definition.Sort;
|
||||
import org.elasticsearch.painless.Definition.Struct;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
/**
|
||||
|
@ -44,7 +44,7 @@ final class LShortcut extends ALink {
|
|||
}
|
||||
|
||||
@Override
|
||||
ALink analyze(Variables variables) {
|
||||
ALink analyze(Locals locals) {
|
||||
Struct struct = before.struct;
|
||||
|
||||
getter = struct.methods.get(new Definition.MethodKey("get" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 0));
|
||||
|
|
|
@ -22,7 +22,7 @@ package org.elasticsearch.painless.node;
|
|||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
|
||||
/**
|
||||
* Represents a static type target.
|
||||
|
@ -38,7 +38,7 @@ public final class LStatic extends ALink {
|
|||
}
|
||||
|
||||
@Override
|
||||
ALink analyze(Variables variables) {
|
||||
ALink analyze(Locals locals) {
|
||||
if (before != null) {
|
||||
throw createError(new IllegalArgumentException("Illegal static type [" + type + "] after target already defined."));
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ package org.elasticsearch.painless.node;
|
|||
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
/**
|
||||
|
@ -36,7 +36,7 @@ public final class LString extends ALink {
|
|||
}
|
||||
|
||||
@Override
|
||||
ALink analyze(Variables variables) {
|
||||
ALink analyze(Locals locals) {
|
||||
if (before != null) {
|
||||
throw createError(new IllegalArgumentException("Illegal String constant [" + string + "]."));
|
||||
} else if (store) {
|
||||
|
|
|
@ -21,8 +21,8 @@ package org.elasticsearch.painless.node;
|
|||
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Variables.Variable;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Locals.Variable;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
/**
|
||||
|
@ -41,12 +41,12 @@ public final class LVariable extends ALink {
|
|||
}
|
||||
|
||||
@Override
|
||||
ALink analyze(Variables variables) {
|
||||
ALink analyze(Locals locals) {
|
||||
if (before != null) {
|
||||
throw createError(new IllegalArgumentException("Illegal variable [" + name + "] access with target already defined."));
|
||||
}
|
||||
|
||||
Variable variable = variables.getVariable(location, name);
|
||||
Variable variable = locals.getVariable(location, name);
|
||||
|
||||
if (store && variable.readonly) {
|
||||
throw createError(new IllegalArgumentException("Variable [" + variable.name + "] is read-only."));
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
package org.elasticsearch.painless.node;
|
||||
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
|
@ -40,7 +40,7 @@ public final class SBlock extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
void analyze(Locals locals) {
|
||||
if (statements == null || statements.isEmpty()) {
|
||||
throw createError(new IllegalArgumentException("A block must contain at least one statement."));
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ public final class SBlock extends AStatement {
|
|||
statement.lastSource = lastSource && statement == last;
|
||||
statement.lastLoop = (beginLoop || lastLoop) && statement == last;
|
||||
|
||||
statement.analyze(variables);
|
||||
statement.analyze(locals);
|
||||
|
||||
methodEscape = statement.methodEscape;
|
||||
loopEscape = statement.loopEscape;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
package org.elasticsearch.painless.node;
|
||||
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
|
@ -33,7 +33,7 @@ public final class SBreak extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
void analyze(Locals locals) {
|
||||
if (!inLoop) {
|
||||
throw createError(new IllegalArgumentException("Break statement outside of a loop."));
|
||||
}
|
||||
|
|
|
@ -22,8 +22,8 @@ package org.elasticsearch.painless.node;
|
|||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Definition.Type;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Variables.Variable;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Locals.Variable;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
@ -52,7 +52,7 @@ public final class SCatch extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
void analyze(Locals locals) {
|
||||
final Type type;
|
||||
|
||||
try {
|
||||
|
@ -65,14 +65,14 @@ public final class SCatch extends AStatement {
|
|||
throw createError(new ClassCastException("Not an exception type [" + this.type + "]."));
|
||||
}
|
||||
|
||||
variable = variables.addVariable(location, type, name, true, false);
|
||||
variable = locals.addVariable(location, type, name, true, false);
|
||||
|
||||
if (block != null) {
|
||||
block.lastSource = lastSource;
|
||||
block.inLoop = inLoop;
|
||||
block.lastLoop = lastLoop;
|
||||
|
||||
block.analyze(variables);
|
||||
block.analyze(locals);
|
||||
|
||||
methodEscape = block.methodEscape;
|
||||
loopEscape = block.loopEscape;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
package org.elasticsearch.painless.node;
|
||||
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
|
@ -33,7 +33,7 @@ public final class SContinue extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
void analyze(Locals locals) {
|
||||
if (!inLoop) {
|
||||
throw createError(new IllegalArgumentException("Continue statement outside of a loop."));
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
package org.elasticsearch.painless.node;
|
||||
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
|
@ -40,9 +40,9 @@ public final class SDeclBlock extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
void analyze(Locals locals) {
|
||||
for (SDeclaration declaration : declarations) {
|
||||
declaration.analyze(variables);
|
||||
declaration.analyze(locals);
|
||||
}
|
||||
|
||||
statementCount = declarations.size();
|
||||
|
|
|
@ -22,8 +22,8 @@ package org.elasticsearch.painless.node;
|
|||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Definition.Type;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Variables.Variable;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Locals.Variable;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
|
@ -47,7 +47,7 @@ public final class SDeclaration extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
void analyze(Locals locals) {
|
||||
final Type type;
|
||||
|
||||
try {
|
||||
|
@ -58,11 +58,11 @@ public final class SDeclaration extends AStatement {
|
|||
|
||||
if (expression != null) {
|
||||
expression.expected = type;
|
||||
expression.analyze(variables);
|
||||
expression = expression.cast(variables);
|
||||
expression.analyze(locals);
|
||||
expression = expression.cast(locals);
|
||||
}
|
||||
|
||||
variable = variables.addVariable(location, type, name, false, false);
|
||||
variable = locals.addVariable(location, type, name, false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -21,7 +21,7 @@ package org.elasticsearch.painless.node;
|
|||
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
|
@ -30,21 +30,19 @@ import org.elasticsearch.painless.MethodWriter;
|
|||
*/
|
||||
public final class SDo extends AStatement {
|
||||
|
||||
final int maxLoopCounter;
|
||||
final SBlock block;
|
||||
AExpression condition;
|
||||
|
||||
public SDo(Location location, int maxLoopCounter, SBlock block, AExpression condition) {
|
||||
public SDo(Location location, SBlock block, AExpression condition) {
|
||||
super(location);
|
||||
|
||||
this.condition = condition;
|
||||
this.block = block;
|
||||
this.maxLoopCounter = maxLoopCounter;
|
||||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
variables.incrementScope();
|
||||
void analyze(Locals locals) {
|
||||
locals.incrementScope();
|
||||
|
||||
if (block == null) {
|
||||
throw createError(new IllegalArgumentException("Extraneous do while loop."));
|
||||
|
@ -53,15 +51,15 @@ public final class SDo extends AStatement {
|
|||
block.beginLoop = true;
|
||||
block.inLoop = true;
|
||||
|
||||
block.analyze(variables);
|
||||
block.analyze(locals);
|
||||
|
||||
if (block.loopEscape && !block.anyContinue) {
|
||||
throw createError(new IllegalArgumentException("Extraneous do while loop."));
|
||||
}
|
||||
|
||||
condition.expected = Definition.BOOLEAN_TYPE;
|
||||
condition.analyze(variables);
|
||||
condition = condition.cast(variables);
|
||||
condition.analyze(locals);
|
||||
condition = condition.cast(locals);
|
||||
|
||||
if (condition.constant != null) {
|
||||
final boolean continuous = (boolean)condition.constant;
|
||||
|
@ -78,11 +76,11 @@ public final class SDo extends AStatement {
|
|||
|
||||
statementCount = 1;
|
||||
|
||||
if (maxLoopCounter > 0) {
|
||||
loopCounterSlot = variables.getVariable(location, "#loop").slot;
|
||||
if (locals.getMaxLoopCounter() > 0) {
|
||||
loopCounterSlot = locals.getVariable(location, "#loop").slot;
|
||||
}
|
||||
|
||||
variables.decrementScope();
|
||||
locals.decrementScope();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -29,8 +29,8 @@ import org.elasticsearch.painless.Definition.Sort;
|
|||
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.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Locals.Variable;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
|
@ -44,7 +44,6 @@ import static org.elasticsearch.painless.WriterConstants.ITERATOR_TYPE;
|
|||
*/
|
||||
public class SEach extends AStatement {
|
||||
|
||||
final int maxLoopCounter;
|
||||
final String type;
|
||||
final String name;
|
||||
AExpression expression;
|
||||
|
@ -63,10 +62,9 @@ public class SEach extends AStatement {
|
|||
Variable iterator = null;
|
||||
Method method = null;
|
||||
|
||||
public SEach(Location location, int maxLoopCounter, String type, String name, AExpression expression, SBlock block) {
|
||||
public SEach(Location location, String type, String name, AExpression expression, SBlock block) {
|
||||
super(location);
|
||||
|
||||
this.maxLoopCounter = maxLoopCounter;
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.expression = expression;
|
||||
|
@ -74,10 +72,10 @@ public class SEach extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
expression.analyze(variables);
|
||||
void analyze(Locals locals) {
|
||||
expression.analyze(locals);
|
||||
expression.expected = expression.actual;
|
||||
expression = expression.cast(variables);
|
||||
expression = expression.cast(locals);
|
||||
|
||||
final Type type;
|
||||
|
||||
|
@ -87,25 +85,25 @@ public class SEach extends AStatement {
|
|||
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
||||
}
|
||||
|
||||
variables.incrementScope();
|
||||
locals.incrementScope();
|
||||
|
||||
variable = variables.addVariable(location, type, name, true, false);
|
||||
variable = locals.addVariable(location, type, name, true, false);
|
||||
|
||||
if (expression.actual.sort == Sort.ARRAY) {
|
||||
analyzeArray(variables, type);
|
||||
analyzeArray(locals, type);
|
||||
} else if (expression.actual.sort == Sort.DEF || Iterable.class.isAssignableFrom(expression.actual.clazz)) {
|
||||
analyzeIterable(variables, type);
|
||||
analyzeIterable(locals, type);
|
||||
} else {
|
||||
throw location.createError(new IllegalArgumentException("Illegal for each type [" + expression.actual.name + "]."));
|
||||
throw createError(new IllegalArgumentException("Illegal for each type [" + expression.actual.name + "]."));
|
||||
}
|
||||
|
||||
if (block == null) {
|
||||
throw location.createError(new IllegalArgumentException("Extraneous for each loop."));
|
||||
throw createError(new IllegalArgumentException("Extraneous for each loop."));
|
||||
}
|
||||
|
||||
block.beginLoop = true;
|
||||
block.inLoop = true;
|
||||
block.analyze(variables);
|
||||
block.analyze(locals);
|
||||
block.statementCount = Math.max(1, block.statementCount);
|
||||
|
||||
if (block.loopEscape && !block.anyContinue) {
|
||||
|
@ -114,14 +112,14 @@ public class SEach extends AStatement {
|
|||
|
||||
statementCount = 1;
|
||||
|
||||
if (maxLoopCounter > 0) {
|
||||
loopCounterSlot = variables.getVariable(location, "#loop").slot;
|
||||
if (locals.getMaxLoopCounter() > 0) {
|
||||
loopCounterSlot = locals.getVariable(location, "#loop").slot;
|
||||
}
|
||||
|
||||
variables.decrementScope();
|
||||
locals.decrementScope();
|
||||
}
|
||||
|
||||
void analyzeArray(Variables variables, Type type) {
|
||||
void analyzeArray(Locals variables, Type type) {
|
||||
// We must store the array and index as variables for securing slots on the stack, and
|
||||
// also add the location offset to make the names unique in case of nested for each loops.
|
||||
array = variables.addVariable(location, expression.actual, "#array" + location.getOffset(), true, false);
|
||||
|
@ -130,7 +128,7 @@ public class SEach extends AStatement {
|
|||
cast = AnalyzerCaster.getLegalCast(location, indexed, type, true, true);
|
||||
}
|
||||
|
||||
void analyzeIterable(Variables variables, Type type) {
|
||||
void analyzeIterable(Locals variables, Type type) {
|
||||
// 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, Definition.getType("Iterator"), "#itr" + location.getOffset(), true, false);
|
||||
|
@ -141,7 +139,7 @@ public class SEach extends AStatement {
|
|||
method = expression.actual.struct.methods.get(new MethodKey("iterator", 0));
|
||||
|
||||
if (method == null) {
|
||||
throw location.createError(new IllegalArgumentException(
|
||||
throw createError(new IllegalArgumentException(
|
||||
"Unable to create iterator for the type [" + expression.actual.name + "]."));
|
||||
}
|
||||
}
|
||||
|
@ -158,7 +156,7 @@ public class SEach extends AStatement {
|
|||
} else if (iterator != null) {
|
||||
writeIterable(writer);
|
||||
} else {
|
||||
throw location.createError(new IllegalStateException("Illegal tree structure."));
|
||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,7 +195,7 @@ public class SEach extends AStatement {
|
|||
if (method == null) {
|
||||
Type itr = Definition.getType("Iterator");
|
||||
String desc = org.objectweb.asm.Type.getMethodDescriptor(itr.type, Definition.DEF_TYPE.type);
|
||||
writer.invokeDynamic("iterator", desc, DEF_BOOTSTRAP_HANDLE, (Object)DefBootstrap.ITERATOR, 0);
|
||||
writer.invokeDynamic("iterator", desc, DEF_BOOTSTRAP_HANDLE, (Object)DefBootstrap.ITERATOR, (Object)0);
|
||||
} else if (java.lang.reflect.Modifier.isInterface(method.owner.clazz.getModifiers())) {
|
||||
writer.invokeInterface(method.owner.type, method.method);
|
||||
} else {
|
||||
|
|
|
@ -20,9 +20,10 @@
|
|||
package org.elasticsearch.painless.node;
|
||||
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Definition.Type;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Definition.Sort;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
/**
|
||||
|
@ -39,19 +40,22 @@ public final class SExpression extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
expression.read = lastSource;
|
||||
expression.analyze(variables);
|
||||
void analyze(Locals locals) {
|
||||
Type rtnType = locals.getReturnType();
|
||||
boolean isVoid = rtnType.sort == Sort.VOID;
|
||||
|
||||
expression.read = lastSource && !isVoid;
|
||||
expression.analyze(locals);
|
||||
|
||||
if (!lastSource && !expression.statement) {
|
||||
throw createError(new IllegalArgumentException("Not a statement."));
|
||||
}
|
||||
|
||||
final boolean rtn = lastSource && expression.actual.sort != Sort.VOID;
|
||||
boolean rtn = lastSource && !isVoid && expression.actual.sort != Sort.VOID;
|
||||
|
||||
expression.expected = rtn ? Definition.OBJECT_TYPE : expression.actual;
|
||||
expression.expected = rtn ? rtnType : expression.actual;
|
||||
expression.internal = rtn;
|
||||
expression = expression.cast(variables);
|
||||
expression = expression.cast(locals);
|
||||
|
||||
methodEscape = rtn;
|
||||
loopEscape = rtn;
|
||||
|
|
|
@ -21,7 +21,7 @@ package org.elasticsearch.painless.node;
|
|||
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
|
@ -30,17 +30,14 @@ import org.elasticsearch.painless.MethodWriter;
|
|||
*/
|
||||
public final class SFor extends AStatement {
|
||||
|
||||
final int maxLoopCounter;
|
||||
ANode initializer;
|
||||
AExpression condition;
|
||||
AExpression afterthought;
|
||||
final SBlock block;
|
||||
|
||||
public SFor(Location location, int maxLoopCounter,
|
||||
ANode initializer, AExpression condition, AExpression afterthought, SBlock block) {
|
||||
public SFor(Location location, ANode initializer, AExpression condition, AExpression afterthought, SBlock block) {
|
||||
super(location);
|
||||
|
||||
this.maxLoopCounter = maxLoopCounter;
|
||||
this.initializer = initializer;
|
||||
this.condition = condition;
|
||||
this.afterthought = afterthought;
|
||||
|
@ -48,19 +45,19 @@ public final class SFor extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
variables.incrementScope();
|
||||
void analyze(Locals locals) {
|
||||
locals.incrementScope();
|
||||
|
||||
boolean continuous = false;
|
||||
|
||||
if (initializer != null) {
|
||||
if (initializer instanceof AStatement) {
|
||||
((AStatement)initializer).analyze(variables);
|
||||
((AStatement)initializer).analyze(locals);
|
||||
} else if (initializer instanceof AExpression) {
|
||||
AExpression initializer = (AExpression)this.initializer;
|
||||
|
||||
initializer.read = false;
|
||||
initializer.analyze(variables);
|
||||
initializer.analyze(locals);
|
||||
|
||||
if (!initializer.statement) {
|
||||
throw createError(new IllegalArgumentException("Not a statement."));
|
||||
|
@ -72,8 +69,8 @@ public final class SFor extends AStatement {
|
|||
|
||||
if (condition != null) {
|
||||
condition.expected = Definition.BOOLEAN_TYPE;
|
||||
condition.analyze(variables);
|
||||
condition = condition.cast(variables);
|
||||
condition.analyze(locals);
|
||||
condition = condition.cast(locals);
|
||||
|
||||
if (condition.constant != null) {
|
||||
continuous = (boolean)condition.constant;
|
||||
|
@ -92,7 +89,7 @@ public final class SFor extends AStatement {
|
|||
|
||||
if (afterthought != null) {
|
||||
afterthought.read = false;
|
||||
afterthought.analyze(variables);
|
||||
afterthought.analyze(locals);
|
||||
|
||||
if (!afterthought.statement) {
|
||||
throw createError(new IllegalArgumentException("Not a statement."));
|
||||
|
@ -103,7 +100,7 @@ public final class SFor extends AStatement {
|
|||
block.beginLoop = true;
|
||||
block.inLoop = true;
|
||||
|
||||
block.analyze(variables);
|
||||
block.analyze(locals);
|
||||
|
||||
if (block.loopEscape && !block.anyContinue) {
|
||||
throw createError(new IllegalArgumentException("Extraneous for loop."));
|
||||
|
@ -119,11 +116,11 @@ public final class SFor extends AStatement {
|
|||
|
||||
statementCount = 1;
|
||||
|
||||
if (maxLoopCounter > 0) {
|
||||
loopCounterSlot = variables.getVariable(location, "#loop").slot;
|
||||
if (locals.getMaxLoopCounter() > 0) {
|
||||
loopCounterSlot = locals.getVariable(location, "#loop").slot;
|
||||
}
|
||||
|
||||
variables.decrementScope();
|
||||
locals.decrementScope();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* 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.Definition;
|
||||
import org.elasticsearch.painless.Definition.Method;
|
||||
import org.elasticsearch.painless.Definition.Sort;
|
||||
import org.elasticsearch.painless.Definition.Type;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Locals.Parameter;
|
||||
import org.elasticsearch.painless.Locals.FunctionReserved;
|
||||
import org.elasticsearch.painless.Locals.Variable;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a user-defined function.
|
||||
*/
|
||||
public class SFunction extends AStatement {
|
||||
final FunctionReserved reserved;
|
||||
final String rtnTypeStr;
|
||||
final String name;
|
||||
final List<String> paramTypeStrs;
|
||||
final List<String> paramNameStrs;
|
||||
final List<AStatement> statements;
|
||||
|
||||
Type rtnType = null;
|
||||
List<Parameter> parameters = new ArrayList<>();
|
||||
Method method = null;
|
||||
|
||||
Locals locals = null;
|
||||
|
||||
public SFunction(FunctionReserved reserved, Location location,
|
||||
String rtnType, String name, List<String> paramTypes, List<String> paramNames, List<AStatement> statements) {
|
||||
super(location);
|
||||
|
||||
this.reserved = reserved;
|
||||
this.rtnTypeStr = rtnType;
|
||||
this.name = name;
|
||||
this.paramTypeStrs = Collections.unmodifiableList(paramTypes);
|
||||
this.paramNameStrs = Collections.unmodifiableList(paramNames);
|
||||
this.statements = Collections.unmodifiableList(statements);
|
||||
}
|
||||
|
||||
void generate() {
|
||||
try {
|
||||
rtnType = Definition.getType(rtnTypeStr);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw createError(new IllegalArgumentException("Illegal return type [" + rtnTypeStr + "] for function [" + name + "]."));
|
||||
}
|
||||
|
||||
if (paramTypeStrs.size() != paramNameStrs.size()) {
|
||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||
}
|
||||
|
||||
Class<?>[] paramClasses = new Class<?>[this.paramTypeStrs.size()];
|
||||
List<Type> paramTypes = new ArrayList<>();
|
||||
|
||||
for (int param = 0; param < this.paramTypeStrs.size(); ++param) {
|
||||
try {
|
||||
Type paramType = Definition.getType(this.paramTypeStrs.get(param));
|
||||
|
||||
paramClasses[param] = paramType.clazz;
|
||||
paramTypes.add(paramType);
|
||||
parameters.add(new Parameter(location, paramNameStrs.get(param), paramType));
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw createError(new IllegalArgumentException(
|
||||
"Illegal parameter type [" + this.paramTypeStrs.get(param) + "] for function [" + name + "]."));
|
||||
}
|
||||
}
|
||||
|
||||
org.objectweb.asm.commons.Method method =
|
||||
new org.objectweb.asm.commons.Method(name, MethodType.methodType(rtnType.clazz, paramClasses).toMethodDescriptorString());
|
||||
this.method = new Method(name, null, rtnType, paramTypes, method, 0, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
void analyze(Locals locals) {
|
||||
if (statements == null || statements.isEmpty()) {
|
||||
throw createError(new IllegalArgumentException("Cannot generate an empty function [" + name + "]."));
|
||||
}
|
||||
|
||||
this.locals = new Locals(reserved, locals, rtnType, parameters);
|
||||
locals = this.locals;
|
||||
|
||||
locals.incrementScope();
|
||||
|
||||
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 = statement == last;
|
||||
|
||||
statement.analyze(locals);
|
||||
|
||||
methodEscape = statement.methodEscape;
|
||||
allEscape = statement.allEscape;
|
||||
}
|
||||
|
||||
if (!methodEscape && rtnType.sort != Sort.VOID) {
|
||||
throw createError(new IllegalArgumentException("Not all paths provide a return value for method [" + name + "]."));
|
||||
}
|
||||
|
||||
locals.decrementScope();
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(MethodWriter writer) {
|
||||
MethodWriter function = writer.newMethodWriter(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, method.method);
|
||||
|
||||
if (reserved.getMaxLoopCounter() > 0) {
|
||||
// if there is infinite loop protection, we do this once:
|
||||
// int #loop = settings.getMaxLoopCounter()
|
||||
|
||||
Variable loop = locals.getVariable(null, FunctionReserved.LOOP);
|
||||
|
||||
function.push(reserved.getMaxLoopCounter());
|
||||
function.visitVarInsn(Opcodes.ISTORE, loop.slot);
|
||||
}
|
||||
|
||||
for (AStatement statement : statements) {
|
||||
statement.write(function);
|
||||
}
|
||||
|
||||
if (!methodEscape) {
|
||||
if (rtnType.sort == Sort.VOID) {
|
||||
function.returnValue();
|
||||
} else {
|
||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||
}
|
||||
}
|
||||
|
||||
function.endMethod();
|
||||
}
|
||||
}
|
|
@ -21,7 +21,7 @@ package org.elasticsearch.painless.node;
|
|||
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
|
@ -41,10 +41,10 @@ public final class SIf extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
void analyze(Locals locals) {
|
||||
condition.expected = Definition.BOOLEAN_TYPE;
|
||||
condition.analyze(variables);
|
||||
condition = condition.cast(variables);
|
||||
condition.analyze(locals);
|
||||
condition = condition.cast(locals);
|
||||
|
||||
if (condition.constant != null) {
|
||||
throw createError(new IllegalArgumentException("Extraneous if statement."));
|
||||
|
@ -58,9 +58,9 @@ public final class SIf extends AStatement {
|
|||
ifblock.inLoop = inLoop;
|
||||
ifblock.lastLoop = lastLoop;
|
||||
|
||||
variables.incrementScope();
|
||||
ifblock.analyze(variables);
|
||||
variables.decrementScope();
|
||||
locals.incrementScope();
|
||||
ifblock.analyze(locals);
|
||||
locals.decrementScope();
|
||||
|
||||
anyContinue = ifblock.anyContinue;
|
||||
anyBreak = ifblock.anyBreak;
|
||||
|
|
|
@ -21,7 +21,7 @@ package org.elasticsearch.painless.node;
|
|||
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
|
@ -43,10 +43,10 @@ public final class SIfElse extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
void analyze(Locals locals) {
|
||||
condition.expected = Definition.BOOLEAN_TYPE;
|
||||
condition.analyze(variables);
|
||||
condition = condition.cast(variables);
|
||||
condition.analyze(locals);
|
||||
condition = condition.cast(locals);
|
||||
|
||||
if (condition.constant != null) {
|
||||
throw createError(new IllegalArgumentException("Extraneous if statement."));
|
||||
|
@ -60,9 +60,9 @@ public final class SIfElse extends AStatement {
|
|||
ifblock.inLoop = inLoop;
|
||||
ifblock.lastLoop = lastLoop;
|
||||
|
||||
variables.incrementScope();
|
||||
ifblock.analyze(variables);
|
||||
variables.decrementScope();
|
||||
locals.incrementScope();
|
||||
ifblock.analyze(locals);
|
||||
locals.decrementScope();
|
||||
|
||||
anyContinue = ifblock.anyContinue;
|
||||
anyBreak = ifblock.anyBreak;
|
||||
|
@ -76,9 +76,9 @@ public final class SIfElse extends AStatement {
|
|||
elseblock.inLoop = inLoop;
|
||||
elseblock.lastLoop = lastLoop;
|
||||
|
||||
variables.incrementScope();
|
||||
elseblock.analyze(variables);
|
||||
variables.decrementScope();
|
||||
locals.incrementScope();
|
||||
elseblock.analyze(locals);
|
||||
locals.decrementScope();
|
||||
|
||||
methodEscape = ifblock.methodEscape && elseblock.methodEscape;
|
||||
loopEscape = ifblock.loopEscape && elseblock.loopEscape;
|
||||
|
|
|
@ -19,9 +19,8 @@
|
|||
|
||||
package org.elasticsearch.painless.node;
|
||||
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
/**
|
||||
|
@ -38,11 +37,11 @@ public final class SReturn extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
expression.expected = Definition.OBJECT_TYPE;
|
||||
void analyze(Locals locals) {
|
||||
expression.expected = locals.getReturnType();
|
||||
expression.internal = true;
|
||||
expression.analyze(variables);
|
||||
expression = expression.cast(variables);
|
||||
expression.analyze(locals);
|
||||
expression = expression.cast(locals);
|
||||
|
||||
methodEscape = true;
|
||||
loopEscape = true;
|
||||
|
|
|
@ -19,34 +19,85 @@
|
|||
|
||||
package org.elasticsearch.painless.node;
|
||||
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Definition.Method;
|
||||
import org.elasticsearch.painless.Definition.MethodKey;
|
||||
import org.elasticsearch.painless.Executable;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Locals.ExecuteReserved;
|
||||
import org.elasticsearch.painless.Locals.Variable;
|
||||
import org.elasticsearch.painless.WriterConstants;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
import java.util.BitSet;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.painless.WriterConstants.BASE_CLASS_TYPE;
|
||||
import static org.elasticsearch.painless.WriterConstants.CLASS_TYPE;
|
||||
import static org.elasticsearch.painless.WriterConstants.CONSTRUCTOR;
|
||||
import static org.elasticsearch.painless.WriterConstants.EXECUTE;
|
||||
import static org.elasticsearch.painless.WriterConstants.MAP_GET;
|
||||
import static org.elasticsearch.painless.WriterConstants.MAP_TYPE;
|
||||
|
||||
/**
|
||||
* The root of all Painless trees. Contains a series of statements.
|
||||
*/
|
||||
public final class SSource extends AStatement {
|
||||
|
||||
final String name;
|
||||
final String source;
|
||||
final ExecuteReserved reserved;
|
||||
final List<SFunction> functions;
|
||||
final List<AStatement> statements;
|
||||
|
||||
public SSource(Location location, List<AStatement> statements) {
|
||||
private Locals locals;
|
||||
private BitSet expressions;
|
||||
private byte[] bytes;
|
||||
|
||||
public SSource(String name, String source, ExecuteReserved reserved, Location location,
|
||||
List<SFunction> functions, List<AStatement> statements) {
|
||||
super(location);
|
||||
|
||||
this.name = name;
|
||||
this.source = source;
|
||||
this.reserved = reserved;
|
||||
this.functions = Collections.unmodifiableList(functions);
|
||||
this.statements = Collections.unmodifiableList(statements);
|
||||
}
|
||||
|
||||
public void analyze() {
|
||||
Map<MethodKey, Method> methods = new HashMap<>();
|
||||
|
||||
for (SFunction function : functions) {
|
||||
function.generate();
|
||||
|
||||
MethodKey key = new MethodKey(function.name, function.parameters.size());
|
||||
|
||||
if (methods.put(key, function.method) != null) {
|
||||
throw createError(new IllegalArgumentException("Duplicate functions with name [" + function.name + "]."));
|
||||
}
|
||||
}
|
||||
|
||||
locals = new Locals(reserved, methods);
|
||||
analyze(locals);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void analyze(Variables variables) {
|
||||
void analyze(Locals locals) {
|
||||
for (SFunction function : functions) {
|
||||
function.analyze(locals);
|
||||
}
|
||||
|
||||
if (statements == null || statements.isEmpty()) {
|
||||
throw createError(new IllegalArgumentException("Cannot generate an empty script."));
|
||||
}
|
||||
|
||||
variables.incrementScope();
|
||||
locals.incrementScope();
|
||||
|
||||
AStatement last = statements.get(statements.size() - 1);
|
||||
|
||||
|
@ -59,17 +110,95 @@ public final class SSource extends AStatement {
|
|||
|
||||
statement.lastSource = statement == last;
|
||||
|
||||
statement.analyze(variables);
|
||||
statement.analyze(locals);
|
||||
|
||||
methodEscape = statement.methodEscape;
|
||||
allEscape = statement.allEscape;
|
||||
}
|
||||
|
||||
variables.decrementScope();
|
||||
locals.decrementScope();
|
||||
}
|
||||
|
||||
public void write() {
|
||||
// Create the ClassWriter.
|
||||
|
||||
int classFrames = ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS;
|
||||
int classVersion = Opcodes.V1_8;
|
||||
int classAccess = Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER | Opcodes.ACC_FINAL;
|
||||
String classBase = BASE_CLASS_TYPE.getInternalName();
|
||||
String className = CLASS_TYPE.getInternalName();
|
||||
String classInterfaces[] = reserved.usesScore() ? new String[] { WriterConstants.NEEDS_SCORE_TYPE.getInternalName() } : null;
|
||||
|
||||
ClassWriter writer = new ClassWriter(classFrames);
|
||||
writer.visit(classVersion, classAccess, className, null, classBase, classInterfaces);
|
||||
writer.visitSource(Location.computeSourceName(name, source), null);
|
||||
|
||||
// Create the execute MethodWriter.
|
||||
|
||||
expressions = new BitSet(source.length());
|
||||
MethodWriter execute = new MethodWriter(Opcodes.ACC_PUBLIC, EXECUTE, writer, expressions);
|
||||
|
||||
// Write the constructor.
|
||||
|
||||
MethodWriter constructor = execute.newMethodWriter(Opcodes.ACC_PUBLIC, CONSTRUCTOR);
|
||||
constructor.loadThis();
|
||||
constructor.loadArgs();
|
||||
constructor.invokeConstructor(org.objectweb.asm.Type.getType(Executable.class), CONSTRUCTOR);
|
||||
constructor.returnValue();
|
||||
constructor.endMethod();
|
||||
|
||||
// Write the execute method.
|
||||
|
||||
write(execute);
|
||||
execute.endMethod();
|
||||
|
||||
// End writing the class and store the generated bytes.
|
||||
|
||||
writer.visitEnd();
|
||||
bytes = writer.toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(MethodWriter writer) {
|
||||
void write(MethodWriter writer) {
|
||||
for (SFunction function : functions) {
|
||||
function.write(writer);
|
||||
}
|
||||
|
||||
if (reserved.usesScore()) {
|
||||
// if the _score value is used, we do this once:
|
||||
// final double _score = scorer.score();
|
||||
Variable scorer = locals.getVariable(null, ExecuteReserved.SCORER);
|
||||
Variable score = locals.getVariable(null, ExecuteReserved.SCORE);
|
||||
|
||||
writer.visitVarInsn(Opcodes.ALOAD, scorer.slot);
|
||||
writer.invokeVirtual(WriterConstants.SCORER_TYPE, WriterConstants.SCORER_SCORE);
|
||||
writer.visitInsn(Opcodes.F2D);
|
||||
writer.visitVarInsn(Opcodes.DSTORE, score.slot);
|
||||
}
|
||||
|
||||
if (reserved.usesCtx()) {
|
||||
// if the _ctx value is used, we do this once:
|
||||
// final Map<String,Object> ctx = input.get("ctx");
|
||||
|
||||
Variable input = locals.getVariable(null, ExecuteReserved.PARAMS);
|
||||
Variable ctx = locals.getVariable(null, ExecuteReserved.CTX);
|
||||
|
||||
writer.visitVarInsn(Opcodes.ALOAD, input.slot);
|
||||
writer.push(ExecuteReserved.CTX);
|
||||
writer.invokeInterface(MAP_TYPE, MAP_GET);
|
||||
writer.visitVarInsn(Opcodes.ASTORE, ctx.slot);
|
||||
}
|
||||
|
||||
if (reserved.getMaxLoopCounter() > 0) {
|
||||
// if there is infinite loop protection, we do this once:
|
||||
// int #loop = settings.getMaxLoopCounter()
|
||||
|
||||
Variable loop = locals.getVariable(null, ExecuteReserved.LOOP);
|
||||
|
||||
writer.push(reserved.getMaxLoopCounter());
|
||||
writer.visitVarInsn(Opcodes.ISTORE, loop.slot);
|
||||
}
|
||||
|
||||
for (AStatement statement : statements) {
|
||||
statement.write(writer);
|
||||
}
|
||||
|
@ -79,4 +208,12 @@ public final class SSource extends AStatement {
|
|||
writer.returnValue();
|
||||
}
|
||||
}
|
||||
|
||||
public BitSet getExpressions() {
|
||||
return expressions;
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ package org.elasticsearch.painless.node;
|
|||
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
/**
|
||||
|
@ -38,10 +38,10 @@ public final class SThrow extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
void analyze(Locals locals) {
|
||||
expression.expected = Definition.EXCEPTION_TYPE;
|
||||
expression.analyze(variables);
|
||||
expression = expression.cast(variables);
|
||||
expression.analyze(locals);
|
||||
expression = expression.cast(locals);
|
||||
|
||||
methodEscape = true;
|
||||
loopEscape = true;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
package org.elasticsearch.painless.node;
|
||||
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
@ -43,7 +43,7 @@ public final class STry extends AStatement {
|
|||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
void analyze(Locals locals) {
|
||||
if (block == null) {
|
||||
throw createError(new IllegalArgumentException("Extraneous try statement."));
|
||||
}
|
||||
|
@ -52,9 +52,9 @@ public final class STry extends AStatement {
|
|||
block.inLoop = inLoop;
|
||||
block.lastLoop = lastLoop;
|
||||
|
||||
variables.incrementScope();
|
||||
block.analyze(variables);
|
||||
variables.decrementScope();
|
||||
locals.incrementScope();
|
||||
block.analyze(locals);
|
||||
locals.decrementScope();
|
||||
|
||||
methodEscape = block.methodEscape;
|
||||
loopEscape = block.loopEscape;
|
||||
|
@ -69,9 +69,9 @@ public final class STry extends AStatement {
|
|||
catc.inLoop = inLoop;
|
||||
catc.lastLoop = lastLoop;
|
||||
|
||||
variables.incrementScope();
|
||||
catc.analyze(variables);
|
||||
variables.decrementScope();
|
||||
locals.incrementScope();
|
||||
catc.analyze(locals);
|
||||
locals.decrementScope();
|
||||
|
||||
methodEscape &= catc.methodEscape;
|
||||
loopEscape &= catc.loopEscape;
|
||||
|
|
|
@ -21,7 +21,7 @@ package org.elasticsearch.painless.node;
|
|||
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Variables;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
||||
|
@ -30,25 +30,23 @@ import org.elasticsearch.painless.MethodWriter;
|
|||
*/
|
||||
public final class SWhile extends AStatement {
|
||||
|
||||
final int maxLoopCounter;
|
||||
AExpression condition;
|
||||
final SBlock block;
|
||||
|
||||
public SWhile(Location location, int maxLoopCounter, AExpression condition, SBlock block) {
|
||||
public SWhile(Location location, AExpression condition, SBlock block) {
|
||||
super(location);
|
||||
|
||||
this.maxLoopCounter = maxLoopCounter;
|
||||
this.condition = condition;
|
||||
this.block = block;
|
||||
}
|
||||
|
||||
@Override
|
||||
void analyze(Variables variables) {
|
||||
variables.incrementScope();
|
||||
void analyze(Locals locals) {
|
||||
locals.incrementScope();
|
||||
|
||||
condition.expected = Definition.BOOLEAN_TYPE;
|
||||
condition.analyze(variables);
|
||||
condition = condition.cast(variables);
|
||||
condition.analyze(locals);
|
||||
condition = condition.cast(locals);
|
||||
|
||||
boolean continuous = false;
|
||||
|
||||
|
@ -68,7 +66,7 @@ public final class SWhile extends AStatement {
|
|||
block.beginLoop = true;
|
||||
block.inLoop = true;
|
||||
|
||||
block.analyze(variables);
|
||||
block.analyze(locals);
|
||||
|
||||
if (block.loopEscape && !block.anyContinue) {
|
||||
throw createError(new IllegalArgumentException("Extraneous while loop."));
|
||||
|
@ -84,11 +82,11 @@ public final class SWhile extends AStatement {
|
|||
|
||||
statementCount = 1;
|
||||
|
||||
if (maxLoopCounter > 0) {
|
||||
loopCounterSlot = variables.getVariable(location, "#loop").slot;
|
||||
if (locals.getMaxLoopCounter() > 0) {
|
||||
loopCounterSlot = locals.getVariable(location, "#loop").slot;
|
||||
}
|
||||
|
||||
variables.decrementScope();
|
||||
locals.decrementScope();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -49,7 +49,8 @@
|
|||
* {@link org.elasticsearch.painless.node.IDefLink} - A marker interface for all LDef* (link) nodes.
|
||||
* {@link org.elasticsearch.painless.node.LArrayLength} - Represents an array length field load.
|
||||
* {@link org.elasticsearch.painless.node.LBrace} - Represents an array load/store or defers to possible shortcuts.
|
||||
* {@link org.elasticsearch.painless.node.LCall} - Represents a method call or defers to a def call.
|
||||
* {@link org.elasticsearch.painless.node.LCallInvoke} - Represents a method call or defers to a def call.
|
||||
* {@link org.elasticsearch.painless.node.LCallLocal} - Represents a user-defined call.
|
||||
* {@link org.elasticsearch.painless.node.LCast} - Represents a cast made in a variable/method chain.
|
||||
* {@link org.elasticsearch.painless.node.LDefArray} - Represents an array load/store or shortcut on a def type. (Internal only.)
|
||||
* {@link org.elasticsearch.painless.node.LDefCall} - Represents a method call made on a def type. (Internal only.)
|
||||
|
@ -73,6 +74,7 @@
|
|||
* {@link org.elasticsearch.painless.node.SEach} - Represents a for each loop shortcut for iterables.
|
||||
* {@link org.elasticsearch.painless.node.SExpression} - Represents the top-level node for an expression as a statement.
|
||||
* {@link org.elasticsearch.painless.node.SFor} - Represents a for loop.
|
||||
* {@link org.elasticsearch.painless.node.SFunction} - Represents a user-defined function.
|
||||
* {@link org.elasticsearch.painless.node.SIf} - Represents an if block.
|
||||
* {@link org.elasticsearch.painless.node.SIfElse} - Represents an if/else block.
|
||||
* {@link org.elasticsearch.painless.node.SReturn} - Represents a return statement.
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
public class FunctionTests extends ScriptTestCase {
|
||||
public void testBasic() {
|
||||
assertEquals(5, exec("int get() {5;} get()"));
|
||||
}
|
||||
|
||||
public void testReference() {
|
||||
assertEquals(5, exec("void get(int[] x) {x[0] = 5;} int[] y = new int[1]; y[0] = 1; get(y); y[0]"));
|
||||
}
|
||||
|
||||
public void testConcat() {
|
||||
assertEquals("xyxy", exec("String catcat(String single) {single + single;} catcat('xy')"));
|
||||
}
|
||||
|
||||
public void testMultiArgs() {
|
||||
assertEquals(5, exec("int add(int x, int y) {return x + y;} int x = 1, y = 2; add(add(x, x), add(x, y))"));
|
||||
}
|
||||
|
||||
public void testMultiFuncs() {
|
||||
assertEquals(1, exec("int add(int x, int y) {return x + y;} int sub(int x, int y) {return x - y;} add(2, sub(3, 4))"));
|
||||
assertEquals(3, exec("int sub2(int x, int y) {sub(x, y) - y;} int sub(int x, int y) {return x - y;} sub2(5, 1)"));
|
||||
}
|
||||
|
||||
public void testRecursion() {
|
||||
assertEquals(55, exec("int fib(int n) {if (n <= 1) return n; else return fib(n-1) + fib(n-2);} fib(10)"));
|
||||
}
|
||||
|
||||
public void testEmpty() {
|
||||
Exception expected = expectScriptThrows(IllegalArgumentException.class, () -> {
|
||||
exec("void test(int x) {} test()");
|
||||
});
|
||||
assertTrue(expected.getMessage().contains("Cannot generate an empty function"));
|
||||
}
|
||||
|
||||
public void testDuplicates() {
|
||||
Exception expected = expectScriptThrows(IllegalArgumentException.class, () -> {
|
||||
exec("void test(int x) {x = 2;} void test(def y) {y = 3;} test()");
|
||||
});
|
||||
assertTrue(expected.getMessage().contains("Duplicate functions"));
|
||||
}
|
||||
|
||||
public void testInfiniteLoop() {
|
||||
Error expected = expectScriptThrows(PainlessError.class, () -> {
|
||||
exec("void test() {boolean x = true; while (x) {}} test()");
|
||||
});
|
||||
assertTrue(expected.getMessage().contains(
|
||||
"The maximum number of statements that can be executed in a loop has been reached."));
|
||||
}
|
||||
}
|
|
@ -30,7 +30,7 @@ public class ReservedWordTests extends ScriptTestCase {
|
|||
IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> {
|
||||
exec("int _score = 5; return _score;");
|
||||
});
|
||||
assertTrue(expected.getMessage().contains("Variable name [_score] is reserved"));
|
||||
assertTrue(expected.getMessage().contains("Variable [_score] is reserved"));
|
||||
}
|
||||
|
||||
/** check that we can't write to _score, its read-only! */
|
||||
|
@ -46,7 +46,7 @@ public class ReservedWordTests extends ScriptTestCase {
|
|||
IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> {
|
||||
exec("int doc = 5; return doc;");
|
||||
});
|
||||
assertTrue(expected.getMessage().contains("Variable name [doc] is reserved"));
|
||||
assertTrue(expected.getMessage().contains("Variable [doc] is reserved"));
|
||||
}
|
||||
|
||||
/** check that we can't write to doc, its read-only! */
|
||||
|
@ -62,7 +62,7 @@ public class ReservedWordTests extends ScriptTestCase {
|
|||
IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> {
|
||||
exec("int ctx = 5; return ctx;");
|
||||
});
|
||||
assertTrue(expected.getMessage().contains("Variable name [ctx] is reserved"));
|
||||
assertTrue(expected.getMessage().contains("Variable [ctx] is reserved"));
|
||||
}
|
||||
|
||||
/** check that we can't write to ctx, its read-only! */
|
||||
|
@ -83,7 +83,7 @@ public class ReservedWordTests extends ScriptTestCase {
|
|||
IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> {
|
||||
exec("int _value = 5; return _value;");
|
||||
});
|
||||
assertTrue(expected.getMessage().contains("Variable name [_value] is reserved"));
|
||||
assertTrue(expected.getMessage().contains("Variable [_value] is reserved"));
|
||||
}
|
||||
|
||||
/** check that we can't write to _value, its read-only! */
|
||||
|
|
Loading…
Reference in New Issue