/* * SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. */ /* * 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. */ /* * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ package org.opensearch.painless; import org.opensearch.painless.lookup.PainlessCast; import org.opensearch.painless.lookup.PainlessMethod; import org.opensearch.painless.lookup.def; import org.opensearch.script.JodaCompatibleZonedDateTime; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.commons.GeneratorAdapter; import org.objectweb.asm.commons.Method; import java.lang.reflect.Modifier; import java.time.ZonedDateTime; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; import java.util.Deque; import java.util.List; import static org.opensearch.painless.WriterConstants.CHAR_TO_STRING; import static org.opensearch.painless.WriterConstants.DEF_BOOTSTRAP_HANDLE; import static org.opensearch.painless.WriterConstants.DEF_TO_B_BOOLEAN; import static org.opensearch.painless.WriterConstants.DEF_TO_B_BYTE_EXPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_B_BYTE_IMPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_B_CHARACTER_EXPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_B_CHARACTER_IMPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_B_DOUBLE_EXPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_B_DOUBLE_IMPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_B_FLOAT_EXPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_B_FLOAT_IMPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_B_INTEGER_EXPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_B_INTEGER_IMPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_B_LONG_EXPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_B_LONG_IMPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_B_SHORT_EXPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_B_SHORT_IMPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_P_BOOLEAN; import static org.opensearch.painless.WriterConstants.DEF_TO_P_BYTE_EXPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_P_BYTE_IMPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_P_CHAR_EXPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_P_CHAR_IMPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_P_DOUBLE_EXPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_P_DOUBLE_IMPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_P_FLOAT_EXPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_P_FLOAT_IMPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_P_INT_EXPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_P_INT_IMPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_P_LONG_EXPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_P_LONG_IMPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_P_SHORT_EXPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_P_SHORT_IMPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_STRING_EXPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_STRING_IMPLICIT; import static org.opensearch.painless.WriterConstants.DEF_TO_ZONEDDATETIME; import static org.opensearch.painless.WriterConstants.DEF_UTIL_TYPE; import static org.opensearch.painless.WriterConstants.INDY_STRING_CONCAT_BOOTSTRAP_HANDLE; import static org.opensearch.painless.WriterConstants.JCZDT_TO_ZONEDDATETIME; import static org.opensearch.painless.WriterConstants.LAMBDA_BOOTSTRAP_HANDLE; import static org.opensearch.painless.WriterConstants.MAX_INDY_STRING_CONCAT_ARGS; import static org.opensearch.painless.WriterConstants.PAINLESS_ERROR_TYPE; import static org.opensearch.painless.WriterConstants.STRINGBUILDER_APPEND_BOOLEAN; import static org.opensearch.painless.WriterConstants.STRINGBUILDER_APPEND_CHAR; import static org.opensearch.painless.WriterConstants.STRINGBUILDER_APPEND_DOUBLE; import static org.opensearch.painless.WriterConstants.STRINGBUILDER_APPEND_FLOAT; import static org.opensearch.painless.WriterConstants.STRINGBUILDER_APPEND_INT; import static org.opensearch.painless.WriterConstants.STRINGBUILDER_APPEND_LONG; import static org.opensearch.painless.WriterConstants.STRINGBUILDER_APPEND_OBJECT; import static org.opensearch.painless.WriterConstants.STRINGBUILDER_APPEND_STRING; import static org.opensearch.painless.WriterConstants.STRINGBUILDER_CONSTRUCTOR; import static org.opensearch.painless.WriterConstants.STRINGBUILDER_TOSTRING; import static org.opensearch.painless.WriterConstants.STRINGBUILDER_TYPE; import static org.opensearch.painless.WriterConstants.STRING_TO_CHAR; import static org.opensearch.painless.WriterConstants.STRING_TYPE; import static org.opensearch.painless.WriterConstants.UTILITY_TYPE; /** * Extension of {@link GeneratorAdapter} with some utility methods. *
* Set of methods used during the writing phase of compilation
* shared by the nodes of the Painless tree.
*/
public final class MethodWriter extends GeneratorAdapter {
private final BitSet statements;
private final CompilerSettings settings;
private final Deque
* This is invoked for each statement boundary (leaf {@code S*} nodes).
*/
public void writeStatementOffset(Location location) {
int offset = location.getOffset();
// ensure we don't have duplicate stuff going in here. can catch bugs
// (e.g. nodes get assigned wrong offsets by antlr walker)
// TODO: introduce a way to ignore internal statements so this assert is not triggered
// TODO: https://github.com/elastic/elasticsearch/issues/51836
// assert statements.get(offset) == false;
statements.set(offset);
}
/**
* Encodes the offset into the line number table as {@code offset + 1}.
*
* This is invoked before instructions that can hit exceptions.
*/
public void writeDebugInfo(Location location) {
// TODO: maybe track these in bitsets too? this is trickier...
Label label = new Label();
visitLabel(label);
visitLineNumber(location.getOffset() + 1, label);
}
public void writeLoopCounter(int slot, Location location) {
assert slot != -1;
writeDebugInfo(location);
final Label end = new Label();
iinc(slot, -1);
visitVarInsn(Opcodes.ILOAD, slot);
push(0);
ifICmp(GeneratorAdapter.GT, end);
throwException(PAINLESS_ERROR_TYPE, "The maximum number of statements that can be executed in a loop has been reached.");
mark(end);
}
public void writeCast(PainlessCast cast) {
if (cast == null) {
return;
}
if (cast.originalType == char.class && cast.targetType == String.class) {
invokeStatic(UTILITY_TYPE, CHAR_TO_STRING);
} else if (cast.originalType == String.class && cast.targetType == char.class) {
invokeStatic(UTILITY_TYPE, STRING_TO_CHAR);
// TODO: remove this when the transition from Joda to Java datetimes is completed
} else if (cast.originalType == JodaCompatibleZonedDateTime.class && cast.targetType == ZonedDateTime.class) {
invokeStatic(UTILITY_TYPE, JCZDT_TO_ZONEDDATETIME);
} else if (cast.unboxOriginalType != null && cast.boxTargetType != null) {
unbox(getType(cast.unboxOriginalType));
writeCast(cast.unboxOriginalType, cast.boxTargetType);
box(getType(cast.boxTargetType));
} else if (cast.unboxOriginalType != null) {
unbox(getType(cast.unboxOriginalType));
writeCast(cast.originalType, cast.targetType);
} else if (cast.unboxTargetType != null) {
writeCast(cast.originalType, cast.targetType);
unbox(getType(cast.unboxTargetType));
} else if (cast.boxOriginalType != null) {
box(getType(cast.boxOriginalType));
writeCast(cast.originalType, cast.targetType);
} else if (cast.boxTargetType != null) {
writeCast(cast.originalType, cast.targetType);
box(getType(cast.boxTargetType));
} else if (cast.originalType == def.class) {
if (cast.explicitCast) {
if (cast.targetType == boolean.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_BOOLEAN);
else if (cast.targetType == byte.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_BYTE_EXPLICIT);
else if (cast.targetType == short.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_SHORT_EXPLICIT);
else if (cast.targetType == char.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_CHAR_EXPLICIT);
else if (cast.targetType == int.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_INT_EXPLICIT);
else if (cast.targetType == long.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_LONG_EXPLICIT);
else if (cast.targetType == float.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_FLOAT_EXPLICIT);
else if (cast.targetType == double.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_DOUBLE_EXPLICIT);
else if (cast.targetType == Boolean.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_BOOLEAN);
else if (cast.targetType == Byte.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_BYTE_EXPLICIT);
else if (cast.targetType == Short.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_SHORT_EXPLICIT);
else if (cast.targetType == Character.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_CHARACTER_EXPLICIT);
else if (cast.targetType == Integer.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_INTEGER_EXPLICIT);
else if (cast.targetType == Long.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_LONG_EXPLICIT);
else if (cast.targetType == Float.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_FLOAT_EXPLICIT);
else if (cast.targetType == Double.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_DOUBLE_EXPLICIT);
else if (cast.targetType == String.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_STRING_EXPLICIT);
// TODO: remove this when the transition from Joda to Java datetimes is completed
else if (cast.targetType == ZonedDateTime.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_ZONEDDATETIME);
else {
writeCast(cast.originalType, cast.targetType);
}
} else {
if (cast.targetType == boolean.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_BOOLEAN);
else if (cast.targetType == byte.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_BYTE_IMPLICIT);
else if (cast.targetType == short.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_SHORT_IMPLICIT);
else if (cast.targetType == char.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_CHAR_IMPLICIT);
else if (cast.targetType == int.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_INT_IMPLICIT);
else if (cast.targetType == long.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_LONG_IMPLICIT);
else if (cast.targetType == float.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_FLOAT_IMPLICIT);
else if (cast.targetType == double.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_DOUBLE_IMPLICIT);
else if (cast.targetType == Boolean.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_BOOLEAN);
else if (cast.targetType == Byte.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_BYTE_IMPLICIT);
else if (cast.targetType == Short.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_SHORT_IMPLICIT);
else if (cast.targetType == Character.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_CHARACTER_IMPLICIT);
else if (cast.targetType == Integer.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_INTEGER_IMPLICIT);
else if (cast.targetType == Long.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_LONG_IMPLICIT);
else if (cast.targetType == Float.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_FLOAT_IMPLICIT);
else if (cast.targetType == Double.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_DOUBLE_IMPLICIT);
else if (cast.targetType == String.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_STRING_IMPLICIT);
// TODO: remove this when the transition from Joda to Java datetimes is completed
else if (cast.targetType == ZonedDateTime.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_ZONEDDATETIME);
else {
writeCast(cast.originalType, cast.targetType);
}
}
} else {
writeCast(cast.originalType, cast.targetType);
}
}
private void writeCast(Class> from, Class> to) {
if (from.equals(to)) {
return;
}
if (from != boolean.class && from.isPrimitive() && to != boolean.class && to.isPrimitive()) {
cast(getType(from), getType(to));
} else {
if (!to.isAssignableFrom(from)) {
checkCast(getType(to));
}
}
}
/**
* Proxy the box method to use valueOf instead to ensure that the modern boxing methods are used.
*/
@Override
public void box(Type type) {
valueOf(type);
}
public static Type getType(Class> clazz) {
if (clazz.isArray()) {
Class> component = clazz.getComponentType();
int dimensions = 1;
while (component.isArray()) {
component = component.getComponentType();
++dimensions;
}
if (component == def.class) {
char[] braces = new char[dimensions];
Arrays.fill(braces, '[');
return Type.getType(new String(braces) + Type.getType(Object.class).getDescriptor());
}
} else if (clazz == def.class) {
return Type.getType(Object.class);
}
return Type.getType(clazz);
}
/** Starts a new string concat.
* @return the size of arguments pushed to stack (the object that does string concats, e.g. a StringBuilder)
*/
public int writeNewStrings() {
if (INDY_STRING_CONCAT_BOOTSTRAP_HANDLE != null) {
// Java 9+: we just push our argument collector onto deque
stringConcatArgs.push(new ArrayList<>());
return 0; // nothing added to stack
} else {
// Java 8: create a StringBuilder in bytecode
newInstance(STRINGBUILDER_TYPE);
dup();
invokeConstructor(STRINGBUILDER_TYPE, STRINGBUILDER_CONSTRUCTOR);
return 1; // StringBuilder on stack
}
}
public void writeAppendStrings(Class> clazz) {
if (INDY_STRING_CONCAT_BOOTSTRAP_HANDLE != null) {
// Java 9+: record type information
stringConcatArgs.peek().add(getType(clazz));
// prevent too many concat args.
// If there are too many, do the actual concat:
if (stringConcatArgs.peek().size() >= MAX_INDY_STRING_CONCAT_ARGS) {
writeToStrings();
writeNewStrings();
// add the return value type as new first param for next concat:
stringConcatArgs.peek().add(STRING_TYPE);
}
} else {
// Java 8: push a StringBuilder append
if (clazz == boolean.class) invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_APPEND_BOOLEAN);
else if (clazz == char.class) invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_APPEND_CHAR);
else if (clazz == byte.class || clazz == short.class || clazz == int.class) invokeVirtual(
STRINGBUILDER_TYPE,
STRINGBUILDER_APPEND_INT
);
else if (clazz == long.class) invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_APPEND_LONG);
else if (clazz == float.class) invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_APPEND_FLOAT);
else if (clazz == double.class) invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_APPEND_DOUBLE);
else if (clazz == String.class) invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_APPEND_STRING);
else invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_APPEND_OBJECT);
}
}
public void writeToStrings() {
if (INDY_STRING_CONCAT_BOOTSTRAP_HANDLE != null) {
// Java 9+: use type information and push invokeDynamic
final String desc = Type.getMethodDescriptor(STRING_TYPE, stringConcatArgs.pop().stream().toArray(Type[]::new));
invokeDynamic("concat", desc, INDY_STRING_CONCAT_BOOTSTRAP_HANDLE);
} else {
// Java 8: call toString() on StringBuilder
invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_TOSTRING);
}
}
/** Writes a dynamic binary instruction: returnType, lhs, and rhs can be different */
public void writeDynamicBinaryInstruction(
Location location,
Class> returnType,
Class> lhs,
Class> rhs,
Operation operation,
int flags
) {
Type methodType = Type.getMethodType(getType(returnType), getType(lhs), getType(rhs));
switch (operation) {
case MUL:
invokeDefCall("mul", methodType, DefBootstrap.BINARY_OPERATOR, flags);
break;
case DIV:
invokeDefCall("div", methodType, DefBootstrap.BINARY_OPERATOR, flags);
break;
case REM:
invokeDefCall("rem", methodType, DefBootstrap.BINARY_OPERATOR, flags);
break;
case ADD:
// if either side is primitive, then the + operator should always throw NPE on null,
// so we don't need a special NPE guard.
// otherwise, we need to allow nulls for possible string concatenation.
boolean hasPrimitiveArg = lhs.isPrimitive() || rhs.isPrimitive();
if (!hasPrimitiveArg) {
flags |= DefBootstrap.OPERATOR_ALLOWS_NULL;
}
invokeDefCall("add", methodType, DefBootstrap.BINARY_OPERATOR, flags);
break;
case SUB:
invokeDefCall("sub", methodType, DefBootstrap.BINARY_OPERATOR, flags);
break;
case LSH:
invokeDefCall("lsh", methodType, DefBootstrap.SHIFT_OPERATOR, flags);
break;
case USH:
invokeDefCall("ush", methodType, DefBootstrap.SHIFT_OPERATOR, flags);
break;
case RSH:
invokeDefCall("rsh", methodType, DefBootstrap.SHIFT_OPERATOR, flags);
break;
case BWAND:
invokeDefCall("and", methodType, DefBootstrap.BINARY_OPERATOR, flags);
break;
case XOR:
invokeDefCall("xor", methodType, DefBootstrap.BINARY_OPERATOR, flags);
break;
case BWOR:
invokeDefCall("or", methodType, DefBootstrap.BINARY_OPERATOR, flags);
break;
default:
throw location.createError(new IllegalStateException("Illegal tree structure."));
}
}
/** Writes a static binary instruction */
public void writeBinaryInstruction(Location location, Class> clazz, Operation operation) {
if ((clazz == float.class || clazz == double.class)
&& (operation == Operation.LSH
|| operation == Operation.USH
|| operation == Operation.RSH
|| operation == Operation.BWAND
|| operation == Operation.XOR
|| operation == Operation.BWOR)) {
throw location.createError(new IllegalStateException("Illegal tree structure."));
}
switch (operation) {
case MUL:
math(GeneratorAdapter.MUL, getType(clazz));
break;
case DIV:
math(GeneratorAdapter.DIV, getType(clazz));
break;
case REM:
math(GeneratorAdapter.REM, getType(clazz));
break;
case ADD:
math(GeneratorAdapter.ADD, getType(clazz));
break;
case SUB:
math(GeneratorAdapter.SUB, getType(clazz));
break;
case LSH:
math(GeneratorAdapter.SHL, getType(clazz));
break;
case USH:
math(GeneratorAdapter.USHR, getType(clazz));
break;
case RSH:
math(GeneratorAdapter.SHR, getType(clazz));
break;
case BWAND:
math(GeneratorAdapter.AND, getType(clazz));
break;
case XOR:
math(GeneratorAdapter.XOR, getType(clazz));
break;
case BWOR:
math(GeneratorAdapter.OR, getType(clazz));
break;
default:
throw location.createError(new IllegalStateException("Illegal tree structure."));
}
}
public void writeDup(final int size, final int xsize) {
if (size == 1) {
if (xsize == 2) {
dupX2();
} else if (xsize == 1) {
dupX1();
} else {
dup();
}
} else if (size == 2) {
if (xsize == 2) {
dup2X2();
} else if (xsize == 1) {
dup2X1();
} else {
dup2();
}
}
}
public void writePop(final int size) {
if (size == 1) {
pop();
} else if (size == 2) {
pop2();
}
}
@Override
public void endMethod() {
if (stringConcatArgs != null && !stringConcatArgs.isEmpty()) {
throw new IllegalStateException("String concat bytecode not completed.");
}
super.endMethod();
}
@Override
public void visitEnd() {
throw new AssertionError("Should never call this method on MethodWriter, use endMethod() instead");
}
/**
* Writes a dynamic call for a def method.
* @param name method name
* @param methodType callsite signature
* @param flavor type of call
* @param params flavor-specific parameters
*/
public void invokeDefCall(String name, Type methodType, int flavor, Object... params) {
Object[] args = new Object[params.length + 2];
args[0] = settings.getInitialCallSiteDepth();
args[1] = flavor;
System.arraycopy(params, 0, args, 2, params.length);
invokeDynamic(name, methodType.getDescriptor(), DEF_BOOTSTRAP_HANDLE, args);
}
public void invokeMethodCall(PainlessMethod painlessMethod) {
Type type = Type.getType(painlessMethod.javaMethod.getDeclaringClass());
Method method = Method.getMethod(painlessMethod.javaMethod);
if (Modifier.isStatic(painlessMethod.javaMethod.getModifiers())) {
// invokeStatic assumes that the owner class is not an interface, so this is a
// special case for interfaces where the interface method boolean needs to be set to
// true to reference the appropriate class constant when calling a static interface
// method since java 8 did not check, but java 9 and 10 do
if (painlessMethod.javaMethod.getDeclaringClass().isInterface()) {
visitMethodInsn(
Opcodes.INVOKESTATIC,
type.getInternalName(),
painlessMethod.javaMethod.getName(),
method.getDescriptor(),
true
);
} else {
invokeStatic(type, method);
}
} else if (painlessMethod.javaMethod.getDeclaringClass().isInterface()) {
invokeInterface(type, method);
} else {
invokeVirtual(type, method);
}
}
public void invokeLambdaCall(FunctionRef functionRef) {
Object[] args = new Object[7 + functionRef.delegateInjections.length];
args[0] = Type.getMethodType(functionRef.interfaceMethodType.toMethodDescriptorString());
args[1] = functionRef.delegateClassName;
args[2] = functionRef.delegateInvokeType;
args[3] = functionRef.delegateMethodName;
args[4] = Type.getMethodType(functionRef.delegateMethodType.toMethodDescriptorString());
args[5] = functionRef.isDelegateInterface ? 1 : 0;
args[6] = functionRef.isDelegateAugmented ? 1 : 0;
System.arraycopy(functionRef.delegateInjections, 0, args, 7, functionRef.delegateInjections.length);
invokeDynamic(
functionRef.interfaceMethodName,
functionRef.factoryMethodType.toMethodDescriptorString(),
LAMBDA_BOOTSTRAP_HANDLE,
args
);
}
}
> stringConcatArgs = (INDY_STRING_CONCAT_BOOTSTRAP_HANDLE == null) ? null : new ArrayDeque<>();
public MethodWriter(int access, Method method, ClassVisitor cw, BitSet statements, CompilerSettings settings) {
super(
Opcodes.ASM5,
cw.visitMethod(access, method.getName(), method.getDescriptor(), null, null),
access,
method.getName(),
method.getDescriptor()
);
this.statements = statements;
this.settings = settings;
}
/**
* Marks a new statement boundary.
*