Skip to content

Commit

Permalink
Remove ILambda user tree node from Painless (#55190)
Browse files Browse the repository at this point in the history
The ILambda user tree node is no longer necessary with the addition of the ir 
tree as it was only supporting writing the ASM, but had no bearing on semantic 
checking. This moves the code necessary to build the def lambda recipes into 
the ir tree since we know that they are correct for compile time and the 
information necessary to do this is already known by the correct ir nodes.

This change also splits up the ir reference nodes into 
DefReferenceInterfaceNode, TypedInterfaceReferenceNode, and 
TypedCaptureReferenceNode which covers all possible cases of known 
information at compile time for method references. Each of these nodes is built 
by the user tree nodes as necessary.

Relates #53702
Closes #54015
  • Loading branch information
jdconrad authored Apr 21, 2020
1 parent bbfa5be commit 6035a29
Show file tree
Hide file tree
Showing 13 changed files with 252 additions and 414 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,13 @@
import java.util.ArrayList;
import java.util.List;

import static org.elasticsearch.painless.symbol.ScopeTable.Variable;

public class CallSubDefNode extends ArgumentsNode {

/* ---- begin node data ---- */

private String name;
private String recipe;
private final List<String> pointers = new ArrayList<>();
private final List<Class<?>> typeParameters = new ArrayList<>();

public void setName(String name) {
this.name = name;
Expand All @@ -45,50 +44,65 @@ public String getName() {
return name;
}

public void setRecipe(String recipe) {
this.recipe = recipe;
}

public String getRecipe() {
return recipe;
}

public void addPointer(String pointer) {
pointers.add(pointer);
}

public List<String> getPointers() {
return pointers;
}

public void addTypeParameter(Class<?> typeParameter) {
typeParameters.add(typeParameter);
}

public List<Class<?>> getTypeParameters() {
return typeParameters;
}

/* ---- end node data ---- */

/**
* Writes an invokedynamic instruction for a call with an unknown receiver type.
*/
@Override
protected void write(ClassWriter classWriter, MethodWriter methodWriter, ScopeTable scopeTable) {
methodWriter.writeDebugInfo(location);

for (ExpressionNode argumentNode : getArgumentNodes()) {
// its possible to have unknown functional interfaces
// as arguments that require captures; the set of
// captures with call arguments is ambiguous so
// additional information is encoded to indicate
// which are values are arguments and which are captures
StringBuilder defCallRecipe = new StringBuilder();
List<Object> boostrapArguments = new ArrayList<>();
List<Class<?>> typeParameters = new ArrayList<>();
int capturedCount = 0;

// add an Object class as a placeholder type for the receiver
typeParameters.add(Object.class);

for (int i = 0; i < getArgumentNodes().size(); ++i) {
ExpressionNode argumentNode = getArgumentNodes().get(i);
argumentNode.write(classWriter, methodWriter, scopeTable);

typeParameters.add(argumentNode.getExpressionType());

// handle the case for unknown functional interface
// to hint at which values are the call's arguments
// versus which values are captures
if (argumentNode instanceof DefInterfaceReferenceNode) {
DefInterfaceReferenceNode defInterfaceReferenceNode = (DefInterfaceReferenceNode)argumentNode;
boostrapArguments.add(defInterfaceReferenceNode.getDefReferenceEncoding());

// the encoding uses a char to indicate the number of captures
// where the value is the number of current arguments plus the
// total number of captures for easier capture count tracking
// when resolved at runtime
char encoding = (char)(i + capturedCount);
defCallRecipe.append(encoding);
capturedCount += defInterfaceReferenceNode.getCaptures().size();

for (String capturedName : defInterfaceReferenceNode.getCaptures()) {
Variable capturedVariable = scopeTable.getVariable(capturedName);
typeParameters.add(capturedVariable.getType());
}
}
}

// create method type from return value and arguments
Type[] asmParameterTypes = new Type[typeParameters.size()];

for (int index = 0; index < asmParameterTypes.length; ++index) {
asmParameterTypes[index] = MethodWriter.getType(typeParameters.get(index));
}

Type methodType = Type.getMethodType(MethodWriter.getType(getExpressionType()), asmParameterTypes);

List<Object> args = new ArrayList<>();
args.add(recipe);
args.addAll(pointers);
methodWriter.invokeDefCall(name, methodType, DefBootstrap.METHOD_CALL, args.toArray());
boostrapArguments.add(0, defCallRecipe.toString());
methodWriter.invokeDefCall(name, methodType, DefBootstrap.METHOD_CALL, boostrapArguments.toArray());
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* 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
* 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
Expand All @@ -20,34 +20,37 @@
package org.elasticsearch.painless.ir;

import org.elasticsearch.painless.ClassWriter;
import org.elasticsearch.painless.FunctionRef;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.symbol.ScopeTable;
import org.objectweb.asm.Opcodes;

public class NewArrayFuncRefNode extends ExpressionNode {
public class DefInterfaceReferenceNode extends ReferenceNode {

/* ---- begin node data ---- */

private FunctionRef funcRef;
private String defReferenceEncoding;

public void setFuncRef(FunctionRef funcRef) {
this.funcRef = funcRef;
public void setDefReferenceEncoding(String defReferenceEncoding) {
this.defReferenceEncoding = defReferenceEncoding;
}

public FunctionRef getFuncRef() {
return funcRef;
public String getDefReferenceEncoding() {
return defReferenceEncoding;
}

/* ---- end node data ---- */

@Override
protected void write(ClassWriter classWriter, MethodWriter methodWriter, ScopeTable scopeTable) {
if (funcRef != null) {
methodWriter.writeDebugInfo(location);
methodWriter.invokeLambdaCall(funcRef);
} else {
// push a null instruction as a placeholder for future lambda instructions
methodWriter.push((String)null);
methodWriter.writeDebugInfo(location);

// place holder for functional interface receiver
// which is resolved and replace at runtime
methodWriter.push((String)null);

for (String capture : getCaptures()) {
ScopeTable.Variable variable = scopeTable.getVariable(capture);
methodWriter.visitVarInsn(variable.getAsmType().getOpcode(Opcodes.ILOAD), variable.getSlot());
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* 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
* 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
Expand All @@ -17,26 +17,25 @@
* under the License.
*/

package org.elasticsearch.painless.node;
package org.elasticsearch.painless.ir;

import java.util.ArrayList;
import java.util.List;

/**
* Interface for lambda/method reference nodes. They need special handling by LDefCall.
* <p>
* This is because they know nothing about the target interface, and can only push
* all their captures onto the stack and defer everything until link-time.
*/
interface ILambda {
public abstract class ReferenceNode extends ExpressionNode {

/* ---- begin node data ---- */

/** Returns reference to resolve at link-time */
String getPointer();
private final List<String> captures = new ArrayList<>();

/** Returns the types of captured parameters. Can be empty */
List<Class<?>> getCaptures();
public void addCapture(String capture) {
captures.add(capture);
}

/** Returns the number of captured parameters */
default int getCaptureCount() {
return getCaptures().size();
public List<String> getCaptures() {
return captures;
}

/* ---- end node data ---- */

}
Loading

0 comments on commit 6035a29

Please sign in to comment.