/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* Code generation backend for LambdaForm.
* <p>
* @author John Rose, JSR 292 EG
*/
class InvokerBytecodeGenerator {
/** Define class names for convenience. */
/** Name of its super class*/
/** Name of new class */
/** Name of the source file (for stack trace printing). */
private final int[] localsMap;
/** ASM bytecode generation. */
}
if (DUMP_CLASS_FILES) {
}
this.lambdaForm = lambdaForm;
this.invokerName = invokerName;
this.invokerType = invokerType;
this.localsMap = new int[localsMapSize];
}
// Create an array to map name indexes to locals indexes.
}
}
// Create an array to map name indexes to locals indexes.
}
}
/** instance counters for dumped classes */
/** debugging flag for saving generated class files */
static {
if (DUMP_CLASS_FILES) {
DUMP_CLASS_FILES_COUNTERS = new HashMap<>();
try {
}
} catch (Exception e) {
throw newInternalError(e);
}
} else {
}
}
if (DUMP_CLASS_FILES) {
try {
//dumpName = dumpName.replace('/', '-');
return null;
} catch (IOException ex) {
throw newInternalError(ex);
}
}
});
}
}
synchronized (DUMP_CLASS_FILES_COUNTERS) {
}
return className;
}
class CpPatch {
final int index;
this.placeholder = placeholder;
}
}
}
}
// insert placeholder in CP and remember the patch
return cpPlaceholder;
}
throw new InternalError("in cpool["+size+"]: "+p+"\n"+Arrays.toString(Arrays.copyOf(classFile, 20)));
}
return res;
}
/**
* Extract the number of constant pool entries from a given class file.
*
* @param classFile the bytes of the class file in question.
* @return the number of entries in the constant pool.
*/
// The first few bytes:
// u4 magic;
// u2 minor_version;
// u2 major_version;
// u2 constant_pool_count;
}
/**
* Extract the MemberName of a newly-defined method.
*
* @param classFile
* @return
*/
}
/**
* Define a given class as anonymous class in the runtime system.
*
* @param classBytes
* @param patches
* @return
*/
UNSAFE.ensureClassInitialized(invokerClass); // Make sure the class is initialized; VM might complain.
return invokerClass;
}
/**
* TODO
*
* @param invokerClass
* @param name
* @param type
* @return
*/
private static MemberName resolveInvokerMember(Class<?> invokerClass, String name, MethodType type) {
//System.out.println("resolveInvokerMember => "+member);
//for (Method m : invokerClass.getDeclaredMethods()) System.out.println(" "+m);
try {
member = MEMBERNAME_FACTORY.resolveOrFail(REF_invokeStatic, member, HOST_CLASS, ReflectiveOperationException.class);
} catch (ReflectiveOperationException e) {
throw newInternalError(e);
}
//System.out.println("resolveInvokerMember => "+member);
return member;
}
/**
* Set up class file generation.
*/
private void classFilePrologue() {
cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, className, null, superName, null);
}
/**
* Tear down class file generation.
*/
private void classFileEpilogue() {
}
/*
* Low-level emit helpers.
*/
return;
}
emitIconstInsn((int) con);
return;
}
long x = (long) con;
if (x == (short) x) {
emitIconstInsn((int) x);
return;
}
}
float x = (float) con;
if (x == (short) x) {
emitIconstInsn((int) x);
return;
}
}
double x = (double) con;
if (x == (short) x) {
emitIconstInsn((int) x);
return;
}
}
return;
}
// fall through:
}
private void emitIconstInsn(int i) {
int opcode;
switch (i) {
default:
if (i == (byte) i) {
} else if (i == (short) i) {
} else {
mv.visitLdcInsn(i);
}
return;
}
}
/*
*/
int opcode;
switch (type) {
default:
}
}
}
int opcode;
switch (type) {
default:
}
}
}
/**
* Emit a boxing call.
*
* @param type primitive type class to box.
*/
}
/**
* Emit an unboxing call (plus preceding checkcast).
*
* @param type wrapper type class to unbox.
*/
}
/**
* Emit an implicit conversion.
*
* @param ptype type of value present on stack
* @param pclass type of value required on stack
*/
switch (ptype) {
case 'L':
return;
if (isStaticallyNameable(pclass)) {
} else {
}
return;
case 'I':
return;
case 'J':
assert(pclass == long.class);
return;
case 'F':
assert(pclass == float.class);
return;
case 'D':
assert(pclass == double.class);
return;
}
}
/**
* Emits an actual return instruction conforming to the given return type.
*/
int opcode;
default:
}
}
}
/**
* Generate customized bytecode for a given LambdaForm.
*
* @param form
* @param invokerType
* @return
*/
return g.loadMethod(g.generateCustomizedCodeBytes());
}
/**
* Generate an invoker method for the passed {@link LambdaForm}.
*/
private byte[] generateCustomizedCodeBytes() {
// Suppress this method in backtraces displayed to the user.
// Mark this method as a compiled LambdaForm
// Force inlining of this invoker method.
// iterate over the form's names, generating bytecode instructions for each
// start iterating at the first name following the arguments
if (isSelectAlternative(member)) {
// selectAlternative idiom
// FIXME: make sure this idiom is really present!
i++; // skip MH.invokeBasic of the selectAlternative result
} else if (isStaticallyInvocable(member)) {
} else {
}
// store the result from evaluating to the target name in a local if required
// (if this is the last value, i.e., the one that is going to be returned,
// return value - do nothing
// non-void: actually assign
}
}
// return statement
emitReturn();
return classFile;
}
/**
* Emit an invoke for the given name.
*
* @param name
*/
if (true) {
// push receiver
} else {
// load receiver
emitAloadInsn(0);
// TODO more to come
}
// push arguments
emitPushArgument(name, i);
}
// invocation
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString());
}
// Sample classes from each package we are willing to bind to statically:
//MethodHandle.class already covered
};
if (member.isConstructor()) return false;
return false; // FIXME
return false; // inner class of some sort
return false; // not on BCP
return true; // in java.lang.invoke package
return true;
return false;
}
if (cls.isPrimitive())
return true; // int[].class, for example
return false;
return true;
return false;
return true;
}
return false;
}
/**
* Emit an invoke for the given name, using the MemberName directly.
*
* @param name
*/
if (refKind == REF_invokeSpecial) {
// in order to pass the verifier, we need to convert this to invokevirtual in all cases
}
// push arguments
emitPushArgument(name, i);
}
// invocation
} else {
}
}
switch (refKind) {
}
}
/**
* Check if MemberName is a call to MethodHandleImpl.selectAlternative.
*
* @param member
* @return true if member is a call to MethodHandleImpl.selectAlternative
*/
}
/**
* Emit bytecode for the selectAlternative idiom.
*
* The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest):
*
* Lambda(a0:L,a1:I)=>{
* t2:I=foo.test(a1:I);
* t3:L=MethodHandleImpl.selectAlternative(t2:I,(MethodHandle(int)int),(MethodHandle(int)int));
* t4:I=MethodHandle.invokeBasic(t3:L,a1:I);t4:I}
*
* @param selectAlternativeName
* @param invokeBasicName
*/
// load test result
// if_icmpne L_fallback
// invoke selectAlternativeName.arguments[1]
// goto L_done
// L_fallback:
// invoke selectAlternativeName.arguments[2]
// L_done:
}
/**
*
* @param name
* @param paramIndex
*/
} else {
} else {
}
}
}
/**
* Emits a return statement from a LF invoker. If required, the result type is cast to the correct return type.
*/
private void emitReturn() {
// return statement
// void
} else {
// put return value on the stack if it is not already there
}
// potentially generate cast
// rtype is the return type of the invoker - generated code must conform to this
// rn.type is the type of the result Name in the LF
// need cast
if (rtype == 'L') {
// possibly cast the primitive to the correct type for boxing
}
// cast primitive to reference ("boxing")
} else {
// to-primitive cast
// prim-to-prim cast
} else {
// ref-to-prim cast ("unboxing")
throw new InternalError("no ref-to-prim (unboxing) casts supported right now");
}
}
}
// generate actual return statement
}
}
/**
* Emit a type conversion bytecode casting from "from" to "to".
*/
// Here's how.
// - indicates forbidden
// <-> indicates implicit
// to ----> boolean byte short char int long float double
// from boolean <-> - - - - - - -
// byte - <-> i2s i2c <-> i2l i2f i2d
// short - i2b <-> i2c <-> i2l i2f i2d
// char - i2b i2s <-> <-> i2l i2f i2d
// int - i2b i2s i2c <-> i2l i2f i2d
// long - l2i,i2b l2i,i2s l2i,i2c l2i <-> l2f l2d
// float - f2i,i2b f2i,i2s f2i,i2c f2i f2l <-> f2d
// double - d2i,i2b d2i,i2s d2i,i2c d2i d2l d2f <->
// no cast required, should be dead code anyway
return;
}
if (wfrom.isSubwordOrInt()) {
// cast from {byte,short,char,int} to anything
} else {
// cast from {long,float,double} to anything
if (wto.isSubwordOrInt()) {
// cast to {byte,short,char,int}
// targets other than int require another conversion
}
} else {
// cast to {long,float,double} - this is verbose
boolean error = false;
switch (from) {
case 'J':
else error = true;
break;
case 'F':
else error = true;
break;
case 'D':
else error = true;
break;
default:
error = true;
break;
}
if (error) {
}
}
}
}
switch (type) {
case 'I': /* naught */ break;
case 'Z':
// For compatibility with ValueConversions and explicitCastArguments:
break;
}
}
switch (type) {
}
}
}
/**
* Generate bytecode for a LambdaForm.vmentry which calls interpretWithArguments.
*
* @param sig
* @return
*/
//System.out.println("generateExactInvoker "+sig);
// compute method type
// first parameter and return type
// other parameter types
for (int i = 1; i < arity; i++) {
}
return g.loadMethod(g.generateLambdaFormInterpreterEntryPointBytes());
}
private byte[] generateLambdaFormInterpreterEntryPointBytes() {
// Suppress this method in backtraces displayed to the user.
// Don't inline the interpreter entry.
// create parameter array
// fill parameter array
emitIconstInsn(i);
// box if primitive type
if (ptype.isPrimitive()) {
}
}
// invoke
emitAloadInsn(0);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, LF, "interpretWithArguments", "([Ljava/lang/Object;)Ljava/lang/Object;");
// maybe unbox
}
// return statement
return classFile;
}
/**
* Generate bytecode for a NamedFunction invoker.
*
* @param srcType
* @param dstType
* @return
*/
}
// Suppress this method in backtraces displayed to the user.
// Force inlining of this invoker method.
// Load receiver
emitAloadInsn(0);
// Load arguments from array
emitAloadInsn(1);
emitIconstInsn(i);
// Maybe unbox
if (dptype.isPrimitive()) {
Wrapper srcWrapper = dstWrapper.isSubwordOrInt() ? Wrapper.INT : dstWrapper; // narrow subword from int
}
}
// Invoke
// Box primitive types
Wrapper dstWrapper = srcWrapper.isSubwordOrInt() ? Wrapper.INT : srcWrapper; // widen subword to int
// boolean casts not allowed
}
// If the return type is void we return a null reference.
if (rtype == void.class) {
}
return classFile;
}
/**
* Emit a bogus method that just loads some string constants. This is to get the constants into the constant pool
* for debugging purposes.
*/
if (DUMP_CLASS_FILES) {
}
}
}
}