Skip to content

Commit 026474d

Browse files
authored
Move specialized execute method code into an external phase (#51954)
This change moves almost all of the customized code required to decorate the execute method into an external phase. This external phase operates only on the ir tree after the user tree has generated the initial ir tree from user specified code. The external phase uses ir nodes to create the necessary customized code instead of writing asm directly. This is a first example of modifying the ir tree to customize it specifically for Elasticsearch and leaves the ir nodes in a more generic state. Another change required for this was to remove the notion of auto-return from the ir tree completely. The user tree is now responsible for generating appropriate ir tree nodes to support auto-return. This is the first example of divergent user and ir trees as the user tree is intended to be higher level while the ir tree is supposed to provide lower level translation into asm. Relates to #49869 Relates to #51841
1 parent ba9c4fb commit 026474d

13 files changed

+517
-191
lines changed

modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ ScriptRoot compile(Loader loader, String name, String source, CompilerSettings s
211211
SClass root = Walker.buildPainlessTree(scriptClassInfo, name, source, settings, painlessLookup, null);
212212
ScriptRoot scriptRoot = root.analyze(painlessLookup, settings);
213213
ClassNode classNode = root.writeClass();
214+
ScriptInjectionPhase.phase(scriptRoot, classNode);
214215
Map<String, Object> statics = classNode.write();
215216

216217
try {
@@ -240,8 +241,9 @@ ScriptRoot compile(Loader loader, String name, String source, CompilerSettings s
240241
byte[] compile(String name, String source, CompilerSettings settings, Printer debugStream) {
241242
ScriptClassInfo scriptClassInfo = new ScriptClassInfo(painlessLookup, scriptClass);
242243
SClass root = Walker.buildPainlessTree(scriptClassInfo, name, source, settings, painlessLookup, debugStream);
243-
root.analyze(painlessLookup, settings);
244+
ScriptRoot scriptRoot = root.analyze(painlessLookup, settings);
244245
ClassNode classNode = root.writeClass();
246+
ScriptInjectionPhase.phase(scriptRoot, classNode);
245247
classNode.write();
246248

247249
return classNode.getBytes();

modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,7 @@ public void invokeMethodCall(PainlessMethod painlessMethod) {
502502
// method since java 8 did not check, but java 9 and 10 do
503503
if (painlessMethod.javaMethod.getDeclaringClass().isInterface()) {
504504
visitMethodInsn(Opcodes.INVOKESTATIC, type.getInternalName(),
505-
painlessMethod.javaMethod.getName(), painlessMethod.methodType.toMethodDescriptorString(), true);
505+
painlessMethod.javaMethod.getName(), method.getDescriptor(), true);
506506
} else {
507507
invokeStatic(type, method);
508508
}

modules/lang-painless/src/main/java/org/elasticsearch/painless/Scope.java

+19-7
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,9 @@ public boolean isFinal() {
8787
public static class FunctionScope extends Scope {
8888

8989
protected final Class<?> returnType;
90-
protected final Set<String> usedVariables = new HashSet<>();
9190

9291
public FunctionScope(Class<?> returnType) {
92+
super(new HashSet<>());
9393
this.returnType = Objects.requireNonNull(returnType);
9494
}
9595

@@ -128,10 +128,6 @@ public Class<?> getReturnType() {
128128
public String getReturnCanonicalTypeName() {
129129
return PainlessLookupUtility.typeToCanonicalTypeName(returnType);
130130
}
131-
132-
public Set<String> getUsedVariables() {
133-
return Collections.unmodifiableSet(usedVariables);
134-
}
135131
}
136132

137133
/**
@@ -150,6 +146,7 @@ public static class LambdaScope extends Scope {
150146
protected final Set<Variable> captures = new HashSet<>();
151147

152148
protected LambdaScope(Scope parent, Class<?> returnType) {
149+
super(parent.usedVariables);
153150
this.parent = parent;
154151
this.returnType = returnType;
155152
}
@@ -180,6 +177,8 @@ public Variable getVariable(Location location, String name) {
180177
variable = parent.getVariable(location, name);
181178
variable = new Variable(variable.getType(), variable.getName(), true);
182179
captures.add(variable);
180+
} else {
181+
usedVariables.add(name);
183182
}
184183

185184
return variable;
@@ -213,6 +212,7 @@ public static class BlockScope extends Scope {
213212
protected final Scope parent;
214213

215214
protected BlockScope(Scope parent) {
215+
super(parent.usedVariables);
216216
this.parent = parent;
217217
}
218218

@@ -238,6 +238,8 @@ public Variable getVariable(Location location, String name) {
238238

239239
if (variable == null) {
240240
variable = parent.getVariable(location, name);
241+
} else {
242+
usedVariables.add(name);
241243
}
242244

243245
return variable;
@@ -263,9 +265,10 @@ public static FunctionScope newFunctionScope(Class<?> returnType) {
263265
}
264266

265267
protected final Map<String, Variable> variables = new HashMap<>();
268+
protected final Set<String> usedVariables;
266269

267-
protected Scope() {
268-
// do nothing
270+
protected Scope(Set<String> usedVariables) {
271+
this.usedVariables = usedVariables;
269272
}
270273

271274
/**
@@ -312,4 +315,13 @@ public boolean isInternalVariableDefined(String name) {
312315
public Variable getInternalVariable(Location location, String name) {
313316
return getVariable(location, "#" + name);
314317
}
318+
319+
/**
320+
* Returns the set of variables used within a top-level {@link FunctionScope}
321+
* including local variables no longer in scope upon completion of writing a
322+
* function to ASM bytecode.
323+
*/
324+
public Set<String> getUsedVariables() {
325+
return Collections.unmodifiableSet(usedVariables);
326+
}
315327
}

0 commit comments

Comments
 (0)