Modify Painless AST to add synthetic functions during semantic pass (#47611)

This has ELambda and ENewArrayFunctionRef add their generated synthetic
methods to the SClass node during the semantic pass and removes this 
data from the write pass. This is the first step to remove "Globals" (mutable 
state) from the write pass.
This commit is contained in:
Jack Conradson 2019-10-07 07:47:56 -07:00
parent 176ca13c57
commit 833ed30f0d
34 changed files with 45 additions and 67 deletions

View File

@ -19,8 +19,6 @@
package org.elasticsearch.painless;
import org.elasticsearch.painless.node.SFunction;
import java.util.BitSet;
import java.util.HashMap;
import java.util.Map;
@ -29,7 +27,6 @@ import java.util.Map;
* Program-wide globals (initializers, synthetic methods, etc)
*/
public class Globals {
private final Map<String,SFunction> syntheticMethods = new HashMap<>();
private final Map<String,Constant> constantInitializers = new HashMap<>();
private final Map<String,Class<?>> classBindings = new HashMap<>();
private final Map<Object,String> instanceBindings = new HashMap<>();
@ -40,16 +37,6 @@ public class Globals {
this.statements = statements;
}
/** Adds a new synthetic method to be written. It must be analyzed! */
public void addSyntheticMethod(SFunction function) {
if (!function.synthetic) {
throw new IllegalStateException("method: " + function.name + " is not synthetic");
}
if (syntheticMethods.put(function.name, function) != null) {
throw new IllegalStateException("synthetic method: " + function.name + " already exists");
}
}
/** Adds a new constant initializer to be written */
public void addConstantInitializer(Constant constant) {
if (constantInitializers.put(constant.name, constant) != null) {
@ -69,11 +56,6 @@ public class Globals {
public String addInstanceBinding(Object instance) {
return instanceBindings.computeIfAbsent(instance, key -> "$instance_binding$" + instanceBindings.size());
}
/** Returns the current synthetic methods */
public Map<String,SFunction> getSyntheticMethods() {
return syntheticMethods;
}
/** Returns the current initializers */
public Map<String,Constant> getConstantInitializers() {

View File

@ -22,9 +22,9 @@ package org.elasticsearch.painless.node;
import org.elasticsearch.painless.AnalyzerCaster;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessCast;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import org.elasticsearch.painless.ScriptRoot;
import java.util.Objects;

View File

@ -29,9 +29,9 @@ import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.Operation;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessCast;
import org.elasticsearch.painless.lookup.def;
import org.elasticsearch.painless.ScriptRoot;
import java.util.ArrayList;
import java.util.List;

View File

@ -28,10 +28,10 @@ import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.Operation;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.WriterConstants;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import org.elasticsearch.painless.lookup.def;
import org.elasticsearch.painless.ScriptRoot;
import java.util.Objects;
import java.util.Set;

View File

@ -25,10 +25,10 @@ import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessClassBinding;
import org.elasticsearch.painless.lookup.PainlessInstanceBinding;
import org.elasticsearch.painless.lookup.PainlessMethod;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.symbol.FunctionTable;
import org.objectweb.asm.Label;
import org.objectweb.asm.Type;

View File

@ -28,9 +28,9 @@ import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Locals.Variable;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import org.elasticsearch.painless.lookup.def;
import org.elasticsearch.painless.ScriptRoot;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

View File

@ -25,9 +25,9 @@ import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessCast;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import org.elasticsearch.painless.ScriptRoot;
import java.util.Objects;
import java.util.Set;

View File

@ -28,9 +28,9 @@ import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.Operation;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import org.elasticsearch.painless.lookup.def;
import org.elasticsearch.painless.ScriptRoot;
import org.objectweb.asm.Label;
import org.objectweb.asm.Type;

View File

@ -25,8 +25,8 @@ import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import java.util.Objects;
import java.util.Set;

View File

@ -27,10 +27,10 @@ import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Locals.Variable;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import org.elasticsearch.painless.lookup.PainlessMethod;
import org.elasticsearch.painless.lookup.def;
import org.elasticsearch.painless.ScriptRoot;
import org.objectweb.asm.Opcodes;
import java.util.ArrayList;
@ -185,6 +185,7 @@ public final class ELambda extends AExpression implements ILambda {
desugared.analyze(scriptRoot, Locals.newLambdaScope(locals.getProgramScope(), desugared.name, returnType,
desugared.parameters, captures.size(), settings.getMaxLoopCounter()));
scriptRoot.getFunctionTable().addFunction(desugared.name, desugared.returnType, desugared.typeParameters, true);
scriptRoot.getClassNode().addFunction(desugared);
// setup method reference to synthetic method
if (expected == null) {
@ -219,9 +220,6 @@ public final class ELambda extends AExpression implements ILambda {
methodWriter.visitVarInsn(MethodWriter.getType(capture.clazz).getOpcode(Opcodes.ILOAD), capture.getSlot());
}
}
// add synthetic method to the queue to be written
globals.addSyntheticMethod(desugared);
}
@Override

View File

@ -25,10 +25,10 @@ import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessConstructor;
import org.elasticsearch.painless.lookup.PainlessMethod;
import org.elasticsearch.painless.lookup.def;
import org.elasticsearch.painless.ScriptRoot;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.Method;

View File

@ -25,10 +25,10 @@ import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessConstructor;
import org.elasticsearch.painless.lookup.PainlessMethod;
import org.elasticsearch.painless.lookup.def;
import org.elasticsearch.painless.ScriptRoot;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.Method;

View File

@ -75,6 +75,7 @@ public final class ENewArrayFunctionRef extends AExpression implements ILambda {
function.analyze(scriptRoot, Locals.newLambdaScope(locals.getProgramScope(), function.name, function.returnType,
function.parameters, 0, settings.getMaxLoopCounter()));
scriptRoot.getFunctionTable().addFunction(function.name, function.returnType, function.typeParameters, true);
scriptRoot.getClassNode().addFunction(function);
if (expected == null) {
ref = null;
@ -97,8 +98,6 @@ public final class ENewArrayFunctionRef extends AExpression implements ILambda {
// push a null instruction as a placeholder for future lambda instructions
methodWriter.push((String)null);
}
globals.addSyntheticMethod(function);
}
@Override

View File

@ -25,9 +25,9 @@ import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessConstructor;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import org.elasticsearch.painless.ScriptRoot;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.Method;

View File

@ -25,8 +25,8 @@ import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import org.objectweb.asm.Opcodes;
import java.util.Set;

View File

@ -26,8 +26,8 @@ import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.WriterConstants;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.WriterConstants;
import java.util.Set;
import java.util.regex.Pattern;

View File

@ -28,9 +28,9 @@ import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.Operation;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import org.elasticsearch.painless.lookup.def;
import org.elasticsearch.painless.ScriptRoot;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

View File

@ -25,9 +25,9 @@ import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import org.elasticsearch.painless.lookup.def;
import org.elasticsearch.painless.ScriptRoot;
import java.util.List;
import java.util.Map;

View File

@ -25,9 +25,9 @@ import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessMethod;
import org.elasticsearch.painless.lookup.def;
import org.elasticsearch.painless.ScriptRoot;
import java.util.List;
import java.util.Objects;

View File

@ -25,11 +25,11 @@ import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessField;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import org.elasticsearch.painless.lookup.PainlessMethod;
import org.elasticsearch.painless.lookup.def;
import org.elasticsearch.painless.ScriptRoot;
import java.util.List;
import java.util.Map;

View File

@ -25,8 +25,8 @@ import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.lookup.PainlessMethod;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessMethod;
import java.util.List;
import java.util.Objects;

View File

@ -26,8 +26,8 @@ import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.lookup.def;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.def;
import org.objectweb.asm.Type;
import java.time.ZonedDateTime;

View File

@ -26,8 +26,8 @@ import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.lookup.def;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.def;
import org.objectweb.asm.Type;
import java.time.ZonedDateTime;

View File

@ -26,8 +26,8 @@ import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.lookup.def;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.def;
import java.time.ZonedDateTime;
import java.util.Objects;

View File

@ -25,9 +25,9 @@ import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessField;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import org.elasticsearch.painless.ScriptRoot;
import org.objectweb.asm.Type;
import java.lang.reflect.Modifier;

View File

@ -25,10 +25,10 @@ import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.WriterConstants;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import org.elasticsearch.painless.lookup.PainlessMethod;
import org.elasticsearch.painless.ScriptRoot;
import java.util.Objects;
import java.util.Set;

View File

@ -25,9 +25,9 @@ import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import org.elasticsearch.painless.lookup.PainlessMethod;
import org.elasticsearch.painless.ScriptRoot;
import java.util.Objects;
import java.util.Set;

View File

@ -25,8 +25,8 @@ import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.lookup.PainlessMethod;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessMethod;
import java.util.Set;

View File

@ -28,9 +28,9 @@ import org.elasticsearch.painless.Locals.Variable;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.ScriptClassInfo;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.WriterConstants;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.symbol.FunctionTable;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Label;
@ -82,7 +82,7 @@ public final class SClass extends AStatement {
private final ScriptClassInfo scriptClassInfo;
private final String name;
private final Printer debugStream;
private final List<SFunction> functions;
private final List<SFunction> functions = new ArrayList<>();
private final Globals globals;
private final List<AStatement> statements;
@ -100,7 +100,7 @@ public final class SClass extends AStatement {
this.scriptClassInfo = Objects.requireNonNull(scriptClassInfo);
this.name = Objects.requireNonNull(name);
this.debugStream = debugStream;
this.functions = Collections.unmodifiableList(functions);
this.functions.addAll(Objects.requireNonNull(functions));
this.statements = Collections.unmodifiableList(statements);
this.globals = new Globals(new BitSet(sourceText.length()));
@ -108,6 +108,10 @@ public final class SClass extends AStatement {
this.getMethods = new ArrayList<>();
}
void addFunction(SFunction function) {
functions.add(function);
}
@Override
public void storeSettings(CompilerSettings settings) {
for (SFunction function : functions) {
@ -155,7 +159,11 @@ public final class SClass extends AStatement {
@Override
void analyze(ScriptRoot scriptRoot, Locals program) {
for (SFunction function : this.functions) {
// copy protection is required because synthetic functions are
// added for lambdas/method references and analysis here is
// only for user-defined functions
List<SFunction> functions = new ArrayList<>(this.functions);
for (SFunction function : functions) {
Locals functionLocals =
Locals.newFunctionScope(program, function.returnType, function.parameters, settings.getMaxLoopCounter());
function.analyze(scriptRoot, functionLocals);
@ -281,15 +289,6 @@ public final class SClass extends AStatement {
function.write(classWriter, globals);
}
// Write all synthetic functions. Note that this process may add more :)
while (!globals.getSyntheticMethods().isEmpty()) {
List<SFunction> current = new ArrayList<>(globals.getSyntheticMethods().values());
globals.getSyntheticMethods().clear();
for (SFunction function : current) {
function.write(classWriter, globals);
}
}
// Write the constants
if (false == globals.getConstantInitializers().isEmpty()) {
Collection<Constant> inits = globals.getConstantInitializers().values();

View File

@ -26,9 +26,9 @@ import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Locals.Variable;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import org.elasticsearch.painless.lookup.def;
import org.elasticsearch.painless.ScriptRoot;
import java.util.Objects;
import java.util.Set;

View File

@ -27,9 +27,9 @@ import org.elasticsearch.painless.Locals.Parameter;
import org.elasticsearch.painless.Locals.Variable;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import org.elasticsearch.painless.ScriptRoot;
import org.objectweb.asm.Opcodes;
import java.lang.invoke.MethodType;

View File

@ -25,8 +25,8 @@ import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import java.util.Set;

View File

@ -27,9 +27,9 @@ import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Locals.Variable;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessCast;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import org.elasticsearch.painless.ScriptRoot;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;

View File

@ -28,11 +28,11 @@ import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Locals.Variable;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessCast;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import org.elasticsearch.painless.lookup.PainlessMethod;
import org.elasticsearch.painless.lookup.def;
import org.elasticsearch.painless.ScriptRoot;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;