# HG changeset patch # User Jaroslav Tulach # Date 1407646932 -7200 # Node ID bd151459ee4f88a8d0dd68a59d7b5c5d32036595 # Parent 8fb89a569621e37ea53504b74b2b29ed25e3dca0 Capable to compile the java.lang.invoke stuff diff -r 8fb89a569621 -r bd151459ee4f rt/emul/compact/pom.xml --- a/rt/emul/compact/pom.xml Sun Aug 10 06:21:50 2014 +0200 +++ b/rt/emul/compact/pom.xml Sun Aug 10 07:02:12 2014 +0200 @@ -55,8 +55,8 @@ netbeans.ignore.jdk.bootclasspath - 1.7 - 1.7 + 1.8 + 1.8 diff -r 8fb89a569621 -r bd151459ee4f rt/emul/compact/src/main/java/java/lang/ClassValue.java --- a/rt/emul/compact/src/main/java/java/lang/ClassValue.java Sun Aug 10 06:21:50 2014 +0200 +++ b/rt/emul/compact/src/main/java/java/lang/ClassValue.java Sun Aug 10 07:02:12 2014 +0200 @@ -186,7 +186,7 @@ /** Return the cache, if it exists, else a dummy empty cache. */ private static Entry[] getCacheCarefully(Class type) { // racing type.classValueMap{.cacheArray} : null => new Entry[X] <=> new Entry[Y] - ClassValueMap map = type.classValueMap; + ClassValueMap map = (ClassValueMap) type.classValueMap; if (map == null) return EMPTY_CACHE; Entry[] cache = map.getCache(); return cache; @@ -364,7 +364,7 @@ // racing type.classValueMap : null (blank) => unique ClassValueMap // if a null is observed, a map is created (lazily, synchronously, uniquely) // all further access to that map is synchronized - ClassValueMap map = type.classValueMap; + ClassValueMap map = (ClassValueMap)type.classValueMap; if (map != null) return map; return initializeMap(type); } @@ -374,7 +374,7 @@ ClassValueMap map; synchronized (CRITICAL_SECTION) { // private object to avoid deadlocks // happens about once per type - if ((map = type.classValueMap) == null) + if ((map = (ClassValueMap)type.classValueMap) == null) type.classValueMap = map = new ClassValueMap(type); } return map; diff -r 8fb89a569621 -r bd151459ee4f rt/emul/compact/src/main/java/java/lang/invoke/BoundMethodHandle.java --- a/rt/emul/compact/src/main/java/java/lang/invoke/BoundMethodHandle.java Sun Aug 10 06:21:50 2014 +0200 +++ b/rt/emul/compact/src/main/java/java/lang/invoke/BoundMethodHandle.java Sun Aug 10 07:02:12 2014 +0200 @@ -25,24 +25,16 @@ package java.lang.invoke; -import static jdk.internal.org.objectweb.asm.Opcodes.*; import static java.lang.invoke.LambdaForm.basicTypes; -import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeStatic; import static java.lang.invoke.MethodHandleStatics.*; import java.lang.invoke.LambdaForm.Name; import java.lang.invoke.LambdaForm.NamedFunction; import java.lang.invoke.MethodHandles.Lookup; -import java.lang.reflect.Field; import java.util.Arrays; import java.util.HashMap; import sun.invoke.util.ValueConversions; -import sun.invoke.util.Wrapper; - -import jdk.internal.org.objectweb.asm.ClassWriter; -import jdk.internal.org.objectweb.asm.MethodVisitor; -import jdk.internal.org.objectweb.asm.Type; /** * The flavor of method handle which emulates an invoke instruction @@ -373,8 +365,9 @@ this.constructor = new MethodHandle[1]; this.getters = new MethodHandle[types.length()]; } else { - this.constructor = Factory.makeCtors(clazz, types, null); - this.getters = Factory.makeGetters(clazz, types, null); + throw new IllegalStateException("bound method handle"); +// this.constructor = Factory.makeCtors(clazz, types, null); +// this.getters = Factory.makeGetters(clazz, types, null); } this.extensions = new SpeciesData[EXTENSION_TYPES.length()]; } @@ -382,8 +375,8 @@ private void initForBootstrap() { assert(!INIT_DONE); if (constructor[0] == null) { - Factory.makeCtors(clazz, types, this.constructor); - Factory.makeGetters(clazz, types, this.getters); +// Factory.makeCtors(clazz, types, this.constructor); +// Factory.makeGetters(clazz, types, this.getters); } } @@ -425,7 +418,7 @@ // Use synch. on the placeholder to prevent multiple instantiation of one species. // Creating this class forces a recursive call to getForClass. if (lookupCache(types).isPlaceholder()) - Factory.generateConcreteBMHClass(types); + throw new IllegalStateException("Cannot generate anything"); } // Reacquire cache lock. d = lookupCache(types); @@ -459,6 +452,7 @@ SpeciesData d0 = BoundMethodHandle.SPECIES_DATA; // trigger class init assert(d0 == null || d0 == lookupCache("")) : d0; try { + /* for (Class c : rootCls.getDeclaredClasses()) { if (rootCls.isAssignableFrom(c)) { final Class cbmh = c.asSubclass(BoundMethodHandle.class); @@ -468,6 +462,7 @@ assert(d == lookupCache(d.types)); } } + */ } catch (Throwable e) { throw newInternalError(e); } @@ -485,375 +480,7 @@ return SpeciesData.get(types); } - /** - * Generation of concrete BMH classes. - * - * A concrete BMH species is fit for binding a number of values adhering to a - * given type pattern. Reference types are erased. - * - * BMH species are cached by type pattern. - * - * A BMH species has a number of fields with the concrete (possibly erased) types of - * bound values. Setters are provided as an API in BMH. Getters are exposed as MHs, - * which can be included as names in lambda forms. - */ - static class Factory { - static final String JLO_SIG = "Ljava/lang/Object;"; - static final String JLS_SIG = "Ljava/lang/String;"; - static final String JLC_SIG = "Ljava/lang/Class;"; - static final String MH = "java/lang/invoke/MethodHandle"; - static final String MH_SIG = "L"+MH+";"; - static final String BMH = "java/lang/invoke/BoundMethodHandle"; - static final String BMH_SIG = "L"+BMH+";"; - static final String SPECIES_DATA = "java/lang/invoke/BoundMethodHandle$SpeciesData"; - static final String SPECIES_DATA_SIG = "L"+SPECIES_DATA+";"; - - static final String SPECIES_PREFIX_NAME = "Species_"; - static final String SPECIES_PREFIX_PATH = BMH + "$" + SPECIES_PREFIX_NAME; - - static final String BMHSPECIES_DATA_EWI_SIG = "(B)" + SPECIES_DATA_SIG; - static final String BMHSPECIES_DATA_GFC_SIG = "(" + JLS_SIG + JLC_SIG + ")" + SPECIES_DATA_SIG; - static final String MYSPECIES_DATA_SIG = "()" + SPECIES_DATA_SIG; - static final String VOID_SIG = "()V"; - - static final String SIG_INCIPIT = "(Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;"; - - static final Class[] TYPES = new Class[] { Object.class, int.class, long.class, float.class, double.class }; - - static final String[] E_THROWABLE = new String[] { "java/lang/Throwable" }; - - /** - * Generate a concrete subclass of BMH for a given combination of bound types. - * - * A concrete BMH species adheres to the following schema: - * - *
-         * class Species_[[types]] extends BoundMethodHandle {
-         *     [[fields]]
-         *     final SpeciesData speciesData() { return SpeciesData.get("[[types]]"); }
-         * }
-         * 
- * - * The {@code [[types]]} signature is precisely the string that is passed to this - * method. - * - * The {@code [[fields]]} section consists of one field definition per character in - * the type signature, adhering to the naming schema described in the definition of - * {@link #makeFieldName}. - * - * For example, a concrete BMH species for two reference and one integral bound values - * would have the following shape: - * - *
-         * class BoundMethodHandle { ... private static
-         * final class Species_LLI extends BoundMethodHandle {
-         *     final Object argL0;
-         *     final Object argL1;
-         *     final int argI2;
-         *     public Species_LLI(MethodType mt, LambdaForm lf, Object argL0, Object argL1, int argI2) {
-         *         super(mt, lf);
-         *         this.argL0 = argL0;
-         *         this.argL1 = argL1;
-         *         this.argI2 = argI2;
-         *     }
-         *     public final SpeciesData speciesData() { return SPECIES_DATA; }
-         *     public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("LLI", Species_LLI.class);
-         *     public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) {
-         *         return SPECIES_DATA.constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2);
-         *     }
-         *     public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) {
-         *         return SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
-         *     }
-         *     public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) {
-         *         return SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
-         *     }
-         *     public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) {
-         *         return SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
-         *     }
-         *     public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) {
-         *         return SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
-         *     }
-         *     public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) {
-         *         return SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
-         *     }
-         * }
-         * 
- * - * @param types the type signature, wherein reference types are erased to 'L' - * @return the generated concrete BMH class - */ - static Class generateConcreteBMHClass(String types) { - final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES); - - final String className = SPECIES_PREFIX_PATH + types; - final String sourceFile = SPECIES_PREFIX_NAME + types; - cw.visit(V1_6, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, className, null, BMH, null); - cw.visitSource(sourceFile, null); - - // emit static types and SPECIES_DATA fields - cw.visitField(ACC_PUBLIC + ACC_STATIC, "SPECIES_DATA", SPECIES_DATA_SIG, null, null).visitEnd(); - - // emit bound argument fields - for (int i = 0; i < types.length(); ++i) { - final char t = types.charAt(i); - final String fieldName = makeFieldName(types, i); - final String fieldDesc = t == 'L' ? JLO_SIG : String.valueOf(t); - cw.visitField(ACC_FINAL, fieldName, fieldDesc, null, null).visitEnd(); - } - - MethodVisitor mv; - - // emit constructor - mv = cw.visitMethod(ACC_PUBLIC, "", makeSignature(types, true), null, null); - mv.visitCode(); - mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 2); - - mv.visitMethodInsn(INVOKESPECIAL, BMH, "", makeSignature("", true)); - - for (int i = 0, j = 0; i < types.length(); ++i, ++j) { - // i counts the arguments, j counts corresponding argument slots - char t = types.charAt(i); - mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(typeLoadOp(t), j + 3); // parameters start at 3 - mv.visitFieldInsn(PUTFIELD, className, makeFieldName(types, i), typeSig(t)); - if (t == 'J' || t == 'D') { - ++j; // adjust argument register access - } - } - - mv.visitInsn(RETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - - // emit implementation of reinvokerTarget() - mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "reinvokerTarget", "()" + MH_SIG, null, null); - mv.visitCode(); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, className, "argL0", JLO_SIG); - mv.visitTypeInsn(CHECKCAST, MH); - mv.visitInsn(ARETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - - // emit implementation of speciesData() - mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "speciesData", MYSPECIES_DATA_SIG, null, null); - mv.visitCode(); - mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG); - mv.visitInsn(ARETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - - // emit clone() - mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "clone", makeSignature("", false), null, E_THROWABLE); - mv.visitCode(); - // return speciesData().constructor[0].invokeBasic(mt, lf, argL0, ...) - // obtain constructor - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG); - mv.visitFieldInsn(GETFIELD, SPECIES_DATA, "constructor", "[" + MH_SIG); - mv.visitInsn(ICONST_0); - mv.visitInsn(AALOAD); - // load mt, lf - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 2); - // put fields on the stack - emitPushFields(types, className, mv); - // finally, invoke the constructor and return - mv.visitMethodInsn(INVOKEVIRTUAL, MH, "invokeBasic", makeSignature(types, false)); - mv.visitInsn(ARETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - - // for each type, emit cloneExtendT() - for (Class c : TYPES) { - char t = Wrapper.basicTypeChar(c); - mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "cloneExtend" + t, makeSignature(String.valueOf(t), false), null, E_THROWABLE); - mv.visitCode(); - // return SPECIES_DATA.extendWithIndex(extensionIndex(t)).constructor[0].invokeBasic(mt, lf, argL0, ..., narg) - // obtain constructor - mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG); - int iconstInsn = ICONST_0 + extensionIndex(t); - assert(iconstInsn <= ICONST_5); - mv.visitInsn(iconstInsn); - mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "extendWithIndex", BMHSPECIES_DATA_EWI_SIG); - mv.visitFieldInsn(GETFIELD, SPECIES_DATA, "constructor", "[" + MH_SIG); - mv.visitInsn(ICONST_0); - mv.visitInsn(AALOAD); - // load mt, lf - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 2); - // put fields on the stack - emitPushFields(types, className, mv); - // put narg on stack - mv.visitVarInsn(typeLoadOp(t), 3); - // finally, invoke the constructor and return - mv.visitMethodInsn(INVOKEVIRTUAL, MH, "invokeBasic", makeSignature(types + t, false)); - mv.visitInsn(ARETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - } - - // emit class initializer - mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "", VOID_SIG, null, null); - mv.visitCode(); - mv.visitLdcInsn(types); - mv.visitLdcInsn(Type.getObjectType(className)); - mv.visitMethodInsn(INVOKESTATIC, SPECIES_DATA, "getForClass", BMHSPECIES_DATA_GFC_SIG); - mv.visitFieldInsn(PUTSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG); - mv.visitInsn(RETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - - cw.visitEnd(); - - // load class - final byte[] classFile = cw.toByteArray(); - InvokerBytecodeGenerator.maybeDump(className, classFile); - Class bmhClass = - //UNSAFE.defineAnonymousClass(BoundMethodHandle.class, classFile, null).asSubclass(BoundMethodHandle.class); - UNSAFE.defineClass(className, classFile, 0, classFile.length, - BoundMethodHandle.class.getClassLoader(), null) - .asSubclass(BoundMethodHandle.class); - UNSAFE.ensureClassInitialized(bmhClass); - - return bmhClass; - } - - private static int typeLoadOp(char t) { - switch (t) { - case 'L': return ALOAD; - case 'I': return ILOAD; - case 'J': return LLOAD; - case 'F': return FLOAD; - case 'D': return DLOAD; - default : throw new InternalError("unrecognized type " + t); - } - } - - private static void emitPushFields(String types, String className, MethodVisitor mv) { - for (int i = 0; i < types.length(); ++i) { - char tc = types.charAt(i); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, className, makeFieldName(types, i), typeSig(tc)); - } - } - - static String typeSig(char t) { - return t == 'L' ? JLO_SIG : String.valueOf(t); - } - - // - // Getter MH generation. - // - - private static MethodHandle makeGetter(Class cbmhClass, String types, int index) { - String fieldName = makeFieldName(types, index); - Class fieldType = Wrapper.forBasicType(types.charAt(index)).primitiveType(); - try { - return LOOKUP.findGetter(cbmhClass, fieldName, fieldType); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw newInternalError(e); - } - } - - static MethodHandle[] makeGetters(Class cbmhClass, String types, MethodHandle[] mhs) { - if (mhs == null) mhs = new MethodHandle[types.length()]; - for (int i = 0; i < mhs.length; ++i) { - mhs[i] = makeGetter(cbmhClass, types, i); - assert(mhs[i].internalMemberName().getDeclaringClass() == cbmhClass); - } - return mhs; - } - - static MethodHandle[] makeCtors(Class cbmh, String types, MethodHandle mhs[]) { - if (mhs == null) mhs = new MethodHandle[1]; - mhs[0] = makeCbmhCtor(cbmh, types); - return mhs; - } - - // - // Auxiliary methods. - // - - static SpeciesData speciesDataFromConcreteBMHClass(Class cbmh) { - try { - Field F_SPECIES_DATA = cbmh.getDeclaredField("SPECIES_DATA"); - return (SpeciesData) F_SPECIES_DATA.get(null); - } catch (ReflectiveOperationException ex) { - throw newInternalError(ex); - } - } - - /** - * Field names in concrete BMHs adhere to this pattern: - * arg + type + index - * where type is a single character (L, I, J, F, D). - */ - private static String makeFieldName(String types, int index) { - assert index >= 0 && index < types.length(); - return "arg" + types.charAt(index) + index; - } - - private static String makeSignature(String types, boolean ctor) { - StringBuilder buf = new StringBuilder(SIG_INCIPIT); - for (char c : types.toCharArray()) { - buf.append(typeSig(c)); - } - return buf.append(')').append(ctor ? "V" : BMH_SIG).toString(); - } - - static MethodHandle makeCbmhCtor(Class cbmh, String types) { - try { - return linkConstructor(LOOKUP.findConstructor(cbmh, MethodType.fromMethodDescriptorString(makeSignature(types, true), null))); - } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | TypeNotPresentException e) { - throw newInternalError(e); - } - } - - /** - * Wrap a constructor call in a {@link LambdaForm}. - * - * If constructors ({@code } methods) are called in LFs, problems might arise if the LFs - * are turned into bytecode, because the call to the allocator is routed through an MH, and the - * verifier cannot find a {@code NEW} instruction preceding the {@code INVOKESPECIAL} to - * {@code }. To avoid this, we add an indirection by invoking {@code } through - * {@link MethodHandle#linkToSpecial}. - * - * The last {@link LambdaForm.Name Name} in the argument's form is expected to be the {@code void} - * result of the {@code } invocation. This entry is replaced. - */ - private static MethodHandle linkConstructor(MethodHandle cmh) { - final LambdaForm lf = cmh.form; - final int initNameIndex = lf.names.length - 1; - final Name initName = lf.names[initNameIndex]; - final MemberName ctorMN = initName.function.member; - final MethodType ctorMT = ctorMN.getInvocationType(); - - // obtain function member (call target) - // linker method type replaces initial parameter (BMH species) with BMH to avoid naming a species (anonymous class!) - final MethodType linkerMT = ctorMT.changeParameterType(0, BoundMethodHandle.class).appendParameterTypes(MemberName.class); - MemberName linkerMN = new MemberName(MethodHandle.class, "linkToSpecial", linkerMT, REF_invokeStatic); - try { - linkerMN = MemberName.getFactory().resolveOrFail(REF_invokeStatic, linkerMN, null, NoSuchMethodException.class); - assert(linkerMN.isStatic()); - } catch (ReflectiveOperationException ex) { - throw newInternalError(ex); - } - // extend arguments array - Object[] newArgs = Arrays.copyOf(initName.arguments, initName.arguments.length + 1); - newArgs[newArgs.length - 1] = ctorMN; - // replace function - final NamedFunction nf = new NamedFunction(linkerMN); - final Name linkedCtor = new Name(nf, newArgs); - linkedCtor.initIndex(initNameIndex); - lf.names[initNameIndex] = linkedCtor; - return cmh; - } - - } private static final Lookup LOOKUP = Lookup.IMPL_LOOKUP; diff -r 8fb89a569621 -r bd151459ee4f rt/emul/compact/src/main/java/java/lang/invoke/DirectMethodHandle.java --- a/rt/emul/compact/src/main/java/java/lang/invoke/DirectMethodHandle.java Sun Aug 10 06:21:50 2014 +0200 +++ b/rt/emul/compact/src/main/java/java/lang/invoke/DirectMethodHandle.java Sun Aug 10 07:02:12 2014 +0200 @@ -25,7 +25,6 @@ package java.lang.invoke; -import sun.misc.Unsafe; import java.lang.reflect.Method; import java.util.Arrays; import sun.invoke.util.VerifyAccess; @@ -326,19 +325,19 @@ VerifyAccess.isSamePackage(ValueConversions.class, cls)) { // It is a system class. It is probably in the process of // being initialized, but we will help it along just to be safe. - if (UNSAFE.shouldBeInitialized(cls)) { - UNSAFE.ensureClassInitialized(cls); + if (shouldBeInitialized(cls)) { + ensureClassInitialized(cls); } return false; } - return UNSAFE.shouldBeInitialized(cls); + return shouldBeInitialized(cls); } private static class EnsureInitialized extends ClassValue> { @Override protected WeakReference computeValue(Class type) { - UNSAFE.ensureClassInitialized(type); - if (UNSAFE.shouldBeInitialized(type)) + ensureClassInitialized(type); + if (shouldBeInitialized(type)) // If the previous call didn't block, this can happen. // We are executing inside . return new WeakReference<>(Thread.currentThread()); @@ -366,14 +365,14 @@ // Somebody may still be running defc.. if (clinitThread == Thread.currentThread()) { // If anybody is running defc., it is this thread. - if (UNSAFE.shouldBeInitialized(defc)) + if (shouldBeInitialized(defc)) // Yes, we are running it; keep the barrier for now. return false; } else { // We are in a random thread. Block. - UNSAFE.ensureClassInitialized(defc); + ensureClassInitialized(defc); } - assert(!UNSAFE.shouldBeInitialized(defc)); + assert(!shouldBeInitialized(defc)); // put it into the final state EnsureInitialized.INSTANCE.remove(defc); return true; @@ -423,7 +422,12 @@ /*non-public*/ static Object allocateInstance(Object mh) throws InstantiationException { Constructor dmh = (Constructor)mh; - return UNSAFE.allocateInstance(dmh.instanceClass); + try { + return dmh.instanceClass.newInstance(); +// return UNSAFE.allocateInstance(dmh.instanceClass); + } catch (IllegalAccessException ex) { + throw (InstantiationException)new InstantiationException().initCause(ex); + } } /** This subclass handles non-static field references. */ @@ -603,7 +607,7 @@ linkerType = MethodType.methodType(ft, Object.class, long.class); else linkerType = MethodType.methodType(void.class, Object.class, long.class, ft); - MemberName linker = new MemberName(Unsafe.class, linkerName, linkerType, REF_invokeVirtual); + MemberName linker = null;//new MemberName(Unsafe.class, linkerName, linkerType, REF_invokeVirtual); try { linker = IMPL_NAMES.resolveOrFail(REF_invokeVirtual, linker, null, NoSuchMethodException.class); } catch (ReflectiveOperationException ex) { @@ -642,7 +646,7 @@ names[PRE_CAST] = new Name(Lazy.NF_checkCast, names[DMH_THIS], names[SET_VALUE]); Object[] outArgs = new Object[1 + linkerType.parameterCount()]; assert(outArgs.length == (isGetter ? 3 : 4)); - outArgs[0] = UNSAFE; +// outArgs[0] = UNSAFE; if (isStatic) { outArgs[1] = names[F_HOLDER] = new Name(Lazy.NF_staticBase, names[DMH_THIS]); outArgs[2] = names[F_OFFSET] = new Name(Lazy.NF_staticOffset, names[DMH_THIS]); @@ -707,7 +711,7 @@ }; for (NamedFunction nf : nfs) { // Each nf must be statically invocable or we get tied up in our bootstraps. - assert(InvokerBytecodeGenerator.isStaticallyInvocable(nf.member)) : nf; +// assert(InvokerBytecodeGenerator.isStaticallyInvocable(nf.member)) : nf; nf.resolve(); } } catch (ReflectiveOperationException ex) { @@ -715,4 +719,12 @@ } } } + + private static boolean shouldBeInitialized(Class c) { + return false; + } + + private static void ensureClassInitialized(Class c) { + c.getName(); + } } diff -r 8fb89a569621 -r bd151459ee4f rt/emul/compact/src/main/java/java/lang/invoke/InnerClassLambdaMetafactory.java --- a/rt/emul/compact/src/main/java/java/lang/invoke/InnerClassLambdaMetafactory.java Sun Aug 10 06:21:50 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,551 +0,0 @@ -/* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * 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. - */ - -package java.lang.invoke; - -import jdk.internal.org.objectweb.asm.*; -import sun.invoke.util.BytecodeDescriptor; -import sun.misc.Unsafe; -import sun.security.action.GetPropertyAction; - -import java.io.FilePermission; -import java.io.Serializable; -import java.lang.reflect.Constructor; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.LinkedHashSet; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.PropertyPermission; -import java.util.Set; - -import static jdk.internal.org.objectweb.asm.Opcodes.*; - -/** - * Lambda metafactory implementation which dynamically creates an - * inner-class-like class per lambda callsite. - * - * @see LambdaMetafactory - */ -/* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory { - private static final Unsafe UNSAFE = Unsafe.getUnsafe(); - - private static final int CLASSFILE_VERSION = 52; - private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE); - private static final String JAVA_LANG_OBJECT = "java/lang/Object"; - private static final String NAME_CTOR = ""; - private static final String NAME_FACTORY = "get$Lambda"; - - //Serialization support - private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda"; - private static final String NAME_NOT_SERIALIZABLE_EXCEPTION = "java/io/NotSerializableException"; - private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;"; - private static final String DESCR_METHOD_WRITE_OBJECT = "(Ljava/io/ObjectOutputStream;)V"; - private static final String DESCR_METHOD_READ_OBJECT = "(Ljava/io/ObjectInputStream;)V"; - private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace"; - private static final String NAME_METHOD_READ_OBJECT = "readObject"; - private static final String NAME_METHOD_WRITE_OBJECT = "writeObject"; - private static final String DESCR_CTOR_SERIALIZED_LAMBDA - = MethodType.methodType(void.class, - Class.class, - String.class, String.class, String.class, - int.class, String.class, String.class, String.class, - String.class, - Object[].class).toMethodDescriptorString(); - private static final String DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION - = MethodType.methodType(void.class, String.class).toMethodDescriptorString(); - private static final String[] SER_HOSTILE_EXCEPTIONS = new String[] {NAME_NOT_SERIALIZABLE_EXCEPTION}; - - - private static final String[] EMPTY_STRING_ARRAY = new String[0]; - - // Used to ensure that each spun class name is unique - private static final AtomicInteger counter = new AtomicInteger(0); - - - // See context values in AbstractValidatingLambdaMetafactory - private final String implMethodClassName; // Name of type containing implementation "CC" - private final String implMethodName; // Name of implementation method "impl" - private final String implMethodDesc; // Type descriptor for implementation methods "(I)Ljava/lang/String;" - private final Class implMethodReturnClass; // class for implementaion method return type "Ljava/lang/String;" - private final MethodType constructorType; // Generated class constructor type "(CC)void" - private final ClassWriter cw; // ASM class writer - private final String[] argNames; // Generated names for the constructor arguments - private final String[] argDescs; // Type descriptors for the constructor arguments - private final String lambdaClassName; // Generated name for the generated class "X$$Lambda$1" - - /** - * General meta-factory constructor, supporting both standard cases and - * allowing for uncommon options such as serialization or bridging. - * - * @param caller Stacked automatically by VM; represents a lookup context - * with the accessibility privileges of the caller. - * @param invokedType Stacked automatically by VM; the signature of the - * invoked method, which includes the expected static - * type of the returned lambda object, and the static - * types of the captured arguments for the lambda. In - * the event that the implementation method is an - * instance method, the first argument in the invocation - * signature will correspond to the receiver. - * @param samMethodName Name of the method in the functional interface to - * which the lambda or method reference is being - * converted, represented as a String. - * @param samMethodType Type of the method in the functional interface to - * which the lambda or method reference is being - * converted, represented as a MethodType. - * @param implMethod The implementation method which should be called (with - * suitable adaptation of argument types, return types, - * and adjustment for captured arguments) when methods of - * the resulting functional interface instance are invoked. - * @param instantiatedMethodType The signature of the primary functional - * interface method after type variables are - * substituted with their instantiation from - * the capture site - * @param isSerializable Should the lambda be made serializable? If set, - * either the target type or one of the additional SAM - * types must extend {@code Serializable}. - * @param markerInterfaces Additional interfaces which the lambda object - * should implement. - * @param additionalBridges Method types for additional signatures to be - * bridged to the implementation method - * @throws LambdaConversionException If any of the meta-factory protocol - * invariants are violated - */ - public InnerClassLambdaMetafactory(MethodHandles.Lookup caller, - MethodType invokedType, - String samMethodName, - MethodType samMethodType, - MethodHandle implMethod, - MethodType instantiatedMethodType, - boolean isSerializable, - Class[] markerInterfaces, - MethodType[] additionalBridges) - throws LambdaConversionException { - super(caller, invokedType, samMethodName, samMethodType, - implMethod, instantiatedMethodType, - isSerializable, markerInterfaces, additionalBridges); - implMethodClassName = implDefiningClass.getName().replace('.', '/'); - implMethodName = implInfo.getName(); - implMethodDesc = implMethodType.toMethodDescriptorString(); - implMethodReturnClass = (implKind == MethodHandleInfo.REF_newInvokeSpecial) - ? implDefiningClass - : implMethodType.returnType(); - constructorType = invokedType.changeReturnType(Void.TYPE); - lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet(); - cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); - int parameterCount = invokedType.parameterCount(); - if (parameterCount > 0) { - argNames = new String[parameterCount]; - argDescs = new String[parameterCount]; - for (int i = 0; i < parameterCount; i++) { - argNames[i] = "arg$" + (i + 1); - argDescs[i] = BytecodeDescriptor.unparse(invokedType.parameterType(i)); - } - } else { - argNames = argDescs = EMPTY_STRING_ARRAY; - } - } - - /** - * Build the CallSite. Generate a class file which implements the functional - * interface, define the class, if there are no parameters create an instance - * of the class which the CallSite will return, otherwise, generate handles - * which will call the class' constructor. - * - * @return a CallSite, which, when invoked, will return an instance of the - * functional interface - * @throws ReflectiveOperationException - * @throws LambdaConversionException If properly formed functional interface - * is not found - */ - @Override - CallSite buildCallSite() throws LambdaConversionException { - final Class innerClass = spinInnerClass(); - if (invokedType.parameterCount() == 0) { - final Constructor[] ctrs = AccessController.doPrivileged( - new PrivilegedAction() { - @Override - public Constructor[] run() { - Constructor[] ctrs = innerClass.getDeclaredConstructors(); - if (ctrs.length == 1) { - // The lambda implementing inner class constructor is private, set - // it accessible (by us) before creating the constant sole instance - ctrs[0].setAccessible(true); - } - return ctrs; - } - }); - if (ctrs.length != 1) { - throw new LambdaConversionException("Expected one lambda constructor for " - + innerClass.getCanonicalName() + ", got " + ctrs.length); - } - - try { - Object inst = ctrs[0].newInstance(); - return new ConstantCallSite(MethodHandles.constant(samBase, inst)); - } - catch (ReflectiveOperationException e) { - throw new LambdaConversionException("Exception instantiating lambda object", e); - } - } else { - try { - UNSAFE.ensureClassInitialized(innerClass); - return new ConstantCallSite( - MethodHandles.Lookup.IMPL_LOOKUP - .findStatic(innerClass, NAME_FACTORY, invokedType)); - } - catch (ReflectiveOperationException e) { - throw new LambdaConversionException("Exception finding constructor", e); - } - } - } - - /** - * Generate a class file which implements the functional - * interface, define and return the class. - * - * @implNote The class that is generated does not include signature - * information for exceptions that may be present on the SAM method. - * This is to reduce classfile size, and is harmless as checked exceptions - * are erased anyway, no one will ever compile against this classfile, - * and we make no guarantees about the reflective properties of lambda - * objects. - * - * @return a Class which implements the functional interface - * @throws LambdaConversionException If properly formed functional interface - * is not found - */ - private Class spinInnerClass() throws LambdaConversionException { - String[] interfaces; - String samIntf = samBase.getName().replace('.', '/'); - boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(samBase); - if (markerInterfaces.length == 0) { - interfaces = new String[]{samIntf}; - } else { - // Assure no duplicate interfaces (ClassFormatError) - Set itfs = new LinkedHashSet<>(markerInterfaces.length + 1); - itfs.add(samIntf); - for (Class markerInterface : markerInterfaces) { - itfs.add(markerInterface.getName().replace('.', '/')); - accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(markerInterface); - } - interfaces = itfs.toArray(new String[itfs.size()]); - } - - cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC, - lambdaClassName, null, - JAVA_LANG_OBJECT, interfaces); - - // Generate final fields to be filled in by constructor - for (int i = 0; i < argDescs.length; i++) { - FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL, - argNames[i], - argDescs[i], - null, null); - fv.visitEnd(); - } - - generateConstructor(); - - if (invokedType.parameterCount() != 0) { - generateFactory(); - } - - // Forward the SAM method - MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName, - samMethodType.toMethodDescriptorString(), null, null); - new ForwardingMethodGenerator(mv).generate(samMethodType); - - // Forward the bridges - if (additionalBridges != null) { - for (MethodType mt : additionalBridges) { - mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName, - mt.toMethodDescriptorString(), null, null); - new ForwardingMethodGenerator(mv).generate(mt); - } - } - - if (isSerializable) - generateSerializationFriendlyMethods(); - else if (accidentallySerializable) - generateSerializationHostileMethods(); - - cw.visitEnd(); - - // Define the generated class in this VM. - - final byte[] classBytes = cw.toByteArray(); - - // If requested, dump out to a file for debugging purposes - if (dumper != null) { - AccessController.doPrivileged(new PrivilegedAction() { - @Override - public Void run() { - dumper.dumpClass(lambdaClassName, classBytes); - return null; - } - }, null, - new FilePermission("<>", "read, write"), - // createDirectories may need it - new PropertyPermission("user.dir", "read")); - } - - return UNSAFE.defineAnonymousClass(targetClass, classBytes, null); - } - - /** - * Generate the factory method for the class - */ - private void generateFactory() { - MethodVisitor m = cw.visitMethod(ACC_PRIVATE | ACC_STATIC, NAME_FACTORY, invokedType.toMethodDescriptorString(), null, null); - m.visitCode(); - m.visitTypeInsn(NEW, lambdaClassName); - m.visitInsn(Opcodes.DUP); - int parameterCount = invokedType.parameterCount(); - for (int typeIndex = 0, varIndex = 0; typeIndex < parameterCount; typeIndex++) { - Class argType = invokedType.parameterType(typeIndex); - m.visitVarInsn(getLoadOpcode(argType), varIndex); - varIndex += getParameterSize(argType); - } - m.visitMethodInsn(INVOKESPECIAL, lambdaClassName, NAME_CTOR, constructorType.toMethodDescriptorString()); - m.visitInsn(ARETURN); - m.visitMaxs(-1, -1); - m.visitEnd(); - } - - /** - * Generate the constructor for the class - */ - private void generateConstructor() { - // Generate constructor - MethodVisitor ctor = cw.visitMethod(ACC_PRIVATE, NAME_CTOR, - constructorType.toMethodDescriptorString(), null, null); - ctor.visitCode(); - ctor.visitVarInsn(ALOAD, 0); - ctor.visitMethodInsn(INVOKESPECIAL, JAVA_LANG_OBJECT, NAME_CTOR, - METHOD_DESCRIPTOR_VOID); - int parameterCount = invokedType.parameterCount(); - for (int i = 0, lvIndex = 0; i < parameterCount; i++) { - ctor.visitVarInsn(ALOAD, 0); - Class argType = invokedType.parameterType(i); - ctor.visitVarInsn(getLoadOpcode(argType), lvIndex + 1); - lvIndex += getParameterSize(argType); - ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i], argDescs[i]); - } - ctor.visitInsn(RETURN); - // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored - ctor.visitMaxs(-1, -1); - ctor.visitEnd(); - } - - /** - * Generate a writeReplace method that supports serialization - */ - private void generateSerializationFriendlyMethods() { - TypeConvertingMethodAdapter mv - = new TypeConvertingMethodAdapter( - cw.visitMethod(ACC_PRIVATE + ACC_FINAL, - NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE, - null, null)); - - mv.visitCode(); - mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA); - mv.visitInsn(DUP); - mv.visitLdcInsn(Type.getType(targetClass)); - mv.visitLdcInsn(invokedType.returnType().getName().replace('.', '/')); - mv.visitLdcInsn(samMethodName); - mv.visitLdcInsn(samMethodType.toMethodDescriptorString()); - mv.visitLdcInsn(implInfo.getReferenceKind()); - mv.visitLdcInsn(implInfo.getDeclaringClass().getName().replace('.', '/')); - mv.visitLdcInsn(implInfo.getName()); - mv.visitLdcInsn(implInfo.getMethodType().toMethodDescriptorString()); - mv.visitLdcInsn(instantiatedMethodType.toMethodDescriptorString()); - mv.iconst(argDescs.length); - mv.visitTypeInsn(ANEWARRAY, JAVA_LANG_OBJECT); - for (int i = 0; i < argDescs.length; i++) { - mv.visitInsn(DUP); - mv.iconst(i); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]); - mv.boxIfTypePrimitive(Type.getType(argDescs[i])); - mv.visitInsn(AASTORE); - } - mv.visitMethodInsn(INVOKESPECIAL, NAME_SERIALIZED_LAMBDA, NAME_CTOR, - DESCR_CTOR_SERIALIZED_LAMBDA); - mv.visitInsn(ARETURN); - // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored - mv.visitMaxs(-1, -1); - mv.visitEnd(); - } - - /** - * Generate a readObject/writeObject method that is hostile to serialization - */ - private void generateSerializationHostileMethods() { - MethodVisitor mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL, - NAME_METHOD_WRITE_OBJECT, DESCR_METHOD_WRITE_OBJECT, - null, SER_HOSTILE_EXCEPTIONS); - mv.visitCode(); - mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION); - mv.visitInsn(DUP); - mv.visitLdcInsn("Non-serializable lambda"); - mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR, - DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION); - mv.visitInsn(ATHROW); - mv.visitMaxs(-1, -1); - mv.visitEnd(); - - mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL, - NAME_METHOD_READ_OBJECT, DESCR_METHOD_READ_OBJECT, - null, SER_HOSTILE_EXCEPTIONS); - mv.visitCode(); - mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION); - mv.visitInsn(DUP); - mv.visitLdcInsn("Non-serializable lambda"); - mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR, - DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION); - mv.visitInsn(ATHROW); - mv.visitMaxs(-1, -1); - mv.visitEnd(); - } - - /** - * This class generates a method body which calls the lambda implementation - * method, converting arguments, as needed. - */ - private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter { - - ForwardingMethodGenerator(MethodVisitor mv) { - super(mv); - } - - void generate(MethodType methodType) { - visitCode(); - - if (implKind == MethodHandleInfo.REF_newInvokeSpecial) { - visitTypeInsn(NEW, implMethodClassName); - visitInsn(DUP); - } - for (int i = 0; i < argNames.length; i++) { - visitVarInsn(ALOAD, 0); - visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]); - } - - convertArgumentTypes(methodType); - - // Invoke the method we want to forward to - visitMethodInsn(invocationOpcode(), implMethodClassName, - implMethodName, implMethodDesc, - implDefiningClass.isInterface()); - - // Convert the return value (if any) and return it - // Note: if adapting from non-void to void, the 'return' - // instruction will pop the unneeded result - Class samReturnClass = methodType.returnType(); - convertType(implMethodReturnClass, samReturnClass, samReturnClass); - visitInsn(getReturnOpcode(samReturnClass)); - // Maxs computed by ClassWriter.COMPUTE_MAXS,these arguments ignored - visitMaxs(-1, -1); - visitEnd(); - } - - private void convertArgumentTypes(MethodType samType) { - int lvIndex = 0; - boolean samIncludesReceiver = implIsInstanceMethod && - invokedType.parameterCount() == 0; - int samReceiverLength = samIncludesReceiver ? 1 : 0; - if (samIncludesReceiver) { - // push receiver - Class rcvrType = samType.parameterType(0); - visitVarInsn(getLoadOpcode(rcvrType), lvIndex + 1); - lvIndex += getParameterSize(rcvrType); - convertType(rcvrType, implDefiningClass, instantiatedMethodType.parameterType(0)); - } - int samParametersLength = samType.parameterCount(); - int argOffset = implMethodType.parameterCount() - samParametersLength; - for (int i = samReceiverLength; i < samParametersLength; i++) { - Class argType = samType.parameterType(i); - visitVarInsn(getLoadOpcode(argType), lvIndex + 1); - lvIndex += getParameterSize(argType); - convertType(argType, implMethodType.parameterType(argOffset + i), instantiatedMethodType.parameterType(i)); - } - } - - private int invocationOpcode() throws InternalError { - switch (implKind) { - case MethodHandleInfo.REF_invokeStatic: - return INVOKESTATIC; - case MethodHandleInfo.REF_newInvokeSpecial: - return INVOKESPECIAL; - case MethodHandleInfo.REF_invokeVirtual: - return INVOKEVIRTUAL; - case MethodHandleInfo.REF_invokeInterface: - return INVOKEINTERFACE; - case MethodHandleInfo.REF_invokeSpecial: - return INVOKESPECIAL; - default: - throw new InternalError("Unexpected invocation kind: " + implKind); - } - } - } - - static int getParameterSize(Class c) { - if (c == Void.TYPE) { - return 0; - } else if (c == Long.TYPE || c == Double.TYPE) { - return 2; - } - return 1; - } - - static int getLoadOpcode(Class c) { - if(c == Void.TYPE) { - throw new InternalError("Unexpected void type of load opcode"); - } - return ILOAD + getOpcodeOffset(c); - } - - static int getReturnOpcode(Class c) { - if(c == Void.TYPE) { - return RETURN; - } - return IRETURN + getOpcodeOffset(c); - } - - private static int getOpcodeOffset(Class c) { - if (c.isPrimitive()) { - if (c == Long.TYPE) { - return 1; - } else if (c == Float.TYPE) { - return 2; - } else if (c == Double.TYPE) { - return 3; - } - return 0; - } else { - return 4; - } - } - -} diff -r 8fb89a569621 -r bd151459ee4f rt/emul/compact/src/main/java/java/lang/invoke/InvokerBytecodeGenerator.java --- a/rt/emul/compact/src/main/java/java/lang/invoke/InvokerBytecodeGenerator.java Sun Aug 10 06:21:50 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1052 +0,0 @@ -/* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * 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. - */ - -package java.lang.invoke; - -import sun.invoke.util.VerifyAccess; -import java.lang.invoke.LambdaForm.Name; -import java.lang.invoke.MethodHandles.Lookup; - -import sun.invoke.util.Wrapper; - -import java.io.*; -import java.util.*; - -import jdk.internal.org.objectweb.asm.*; - -import java.lang.reflect.*; -import static java.lang.invoke.MethodHandleStatics.*; -import static java.lang.invoke.MethodHandleNatives.Constants.*; -import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; -import sun.invoke.util.ValueConversions; -import sun.invoke.util.VerifyType; - -/** - * Code generation backend for LambdaForm. - *

- * @author John Rose, JSR 292 EG - */ -class InvokerBytecodeGenerator { - /** Define class names for convenience. */ - private static final String MH = "java/lang/invoke/MethodHandle"; - private static final String BMH = "java/lang/invoke/BoundMethodHandle"; - private static final String LF = "java/lang/invoke/LambdaForm"; - private static final String LFN = "java/lang/invoke/LambdaForm$Name"; - private static final String CLS = "java/lang/Class"; - private static final String OBJ = "java/lang/Object"; - private static final String OBJARY = "[Ljava/lang/Object;"; - - private static final String LF_SIG = "L" + LF + ";"; - private static final String LFN_SIG = "L" + LFN + ";"; - private static final String LL_SIG = "(L" + OBJ + ";)L" + OBJ + ";"; - - /** Name of its super class*/ - private static final String superName = LF; - - /** Name of new class */ - private final String className; - - /** Name of the source file (for stack trace printing). */ - private final String sourceFile; - - private final LambdaForm lambdaForm; - private final String invokerName; - private final MethodType invokerType; - private final int[] localsMap; - - /** ASM bytecode generation. */ - private ClassWriter cw; - private MethodVisitor mv; - - private static final MemberName.Factory MEMBERNAME_FACTORY = MemberName.getFactory(); - private static final Class HOST_CLASS = LambdaForm.class; - - private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize, - String className, String invokerName, MethodType invokerType) { - if (invokerName.contains(".")) { - int p = invokerName.indexOf("."); - className = invokerName.substring(0, p); - invokerName = invokerName.substring(p+1); - } - if (DUMP_CLASS_FILES) { - className = makeDumpableClassName(className); - } - this.className = superName + "$" + className; - this.sourceFile = "LambdaForm$" + className; - this.lambdaForm = lambdaForm; - this.invokerName = invokerName; - this.invokerType = invokerType; - this.localsMap = new int[localsMapSize]; - } - - private InvokerBytecodeGenerator(String className, String invokerName, MethodType invokerType) { - this(null, invokerType.parameterCount(), - className, invokerName, invokerType); - // Create an array to map name indexes to locals indexes. - for (int i = 0; i < localsMap.length; i++) { - localsMap[i] = invokerType.parameterSlotCount() - invokerType.parameterSlotDepth(i); - } - } - - private InvokerBytecodeGenerator(String className, LambdaForm form, MethodType invokerType) { - this(form, form.names.length, - className, form.debugName, invokerType); - // Create an array to map name indexes to locals indexes. - Name[] names = form.names; - for (int i = 0, index = 0; i < localsMap.length; i++) { - localsMap[i] = index; - index += Wrapper.forBasicType(names[i].type).stackSlots(); - } - } - - - /** instance counters for dumped classes */ - private final static HashMap DUMP_CLASS_FILES_COUNTERS; - /** debugging flag for saving generated class files */ - private final static File DUMP_CLASS_FILES_DIR; - - static { - if (DUMP_CLASS_FILES) { - DUMP_CLASS_FILES_COUNTERS = new HashMap<>(); - try { - File dumpDir = new File("DUMP_CLASS_FILES"); - if (!dumpDir.exists()) { - dumpDir.mkdirs(); - } - DUMP_CLASS_FILES_DIR = dumpDir; - System.out.println("Dumping class files to "+DUMP_CLASS_FILES_DIR+"/..."); - } catch (Exception e) { - throw newInternalError(e); - } - } else { - DUMP_CLASS_FILES_COUNTERS = null; - DUMP_CLASS_FILES_DIR = null; - } - } - - static void maybeDump(final String className, final byte[] classFile) { - if (DUMP_CLASS_FILES) { - System.out.println("dump: " + className); - java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction() { - public Void run() { - try { - String dumpName = className; - //dumpName = dumpName.replace('/', '-'); - File dumpFile = new File(DUMP_CLASS_FILES_DIR, dumpName+".class"); - dumpFile.getParentFile().mkdirs(); - FileOutputStream file = new FileOutputStream(dumpFile); - file.write(classFile); - file.close(); - return null; - } catch (IOException ex) { - throw newInternalError(ex); - } - } - }); - } - - } - - private static String makeDumpableClassName(String className) { - Integer ctr; - synchronized (DUMP_CLASS_FILES_COUNTERS) { - ctr = DUMP_CLASS_FILES_COUNTERS.get(className); - if (ctr == null) ctr = 0; - DUMP_CLASS_FILES_COUNTERS.put(className, ctr+1); - } - String sfx = ctr.toString(); - while (sfx.length() < 3) - sfx = "0"+sfx; - className += sfx; - return className; - } - - class CpPatch { - final int index; - final String placeholder; - final Object value; - CpPatch(int index, String placeholder, Object value) { - this.index = index; - this.placeholder = placeholder; - this.value = value; - } - public String toString() { - return "CpPatch/index="+index+",placeholder="+placeholder+",value="+value; - } - } - - Map cpPatches = new HashMap<>(); - - int cph = 0; // for counting constant placeholders - - String constantPlaceholder(Object arg) { - String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + cph++; - if (DUMP_CLASS_FILES) cpPlaceholder += " <<" + arg.toString() + ">>"; // debugging aid - if (cpPatches.containsKey(cpPlaceholder)) { - throw new InternalError("observed CP placeholder twice: " + cpPlaceholder); - } - // insert placeholder in CP and remember the patch - int index = cw.newConst((Object) cpPlaceholder); // TODO check if aready in the constant pool - cpPatches.put(cpPlaceholder, new CpPatch(index, cpPlaceholder, arg)); - return cpPlaceholder; - } - - Object[] cpPatches(byte[] classFile) { - int size = getConstantPoolSize(classFile); - Object[] res = new Object[size]; - for (CpPatch p : cpPatches.values()) { - if (p.index >= size) - throw new InternalError("in cpool["+size+"]: "+p+"\n"+Arrays.toString(Arrays.copyOf(classFile, 20))); - res[p.index] = p.value; - } - 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. - */ - private static int getConstantPoolSize(byte[] classFile) { - // The first few bytes: - // u4 magic; - // u2 minor_version; - // u2 major_version; - // u2 constant_pool_count; - return ((classFile[8] & 0xFF) << 8) | (classFile[9] & 0xFF); - } - - /** - * Extract the MemberName of a newly-defined method. - */ - private MemberName loadMethod(byte[] classFile) { - Class invokerClass = loadAndInitializeInvokerClass(classFile, cpPatches(classFile)); - return resolveInvokerMember(invokerClass, invokerName, invokerType); - } - - /** - * Define a given class as anonymous class in the runtime system. - */ - private static Class loadAndInitializeInvokerClass(byte[] classBytes, Object[] patches) { - Class invokerClass = UNSAFE.defineAnonymousClass(HOST_CLASS, classBytes, patches); - UNSAFE.ensureClassInitialized(invokerClass); // Make sure the class is initialized; VM might complain. - return invokerClass; - } - - private static MemberName resolveInvokerMember(Class invokerClass, String name, MethodType type) { - MemberName member = new MemberName(invokerClass, name, type, REF_invokeStatic); - //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 = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES); - cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, className, null, superName, null); - cw.visitSource(sourceFile, null); - - String invokerDesc = invokerType.toMethodDescriptorString(); - mv = cw.visitMethod(Opcodes.ACC_STATIC, invokerName, invokerDesc, null, null); - } - - /** - * Tear down class file generation. - */ - private void classFileEpilogue() { - mv.visitMaxs(0, 0); - mv.visitEnd(); - } - - /* - * Low-level emit helpers. - */ - private void emitConst(Object con) { - if (con == null) { - mv.visitInsn(Opcodes.ACONST_NULL); - return; - } - if (con instanceof Integer) { - emitIconstInsn((int) con); - return; - } - if (con instanceof Long) { - long x = (long) con; - if (x == (short) x) { - emitIconstInsn((int) x); - mv.visitInsn(Opcodes.I2L); - return; - } - } - if (con instanceof Float) { - float x = (float) con; - if (x == (short) x) { - emitIconstInsn((int) x); - mv.visitInsn(Opcodes.I2F); - return; - } - } - if (con instanceof Double) { - double x = (double) con; - if (x == (short) x) { - emitIconstInsn((int) x); - mv.visitInsn(Opcodes.I2D); - return; - } - } - if (con instanceof Boolean) { - emitIconstInsn((boolean) con ? 1 : 0); - return; - } - // fall through: - mv.visitLdcInsn(con); - } - - private void emitIconstInsn(int i) { - int opcode; - switch (i) { - case 0: opcode = Opcodes.ICONST_0; break; - case 1: opcode = Opcodes.ICONST_1; break; - case 2: opcode = Opcodes.ICONST_2; break; - case 3: opcode = Opcodes.ICONST_3; break; - case 4: opcode = Opcodes.ICONST_4; break; - case 5: opcode = Opcodes.ICONST_5; break; - default: - if (i == (byte) i) { - mv.visitIntInsn(Opcodes.BIPUSH, i & 0xFF); - } else if (i == (short) i) { - mv.visitIntInsn(Opcodes.SIPUSH, (char) i); - } else { - mv.visitLdcInsn(i); - } - return; - } - mv.visitInsn(opcode); - } - - /* - * NOTE: These load/store methods use the localsMap to find the correct index! - */ - private void emitLoadInsn(char type, int index) { - int opcode; - switch (type) { - case 'I': opcode = Opcodes.ILOAD; break; - case 'J': opcode = Opcodes.LLOAD; break; - case 'F': opcode = Opcodes.FLOAD; break; - case 'D': opcode = Opcodes.DLOAD; break; - case 'L': opcode = Opcodes.ALOAD; break; - default: - throw new InternalError("unknown type: " + type); - } - mv.visitVarInsn(opcode, localsMap[index]); - } - private void emitAloadInsn(int index) { - emitLoadInsn('L', index); - } - - private void emitStoreInsn(char type, int index) { - int opcode; - switch (type) { - case 'I': opcode = Opcodes.ISTORE; break; - case 'J': opcode = Opcodes.LSTORE; break; - case 'F': opcode = Opcodes.FSTORE; break; - case 'D': opcode = Opcodes.DSTORE; break; - case 'L': opcode = Opcodes.ASTORE; break; - default: - throw new InternalError("unknown type: " + type); - } - mv.visitVarInsn(opcode, localsMap[index]); - } - private void emitAstoreInsn(int index) { - emitStoreInsn('L', index); - } - - /** - * Emit a boxing call. - * - * @param type primitive type class to box. - */ - private void emitBoxing(Class type) { - Wrapper wrapper = Wrapper.forPrimitiveType(type); - String owner = "java/lang/" + wrapper.wrapperType().getSimpleName(); - String name = "valueOf"; - String desc = "(" + wrapper.basicTypeChar() + ")L" + owner + ";"; - mv.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc); - } - - /** - * Emit an unboxing call (plus preceding checkcast). - * - * @param type wrapper type class to unbox. - */ - private void emitUnboxing(Class type) { - Wrapper wrapper = Wrapper.forWrapperType(type); - String owner = "java/lang/" + wrapper.wrapperType().getSimpleName(); - String name = wrapper.primitiveSimpleName() + "Value"; - String desc = "()" + wrapper.basicTypeChar(); - mv.visitTypeInsn(Opcodes.CHECKCAST, owner); - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc); - } - - /** - * Emit an implicit conversion. - * - * @param ptype type of value present on stack - * @param pclass type of value required on stack - */ - private void emitImplicitConversion(char ptype, Class pclass) { - switch (ptype) { - case 'L': - if (VerifyType.isNullConversion(Object.class, pclass)) - return; - if (isStaticallyNameable(pclass)) { - mv.visitTypeInsn(Opcodes.CHECKCAST, getInternalName(pclass)); - } else { - mv.visitLdcInsn(constantPlaceholder(pclass)); - mv.visitTypeInsn(Opcodes.CHECKCAST, CLS); - mv.visitInsn(Opcodes.SWAP); - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, CLS, "cast", LL_SIG); - if (pclass.isArray()) - mv.visitTypeInsn(Opcodes.CHECKCAST, OBJARY); - } - return; - case 'I': - if (!VerifyType.isNullConversion(int.class, pclass)) - emitPrimCast(ptype, Wrapper.basicTypeChar(pclass)); - return; - case 'J': - assert(pclass == long.class); - return; - case 'F': - assert(pclass == float.class); - return; - case 'D': - assert(pclass == double.class); - return; - } - throw new InternalError("bad implicit conversion: tc="+ptype+": "+pclass); - } - - /** - * Emits an actual return instruction conforming to the given return type. - */ - private void emitReturnInsn(Class type) { - int opcode; - switch (Wrapper.basicTypeChar(type)) { - case 'I': opcode = Opcodes.IRETURN; break; - case 'J': opcode = Opcodes.LRETURN; break; - case 'F': opcode = Opcodes.FRETURN; break; - case 'D': opcode = Opcodes.DRETURN; break; - case 'L': opcode = Opcodes.ARETURN; break; - case 'V': opcode = Opcodes.RETURN; break; - default: - throw new InternalError("unknown return type: " + type); - } - mv.visitInsn(opcode); - } - - private static String getInternalName(Class c) { - assert(VerifyAccess.isTypeVisible(c, Object.class)); - return c.getName().replace('.', '/'); - } - - /** - * Generate customized bytecode for a given LambdaForm. - */ - static MemberName generateCustomizedCode(LambdaForm form, MethodType invokerType) { - InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("MH", form, invokerType); - return g.loadMethod(g.generateCustomizedCodeBytes()); - } - - /** - * Generate an invoker method for the passed {@link LambdaForm}. - */ - private byte[] generateCustomizedCodeBytes() { - classFilePrologue(); - - // Suppress this method in backtraces displayed to the user. - mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true); - - // Mark this method as a compiled LambdaForm - mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Compiled;", true); - - // Force inlining of this invoker method. - mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true); - - // iterate over the form's names, generating bytecode instructions for each - // start iterating at the first name following the arguments - for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) { - Name name = lambdaForm.names[i]; - MemberName member = name.function.member(); - - if (isSelectAlternative(member)) { - // selectAlternative idiom - // FIXME: make sure this idiom is really present! - emitSelectAlternative(name, lambdaForm.names[i + 1]); - i++; // skip MH.invokeBasic of the selectAlternative result - } else if (isStaticallyInvocable(member)) { - emitStaticInvoke(member, name); - } else { - emitInvoke(name); - } - - // 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, - // avoid store/load/return and just return) - if (i == lambdaForm.names.length - 1 && i == lambdaForm.result) { - // return value - do nothing - } else if (name.type != 'V') { - // non-void: actually assign - emitStoreInsn(name.type, name.index()); - } - } - - // return statement - emitReturn(); - - classFileEpilogue(); - bogusMethod(lambdaForm); - - final byte[] classFile = cw.toByteArray(); - maybeDump(className, classFile); - return classFile; - } - - /** - * Emit an invoke for the given name. - */ - void emitInvoke(Name name) { - if (true) { - // push receiver - MethodHandle target = name.function.resolvedHandle; - assert(target != null) : name.exprString(); - mv.visitLdcInsn(constantPlaceholder(target)); - mv.visitTypeInsn(Opcodes.CHECKCAST, MH); - } else { - // load receiver - emitAloadInsn(0); - mv.visitTypeInsn(Opcodes.CHECKCAST, MH); - mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", LF_SIG); - mv.visitFieldInsn(Opcodes.GETFIELD, LF, "names", LFN_SIG); - // TODO more to come - } - - // push arguments - for (int i = 0; i < name.arguments.length; i++) { - emitPushArgument(name, i); - } - - // invocation - MethodType type = name.function.methodType(); - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString()); - } - - static private Class[] STATICALLY_INVOCABLE_PACKAGES = { - // Sample classes from each package we are willing to bind to statically: - java.lang.Object.class, - java.util.Arrays.class, - sun.misc.Unsafe.class - //MethodHandle.class already covered - }; - - static boolean isStaticallyInvocable(MemberName member) { - if (member == null) return false; - if (member.isConstructor()) return false; - Class cls = member.getDeclaringClass(); - if (cls.isArray() || cls.isPrimitive()) - return false; // FIXME - if (cls.isAnonymousClass() || cls.isLocalClass()) - return false; // inner class of some sort - if (cls.getClassLoader() != MethodHandle.class.getClassLoader()) - return false; // not on BCP - MethodType mtype = member.getMethodOrFieldType(); - if (!isStaticallyNameable(mtype.returnType())) - return false; - for (Class ptype : mtype.parameterArray()) - if (!isStaticallyNameable(ptype)) - return false; - if (!member.isPrivate() && VerifyAccess.isSamePackage(MethodHandle.class, cls)) - return true; // in java.lang.invoke package - if (member.isPublic() && isStaticallyNameable(cls)) - return true; - return false; - } - - static boolean isStaticallyNameable(Class cls) { - while (cls.isArray()) - cls = cls.getComponentType(); - if (cls.isPrimitive()) - return true; // int[].class, for example - // could use VerifyAccess.isClassAccessible but the following is a safe approximation - if (cls.getClassLoader() != Object.class.getClassLoader()) - return false; - if (VerifyAccess.isSamePackage(MethodHandle.class, cls)) - return true; - if (!Modifier.isPublic(cls.getModifiers())) - return false; - for (Class pkgcls : STATICALLY_INVOCABLE_PACKAGES) { - if (VerifyAccess.isSamePackage(pkgcls, cls)) - return true; - } - return false; - } - - /** - * Emit an invoke for the given name, using the MemberName directly. - */ - void emitStaticInvoke(MemberName member, Name name) { - assert(member.equals(name.function.member())); - String cname = getInternalName(member.getDeclaringClass()); - String mname = member.getName(); - String mtype; - byte refKind = member.getReferenceKind(); - if (refKind == REF_invokeSpecial) { - // in order to pass the verifier, we need to convert this to invokevirtual in all cases - assert(member.canBeStaticallyBound()) : member; - refKind = REF_invokeVirtual; - } - - if (member.getDeclaringClass().isInterface() && refKind == REF_invokeVirtual) { - // Methods from Object declared in an interface can be resolved by JVM to invokevirtual kind. - // Need to convert it back to invokeinterface to pass verification and make the invocation works as expected. - refKind = REF_invokeInterface; - } - - // push arguments - for (int i = 0; i < name.arguments.length; i++) { - emitPushArgument(name, i); - } - - // invocation - if (member.isMethod()) { - mtype = member.getMethodType().toMethodDescriptorString(); - mv.visitMethodInsn(refKindOpcode(refKind), cname, mname, mtype, - member.getDeclaringClass().isInterface()); - } else { - mtype = MethodType.toFieldDescriptorString(member.getFieldType()); - mv.visitFieldInsn(refKindOpcode(refKind), cname, mname, mtype); - } - } - int refKindOpcode(byte refKind) { - switch (refKind) { - case REF_invokeVirtual: return Opcodes.INVOKEVIRTUAL; - case REF_invokeStatic: return Opcodes.INVOKESTATIC; - case REF_invokeSpecial: return Opcodes.INVOKESPECIAL; - case REF_invokeInterface: return Opcodes.INVOKEINTERFACE; - case REF_getField: return Opcodes.GETFIELD; - case REF_putField: return Opcodes.PUTFIELD; - case REF_getStatic: return Opcodes.GETSTATIC; - case REF_putStatic: return Opcodes.PUTSTATIC; - } - throw new InternalError("refKind="+refKind); - } - - /** - * Check if MemberName is a call to MethodHandleImpl.selectAlternative. - */ - private boolean isSelectAlternative(MemberName member) { - return member != null && - member.getDeclaringClass() == MethodHandleImpl.class && - member.getName().equals("selectAlternative"); - } - - /** - * Emit bytecode for the selectAlternative idiom. - * - * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest): - *

{@code
-     *   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}
-     * }
- */ - private void emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) { - MethodType type = selectAlternativeName.function.methodType(); - - Name receiver = (Name) invokeBasicName.arguments[0]; - - Label L_fallback = new Label(); - Label L_done = new Label(); - - // load test result - emitPushArgument(selectAlternativeName, 0); - mv.visitInsn(Opcodes.ICONST_1); - - // if_icmpne L_fallback - mv.visitJumpInsn(Opcodes.IF_ICMPNE, L_fallback); - - // invoke selectAlternativeName.arguments[1] - MethodHandle target = (MethodHandle) selectAlternativeName.arguments[1]; - emitPushArgument(selectAlternativeName, 1); // get 2nd argument of selectAlternative - emitAstoreInsn(receiver.index()); // store the MH in the receiver slot - emitInvoke(invokeBasicName); - - // goto L_done - mv.visitJumpInsn(Opcodes.GOTO, L_done); - - // L_fallback: - mv.visitLabel(L_fallback); - - // invoke selectAlternativeName.arguments[2] - MethodHandle fallback = (MethodHandle) selectAlternativeName.arguments[2]; - emitPushArgument(selectAlternativeName, 2); // get 3rd argument of selectAlternative - emitAstoreInsn(receiver.index()); // store the MH in the receiver slot - emitInvoke(invokeBasicName); - - // L_done: - mv.visitLabel(L_done); - } - - private void emitPushArgument(Name name, int paramIndex) { - Object arg = name.arguments[paramIndex]; - char ptype = name.function.parameterType(paramIndex); - MethodType mtype = name.function.methodType(); - if (arg instanceof Name) { - Name n = (Name) arg; - emitLoadInsn(n.type, n.index()); - emitImplicitConversion(n.type, mtype.parameterType(paramIndex)); - } else if ((arg == null || arg instanceof String) && ptype == 'L') { - emitConst(arg); - } else { - if (Wrapper.isWrapperType(arg.getClass()) && ptype != 'L') { - emitConst(arg); - } else { - mv.visitLdcInsn(constantPlaceholder(arg)); - emitImplicitConversion('L', mtype.parameterType(paramIndex)); - } - } - } - - /** - * 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 - if (lambdaForm.result == -1) { - // void - mv.visitInsn(Opcodes.RETURN); - } else { - LambdaForm.Name rn = lambdaForm.names[lambdaForm.result]; - char rtype = Wrapper.basicTypeChar(invokerType.returnType()); - - // put return value on the stack if it is not already there - if (lambdaForm.result != lambdaForm.names.length - 1) { - emitLoadInsn(rn.type, lambdaForm.result); - } - - // 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 - if (rtype != rn.type) { - // need cast - if (rtype == 'L') { - // possibly cast the primitive to the correct type for boxing - char boxedType = Wrapper.forWrapperType(invokerType.returnType()).basicTypeChar(); - if (boxedType != rn.type) { - emitPrimCast(rn.type, boxedType); - } - // cast primitive to reference ("boxing") - emitBoxing(invokerType.returnType()); - } else { - // to-primitive cast - if (rn.type != 'L') { - // prim-to-prim cast - emitPrimCast(rn.type, rtype); - } else { - // ref-to-prim cast ("unboxing") - throw new InternalError("no ref-to-prim (unboxing) casts supported right now"); - } - } - } - - // generate actual return statement - emitReturnInsn(invokerType.returnType()); - } - } - - /** - * Emit a type conversion bytecode casting from "from" to "to". - */ - private void emitPrimCast(char from, char 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 <-> - if (from == to) { - // no cast required, should be dead code anyway - return; - } - Wrapper wfrom = Wrapper.forBasicType(from); - Wrapper wto = Wrapper.forBasicType(to); - if (wfrom.isSubwordOrInt()) { - // cast from {byte,short,char,int} to anything - emitI2X(to); - } else { - // cast from {long,float,double} to anything - if (wto.isSubwordOrInt()) { - // cast to {byte,short,char,int} - emitX2I(from); - if (wto.bitWidth() < 32) { - // targets other than int require another conversion - emitI2X(to); - } - } else { - // cast to {long,float,double} - this is verbose - boolean error = false; - switch (from) { - case 'J': - if (to == 'F') { mv.visitInsn(Opcodes.L2F); } - else if (to == 'D') { mv.visitInsn(Opcodes.L2D); } - else error = true; - break; - case 'F': - if (to == 'J') { mv.visitInsn(Opcodes.F2L); } - else if (to == 'D') { mv.visitInsn(Opcodes.F2D); } - else error = true; - break; - case 'D': - if (to == 'J') { mv.visitInsn(Opcodes.D2L); } - else if (to == 'F') { mv.visitInsn(Opcodes.D2F); } - else error = true; - break; - default: - error = true; - break; - } - if (error) { - throw new IllegalStateException("unhandled prim cast: " + from + "2" + to); - } - } - } - } - - private void emitI2X(char type) { - switch (type) { - case 'B': mv.visitInsn(Opcodes.I2B); break; - case 'S': mv.visitInsn(Opcodes.I2S); break; - case 'C': mv.visitInsn(Opcodes.I2C); break; - case 'I': /* naught */ break; - case 'J': mv.visitInsn(Opcodes.I2L); break; - case 'F': mv.visitInsn(Opcodes.I2F); break; - case 'D': mv.visitInsn(Opcodes.I2D); break; - case 'Z': - // For compatibility with ValueConversions and explicitCastArguments: - mv.visitInsn(Opcodes.ICONST_1); - mv.visitInsn(Opcodes.IAND); - break; - default: throw new InternalError("unknown type: " + type); - } - } - - private void emitX2I(char type) { - switch (type) { - case 'J': mv.visitInsn(Opcodes.L2I); break; - case 'F': mv.visitInsn(Opcodes.F2I); break; - case 'D': mv.visitInsn(Opcodes.D2I); break; - default: throw new InternalError("unknown type: " + type); - } - } - - private static String basicTypeCharSignature(String prefix, MethodType type) { - StringBuilder buf = new StringBuilder(prefix); - for (Class ptype : type.parameterList()) - buf.append(Wrapper.forBasicType(ptype).basicTypeChar()); - buf.append('_').append(Wrapper.forBasicType(type.returnType()).basicTypeChar()); - return buf.toString(); - } - - /** - * Generate bytecode for a LambdaForm.vmentry which calls interpretWithArguments. - */ - static MemberName generateLambdaFormInterpreterEntryPoint(String sig) { - assert(LambdaForm.isValidSignature(sig)); - //System.out.println("generateExactInvoker "+sig); - // compute method type - // first parameter and return type - char tret = LambdaForm.signatureReturn(sig); - MethodType type = MethodType.methodType(LambdaForm.typeClass(tret), MethodHandle.class); - // other parameter types - int arity = LambdaForm.signatureArity(sig); - for (int i = 1; i < arity; i++) { - type = type.appendParameterTypes(LambdaForm.typeClass(sig.charAt(i))); - } - InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("LFI", "interpret_"+tret, type); - return g.loadMethod(g.generateLambdaFormInterpreterEntryPointBytes()); - } - - private byte[] generateLambdaFormInterpreterEntryPointBytes() { - classFilePrologue(); - - // Suppress this method in backtraces displayed to the user. - mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true); - - // Don't inline the interpreter entry. - mv.visitAnnotation("Ljava/lang/invoke/DontInline;", true); - - // create parameter array - emitIconstInsn(invokerType.parameterCount()); - mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); - - // fill parameter array - for (int i = 0; i < invokerType.parameterCount(); i++) { - Class ptype = invokerType.parameterType(i); - mv.visitInsn(Opcodes.DUP); - emitIconstInsn(i); - emitLoadInsn(Wrapper.basicTypeChar(ptype), i); - // box if primitive type - if (ptype.isPrimitive()) { - emitBoxing(ptype); - } - mv.visitInsn(Opcodes.AASTORE); - } - // invoke - emitAloadInsn(0); - mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", "Ljava/lang/invoke/LambdaForm;"); - mv.visitInsn(Opcodes.SWAP); // swap form and array; avoid local variable - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, LF, "interpretWithArguments", "([Ljava/lang/Object;)Ljava/lang/Object;"); - - // maybe unbox - Class rtype = invokerType.returnType(); - if (rtype.isPrimitive() && rtype != void.class) { - emitUnboxing(Wrapper.asWrapperType(rtype)); - } - - // return statement - emitReturnInsn(rtype); - - classFileEpilogue(); - bogusMethod(invokerType); - - final byte[] classFile = cw.toByteArray(); - maybeDump(className, classFile); - return classFile; - } - - /** - * Generate bytecode for a NamedFunction invoker. - */ - static MemberName generateNamedFunctionInvoker(MethodTypeForm typeForm) { - MethodType invokerType = LambdaForm.NamedFunction.INVOKER_METHOD_TYPE; - String invokerName = basicTypeCharSignature("invoke_", typeForm.erasedType()); - InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("NFI", invokerName, invokerType); - return g.loadMethod(g.generateNamedFunctionInvokerImpl(typeForm)); - } - - static int nfi = 0; - - private byte[] generateNamedFunctionInvokerImpl(MethodTypeForm typeForm) { - MethodType dstType = typeForm.erasedType(); - classFilePrologue(); - - // Suppress this method in backtraces displayed to the user. - mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true); - - // Force inlining of this invoker method. - mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true); - - // Load receiver - emitAloadInsn(0); - - // Load arguments from array - for (int i = 0; i < dstType.parameterCount(); i++) { - emitAloadInsn(1); - emitIconstInsn(i); - mv.visitInsn(Opcodes.AALOAD); - - // Maybe unbox - Class dptype = dstType.parameterType(i); - if (dptype.isPrimitive()) { - Class sptype = dstType.basicType().wrap().parameterType(i); - Wrapper dstWrapper = Wrapper.forBasicType(dptype); - Wrapper srcWrapper = dstWrapper.isSubwordOrInt() ? Wrapper.INT : dstWrapper; // narrow subword from int - emitUnboxing(srcWrapper.wrapperType()); - emitPrimCast(srcWrapper.basicTypeChar(), dstWrapper.basicTypeChar()); - } - } - - // Invoke - String targetDesc = dstType.basicType().toMethodDescriptorString(); - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", targetDesc); - - // Box primitive types - Class rtype = dstType.returnType(); - if (rtype != void.class && rtype.isPrimitive()) { - Wrapper srcWrapper = Wrapper.forBasicType(rtype); - Wrapper dstWrapper = srcWrapper.isSubwordOrInt() ? Wrapper.INT : srcWrapper; // widen subword to int - // boolean casts not allowed - emitPrimCast(srcWrapper.basicTypeChar(), dstWrapper.basicTypeChar()); - emitBoxing(dstWrapper.primitiveType()); - } - - // If the return type is void we return a null reference. - if (rtype == void.class) { - mv.visitInsn(Opcodes.ACONST_NULL); - } - emitReturnInsn(Object.class); // NOTE: NamedFunction invokers always return a reference value. - - classFileEpilogue(); - bogusMethod(dstType); - - final byte[] classFile = cw.toByteArray(); - maybeDump(className, classFile); - 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. - */ - private void bogusMethod(Object... os) { - if (DUMP_CLASS_FILES) { - mv = cw.visitMethod(Opcodes.ACC_STATIC, "dummy", "()V", null, null); - for (Object o : os) { - mv.visitLdcInsn(o.toString()); - mv.visitInsn(Opcodes.POP); - } - mv.visitInsn(Opcodes.RETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - } - } -} diff -r 8fb89a569621 -r bd151459ee4f rt/emul/compact/src/main/java/java/lang/invoke/LambdaForm.java --- a/rt/emul/compact/src/main/java/java/lang/invoke/LambdaForm.java Sun Aug 10 06:21:50 2014 +0200 +++ b/rt/emul/compact/src/main/java/java/lang/invoke/LambdaForm.java Sun Aug 10 07:02:12 2014 +0200 @@ -450,15 +450,16 @@ if (vmentry != null && isCompiled) { return vmentry; // already compiled somehow } - try { - vmentry = InvokerBytecodeGenerator.generateCustomizedCode(this, invokerType); - if (TRACE_INTERPRETER) - traceInterpreter("compileToBytecode", this); - isCompiled = true; - return vmentry; - } catch (Error | Exception ex) { - throw newInternalError("compileToBytecode", ex); - } + throw newInternalError("compileToBytecode", new Exception()); +// try { +// vmentry = InvokerBytecodeGenerator.generateCustomizedCode(this, invokerType); +// if (TRACE_INTERPRETER) +// traceInterpreter("compileToBytecode", this); +// isCompiled = true; +// return vmentry; +// } catch (Error | Exception ex) { +// throw newInternalError("compileToBytecode", ex); +// } } private static final ConcurrentHashMap PREPARED_FORMS; @@ -527,7 +528,9 @@ if (prep != null) return prep; assert(isValidSignature(sig)); prep = new LambdaForm(sig); - prep.vmentry = InvokerBytecodeGenerator.generateLambdaFormInterpreterEntryPoint(sig); +// prep.vmentry = InvokerBytecodeGenerator.generateLambdaFormInterpreterEntryPoint(sig); + if (true) throw new IllegalStateException(); + //LambdaForm prep2 = PREPARED_FORMS.putIfAbsent(sig.intern(), prep); return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_INTERPRET, prep); } @@ -1129,8 +1132,9 @@ private static MethodHandle computeInvoker(MethodTypeForm typeForm) { MethodHandle mh = typeForm.namedFunctionInvoker; if (mh != null) return mh; - MemberName invoker = InvokerBytecodeGenerator.generateNamedFunctionInvoker(typeForm); // this could take a while - mh = DirectMethodHandle.make(invoker); +// MemberName invoker = InvokerBytecodeGenerator.generateNamedFunctionInvoker(typeForm); // this could take a while + if (true) throw new IllegalStateException(); +// mh = DirectMethodHandle.make(invoker); MethodHandle mh2 = typeForm.namedFunctionInvoker; if (mh2 != null) return mh2; // benign race if (!mh.type().equals(INVOKER_METHOD_TYPE)) diff -r 8fb89a569621 -r bd151459ee4f rt/emul/compact/src/main/java/java/lang/invoke/LambdaMetafactory.java --- a/rt/emul/compact/src/main/java/java/lang/invoke/LambdaMetafactory.java Sun Aug 10 06:21:50 2014 +0200 +++ b/rt/emul/compact/src/main/java/java/lang/invoke/LambdaMetafactory.java Sun Aug 10 07:02:12 2014 +0200 @@ -296,12 +296,13 @@ MethodType instantiatedMethodType) throws LambdaConversionException { AbstractValidatingLambdaMetafactory mf; - mf = new InnerClassLambdaMetafactory(caller, invokedType, - invokedName, samMethodType, - implMethod, instantiatedMethodType, - false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY); - mf.validateMetafactoryArgs(); - return mf.buildCallSite(); +// mf = new InnerClassLambdaMetafactory(caller, invokedType, +// invokedName, samMethodType, +// implMethod, instantiatedMethodType, +// false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY); +// mf.validateMetafactoryArgs(); +// return mf.buildCallSite(); + throw new IllegalStateException(); } /** @@ -462,14 +463,15 @@ } } - AbstractValidatingLambdaMetafactory mf - = new InnerClassLambdaMetafactory(caller, invokedType, - invokedName, samMethodType, - implMethod, - instantiatedMethodType, - isSerializable, - markerInterfaces, bridges); - mf.validateMetafactoryArgs(); - return mf.buildCallSite(); +// AbstractValidatingLambdaMetafactory mf +// = new InnerClassLambdaMetafactory(caller, invokedType, +// invokedName, samMethodType, +// implMethod, +// instantiatedMethodType, +// isSerializable, +// markerInterfaces, bridges); +// mf.validateMetafactoryArgs(); +// return mf.buildCallSite(); + throw new IllegalStateException(); } } diff -r 8fb89a569621 -r bd151459ee4f rt/emul/compact/src/main/java/java/lang/invoke/TypeConvertingMethodAdapter.java --- a/rt/emul/compact/src/main/java/java/lang/invoke/TypeConvertingMethodAdapter.java Sun Aug 10 06:21:50 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,302 +0,0 @@ -/* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * 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. - */ - -package java.lang.invoke; - -import jdk.internal.org.objectweb.asm.MethodVisitor; -import jdk.internal.org.objectweb.asm.Opcodes; -import jdk.internal.org.objectweb.asm.Type; -import sun.invoke.util.BytecodeDescriptor; -import sun.invoke.util.Wrapper; -import static sun.invoke.util.Wrapper.*; - -class TypeConvertingMethodAdapter extends MethodVisitor { - - TypeConvertingMethodAdapter(MethodVisitor mv) { - super(Opcodes.ASM5, mv); - } - - private static final int NUM_WRAPPERS = Wrapper.values().length; - - private static final String NAME_OBJECT = "java/lang/Object"; - private static final String WRAPPER_PREFIX = "Ljava/lang/"; - - // Same for all primitives; name of the boxing method - private static final String NAME_BOX_METHOD = "valueOf"; - - // Table of opcodes for widening primitive conversions; NOP = no conversion - private static final int[][] wideningOpcodes = new int[NUM_WRAPPERS][NUM_WRAPPERS]; - - private static final Wrapper[] FROM_WRAPPER_NAME = new Wrapper[16]; - - // Table of wrappers for primitives, indexed by ASM type sorts - private static final Wrapper[] FROM_TYPE_SORT = new Wrapper[16]; - - static { - for (Wrapper w : Wrapper.values()) { - if (w.basicTypeChar() != 'L') { - int wi = hashWrapperName(w.wrapperSimpleName()); - assert (FROM_WRAPPER_NAME[wi] == null); - FROM_WRAPPER_NAME[wi] = w; - } - } - - for (int i = 0; i < NUM_WRAPPERS; i++) { - for (int j = 0; j < NUM_WRAPPERS; j++) { - wideningOpcodes[i][j] = Opcodes.NOP; - } - } - - initWidening(LONG, Opcodes.I2L, BYTE, SHORT, INT, CHAR); - initWidening(LONG, Opcodes.F2L, FLOAT); - initWidening(FLOAT, Opcodes.I2F, BYTE, SHORT, INT, CHAR); - initWidening(FLOAT, Opcodes.L2F, LONG); - initWidening(DOUBLE, Opcodes.I2D, BYTE, SHORT, INT, CHAR); - initWidening(DOUBLE, Opcodes.F2D, FLOAT); - initWidening(DOUBLE, Opcodes.L2D, LONG); - - FROM_TYPE_SORT[Type.BYTE] = Wrapper.BYTE; - FROM_TYPE_SORT[Type.SHORT] = Wrapper.SHORT; - FROM_TYPE_SORT[Type.INT] = Wrapper.INT; - FROM_TYPE_SORT[Type.LONG] = Wrapper.LONG; - FROM_TYPE_SORT[Type.CHAR] = Wrapper.CHAR; - FROM_TYPE_SORT[Type.FLOAT] = Wrapper.FLOAT; - FROM_TYPE_SORT[Type.DOUBLE] = Wrapper.DOUBLE; - FROM_TYPE_SORT[Type.BOOLEAN] = Wrapper.BOOLEAN; - } - - private static void initWidening(Wrapper to, int opcode, Wrapper... from) { - for (Wrapper f : from) { - wideningOpcodes[f.ordinal()][to.ordinal()] = opcode; - } - } - - /** - * Class name to Wrapper hash, derived from Wrapper.hashWrap() - * @param xn - * @return The hash code 0-15 - */ - private static int hashWrapperName(String xn) { - if (xn.length() < 3) { - return 0; - } - return (3 * xn.charAt(1) + xn.charAt(2)) % 16; - } - - private Wrapper wrapperOrNullFromDescriptor(String desc) { - if (!desc.startsWith(WRAPPER_PREFIX)) { - // Not a class type (array or method), so not a boxed type - // or not in the right package - return null; - } - // Pare it down to the simple class name - String cname = desc.substring(WRAPPER_PREFIX.length(), desc.length() - 1); - // Hash to a Wrapper - Wrapper w = FROM_WRAPPER_NAME[hashWrapperName(cname)]; - if (w == null || w.wrapperSimpleName().equals(cname)) { - return w; - } else { - return null; - } - } - - private static String wrapperName(Wrapper w) { - return "java/lang/" + w.wrapperSimpleName(); - } - - private static String unboxMethod(Wrapper w) { - return w.primitiveSimpleName() + "Value"; - } - - private static String boxingDescriptor(Wrapper w) { - return String.format("(%s)L%s;", w.basicTypeChar(), wrapperName(w)); - } - - private static String unboxingDescriptor(Wrapper w) { - return "()" + w.basicTypeChar(); - } - - void boxIfTypePrimitive(Type t) { - Wrapper w = FROM_TYPE_SORT[t.getSort()]; - if (w != null) { - box(w); - } - } - - void widen(Wrapper ws, Wrapper wt) { - if (ws != wt) { - int opcode = wideningOpcodes[ws.ordinal()][wt.ordinal()]; - if (opcode != Opcodes.NOP) { - visitInsn(opcode); - } - } - } - - void box(Wrapper w) { - visitMethodInsn(Opcodes.INVOKESTATIC, - wrapperName(w), - NAME_BOX_METHOD, - boxingDescriptor(w)); - } - - /** - * Convert types by unboxing. The source type is known to be a primitive wrapper. - * @param ws A primitive wrapper corresponding to wrapped reference source type - * @param wt A primitive wrapper being converted to - */ - void unbox(String sname, Wrapper wt) { - visitMethodInsn(Opcodes.INVOKEVIRTUAL, - sname, - unboxMethod(wt), - unboxingDescriptor(wt)); - } - - private String descriptorToName(String desc) { - int last = desc.length() - 1; - if (desc.charAt(0) == 'L' && desc.charAt(last) == ';') { - // In descriptor form - return desc.substring(1, last); - } else { - // Already in internal name form - return desc; - } - } - - void cast(String ds, String dt) { - String ns = descriptorToName(ds); - String nt = descriptorToName(dt); - if (!nt.equals(ns) && !nt.equals(NAME_OBJECT)) { - visitTypeInsn(Opcodes.CHECKCAST, nt); - } - } - - private boolean isPrimitive(Wrapper w) { - return w != OBJECT; - } - - private Wrapper toWrapper(String desc) { - char first = desc.charAt(0); - if (first == '[' || first == '(') { - first = 'L'; - } - return Wrapper.forBasicType(first); - } - - /** - * Convert an argument of type 'arg' to be passed to 'target' assuring that it is 'functional'. - * Insert the needed conversion instructions in the method code. - * @param arg - * @param target - * @param functional - */ - void convertType(Class arg, Class target, Class functional) { - if (arg.equals(target) && arg.equals(functional)) { - return; - } - if (arg == Void.TYPE || target == Void.TYPE) { - return; - } - if (arg.isPrimitive()) { - Wrapper wArg = Wrapper.forPrimitiveType(arg); - if (target.isPrimitive()) { - // Both primitives: widening - widen(wArg, Wrapper.forPrimitiveType(target)); - } else { - // Primitive argument to reference target - String dTarget = BytecodeDescriptor.unparse(target); - Wrapper wPrimTarget = wrapperOrNullFromDescriptor(dTarget); - if (wPrimTarget != null) { - // The target is a boxed primitive type, widen to get there before boxing - widen(wArg, wPrimTarget); - box(wPrimTarget); - } else { - // Otherwise, box and cast - box(wArg); - cast(wrapperName(wArg), dTarget); - } - } - } else { - String dArg = BytecodeDescriptor.unparse(arg); - String dSrc; - if (functional.isPrimitive()) { - dSrc = dArg; - } else { - // Cast to convert to possibly more specific type, and generate CCE for invalid arg - dSrc = BytecodeDescriptor.unparse(functional); - cast(dArg, dSrc); - } - String dTarget = BytecodeDescriptor.unparse(target); - if (target.isPrimitive()) { - Wrapper wTarget = toWrapper(dTarget); - // Reference argument to primitive target - Wrapper wps = wrapperOrNullFromDescriptor(dSrc); - if (wps != null) { - if (wps.isSigned() || wps.isFloating()) { - // Boxed number to primitive - unbox(wrapperName(wps), wTarget); - } else { - // Character or Boolean - unbox(wrapperName(wps), wps); - widen(wps, wTarget); - } - } else { - // Source type is reference type, but not boxed type, - // assume it is super type of target type - String intermediate; - if (wTarget.isSigned() || wTarget.isFloating()) { - // Boxed number to primitive - intermediate = "java/lang/Number"; - } else { - // Character or Boolean - intermediate = wrapperName(wTarget); - } - cast(dSrc, intermediate); - unbox(intermediate, wTarget); - } - } else { - // Both reference types: just case to target type - cast(dSrc, dTarget); - } - } - } - - /** - * The following method is copied from - * org.objectweb.asm.commons.InstructionAdapter. Part of ASM: a very small - * and fast Java bytecode manipulation framework. - * Copyright (c) 2000-2005 INRIA, France Telecom All rights reserved. - */ - void iconst(final int cst) { - if (cst >= -1 && cst <= 5) { - mv.visitInsn(Opcodes.ICONST_0 + cst); - } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) { - mv.visitIntInsn(Opcodes.BIPUSH, cst); - } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) { - mv.visitIntInsn(Opcodes.SIPUSH, cst); - } else { - mv.visitLdcInsn(cst); - } - } -} diff -r 8fb89a569621 -r bd151459ee4f rt/emul/mini/src/main/java/java/lang/Class.java --- a/rt/emul/mini/src/main/java/java/lang/Class.java Sun Aug 10 06:21:50 2014 +0200 +++ b/rt/emul/mini/src/main/java/java/lang/Class.java Sun Aug 10 07:02:12 2014 +0200 @@ -94,6 +94,11 @@ private static final int ENUM = 0x00004000; private static final int SYNTHETIC = 0x00001000; + /* Backing store of user-defined values pertaining to this class. + * Maintained by the ClassValue class. + */ + transient Object classValueMap; + /* * Constructor. Only the Java Virtual Machine creates Class * objects.