/* * Copyright (c) 2007, 2012, 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. * * 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 com.sun.max.vm.reflection; import static com.sun.max.vm.MaxineVM.*; import static com.sun.max.vm.classfile.constant.PoolConstantFactory.*; import static com.sun.max.vm.type.ClassRegistry.Property.*; import java.io.*; import java.lang.reflect.*; import java.util.*; import com.sun.max.annotate.*; import com.sun.max.io.*; import com.sun.max.lang.*; import com.sun.max.program.*; import com.sun.max.unsafe.*; import com.sun.max.vm.*; import com.sun.max.vm.actor.*; import com.sun.max.vm.actor.holder.*; import com.sun.max.vm.actor.member.*; import com.sun.max.vm.bytecode.graft.*; import com.sun.max.vm.bytecode.graft.BytecodeAssembler.Label; import com.sun.max.vm.classfile.*; import com.sun.max.vm.classfile.ClassfileWriter.ClassInfo; import com.sun.max.vm.classfile.constant.*; import com.sun.max.vm.heap.*; import com.sun.max.vm.type.*; import com.sun.max.vm.value.*; /** * */ public class InvocationStubGenerator<T> { private static volatile int nextSerial; public static final String STUB_PACKAGE_PREFIX = "$INVOKE_STUB$."; private static final String SAVE_JAVA_SOURCE_PROPERTY = "max.reflection.InvocationStubGenerator.saveSource"; private static boolean saveJavaSource = System.getProperty(SAVE_JAVA_SOURCE_PROPERTY) != null; /** * Determines if a given class name specifies a generated stub class. */ public static boolean isInvocationStubClassName(String typeName) { return typeName.startsWith(STUB_PACKAGE_PREFIX); } private static synchronized Utf8Constant generateName(Class declaringClass, Utf8Constant methodName, boolean forSerialization) { final int serial = ++nextSerial; String className = declaringClass.getName().replace('.', '_'); String stubName; if (methodName == SymbolTable.INIT) { stubName = STUB_PACKAGE_PREFIX + className + (forSerialization ? "$serialization_init$" : "$init$") + serial; } else if (methodName == SymbolTable.CLINIT) { stubName = STUB_PACKAGE_PREFIX + className + "$clinit$" + serial; } else { stubName = STUB_PACKAGE_PREFIX + className + "$" + methodName + "$" + serial; } return SymbolTable.makeSymbol(stubName); } private final ConstantPoolEditor constantPoolEditor; private final Boxing boxing; private final T stub; private final Class[] runtimeParameterTypes; private final boolean isStatic; private final boolean isPrivate; private final boolean isInterface; private final boolean isConstructor; // These are constant pool indexes (CPIs) for the non-shared constants used by the generated code private final int targetCPI; private final int incorrectArgumentCountMessageCPI; private final int[] runtimeParameterTypesPoolCPIs; private final int classToInstantiateCPI; private final int declaringClassCPI; static class PoolConstantArrayAppender { private final PoolConstant[] constants; private int index; PoolConstantArrayAppender(PoolConstant[] array, int index) { constants = array; this.index = index; } int append(PoolConstant entry) { final int index = this.index; constants[this.index++] = entry; return index; } public int index() { return index; } } /** * Creates a generator for a stub used to invoke a {@code target}. * * @param target the {@linkplain Method method} or {@linkplain Constructor constructor} for which the stub is being * generated * @param superClass the super class of the stub. Must be {@link MethodInvocationStub} or * {@link ConstructorInvocationStub}. * @param name the VM-level name of the target (must be "<init>" if target is a constructor) * @param declaringClass the class in which the target is declared * @param returnType the declared return type of the target * @param parameterTypes the declared parameter types of the target * @param isStatic specifies if the target is {@code static} * @param isPrivate specifies if the target is {@code private} * @param classToInstantiate the class instantiated by the target (ignored if target is not a constructor and only non-null for serialization stubs) * @param boxing enum value encapsulating the semantics of how values are to be boxed and unboxed by the stub */ InvocationStubGenerator(AccessibleObject target, Class<T> superClass, Utf8Constant name, Class declaringClass, Class returnType, Class[] parameterTypes, boolean isStatic, boolean isPrivate, Class classToInstantiate, Boxing boxing) { try { this.boxing = boxing; this.isStatic = isStatic; this.isPrivate = isPrivate; this.isInterface = declaringClass.isInterface(); this.isConstructor = target instanceof Constructor; this.runtimeParameterTypes = boxing.runtimeParameterTypes(parameterTypes, declaringClass, isStatic, isConstructor); boolean forSerialization = false; if (isConstructor) { if (classToInstantiate == null) { classToInstantiate = declaringClass; } else { forSerialization = true; } } final Utf8Constant stubClassName = generateName(declaringClass, name, forSerialization); final ClassActor declaringClassActor = ClassActor.fromJava(declaringClass); // Create the (non-shared) constant pool entries specific to this stub final MethodRefConstant targetMethodConstant; if (isInterface && name != SymbolTable.CLINIT) { targetMethodConstant = createInterfaceMethodConstant(ClassActor.fromJava(declaringClass), name, SignatureDescriptor.create(returnType, parameterTypes)); } else { targetMethodConstant = createClassMethodConstant(ClassActor.fromJava(declaringClass), name, SignatureDescriptor.create(returnType, parameterTypes)); } final StringConstant incorrectArgumentCountMessageConstant = createStringConstant("expected " + runtimeParameterTypes.length + " arguments, received "); final ClassConstant[] runtimeParameterTypesPoolConstants = new ClassConstant[runtimeParameterTypes.length]; for (int i = 0; i != runtimeParameterTypes.length; ++i) { final Class runtimeParameterType = runtimeParameterTypes[i]; runtimeParameterTypesPoolConstants[i] = runtimeParameterType.isPrimitive() ? null : createClassConstant(runtimeParameterType); } // Create the array of pool constants, recording the indexes of the non-shared constants as they are // appended to the array final PoolConstant[] constants = new PoolConstant[PROTOTYPE_CONSTANTS.length + 4 + runtimeParameterTypesPoolConstants.length]; System.arraycopy(PROTOTYPE_CONSTANTS, 0, constants, 0, PROTOTYPE_CONSTANTS.length); final PoolConstantArrayAppender appender = new PoolConstantArrayAppender(constants, PROTOTYPE_CONSTANTS.length); this.declaringClassCPI = appender.append(createClassConstant(declaringClass)); this.targetCPI = appender.append(targetMethodConstant); this.incorrectArgumentCountMessageCPI = appender.append(incorrectArgumentCountMessageConstant); this.classToInstantiateCPI = isConstructor ? appender.append(createClassConstant(classToInstantiate)) : -1; this.runtimeParameterTypesPoolCPIs = new int[runtimeParameterTypesPoolConstants.length]; for (int i = 0; i != runtimeParameterTypesPoolConstants.length; ++i) { final ClassConstant classConstant = runtimeParameterTypesPoolConstants[i]; if (classConstant != null) { runtimeParameterTypesPoolCPIs[i] = appender.append(classConstant); } } final ConstantPool constantPool = new ConstantPool(declaringClassActor.constantPool().classLoader(), constants, appender.index()); this.constantPoolEditor = constantPool.edit(); final ClassMethodActor initMethodActor = generateInit(superClass); final ClassMethodActor invokeMethodActor = generateInvoke(returnType); final ClassMethodActor[] classMethodActors = new ClassMethodActor[]{initMethodActor, invokeMethodActor}; final ClassActor superClassActor = ClassActor.fromJava(superClass); final InterfaceActor[] interfaceActors = new InterfaceActor[0]; final FieldActor[] fieldActors = new FieldActor[0]; final ClassActor stubClassActor = ClassRegistry.define(ClassActorFactory.createTupleOrHybridClassActor( constantPool, declaringClassActor.classLoader, stubClassName, ClassfileReader.JAVA_1_5_VERSION, (char) 0, Modifier.PUBLIC | Actor.REFLECTION_STUB, superClassActor, interfaceActors, fieldActors, classMethodActors, Actor.NO_GENERIC_SIGNATURE, Actor.NO_RUNTIME_VISIBLE_ANNOTATION_BYTES, ClassActor.NO_SOURCE_FILE_NAME, ClassActor.NO_INNER_CLASSES, ClassActor.NO_OUTER_CLASS, ClassActor.NO_ENCLOSING_METHOD_INFO)); try { if (isHosted() || ClassfileReader.saveClassDir.getValue() != null) { ClassfileWriter.saveGeneratedClass(new ClassInfo(stubClassActor), constantPoolEditor.copy()); if (isHosted() && saveJavaSource) { traceStubAsJavaSource(superClass, name, declaringClass, returnType, parameterTypes, isStatic, classToInstantiate, target, boxing, stubClassName); } } } catch (IOException ioException) { ioException.printStackTrace(); } this.constantPoolEditor.release(); if (MaxineVM.isHosted()) { stub = superClass.cast(stubClassActor.toJava().newInstance()); } else { // In the target we cannot call Class.newInstance() as it calls the constructor for the stub class by reflection // and ends up back here. Since the stub constructor actually does nothing important we just allocate the object. stub = superClass.cast(Heap.createTuple(stubClassActor.dynamicHub())); } } catch (InstantiationException e) { throw (InternalError) new InternalError().initCause(e); } catch (IllegalAccessException e) { throw (InternalError) new InternalError().initCause(e); } catch (NoClassDefFoundError e) { throw (InternalError) new InternalError().initCause(e); } } T stub() { return stub; } private static CodeAttribute generatedConstructorStubInitTemplate; private static CodeAttribute generatedMethodStubInitTemplate; private CodeAttribute generateInitCodeAttribute(int superConstructorCPI) { final ByteArrayBytecodeAssembler asm = new ByteArrayBytecodeAssembler(constantPoolEditor); asm.allocateLocal(Kind.REFERENCE); asm.aload(0); asm.invokespecial(superConstructorCPI, 1, 0); asm.vreturn(); return new CodeAttribute( constantPoolEditor.pool(), asm.code(), (char) asm.maxStack(), (char) asm.maxLocals(), CodeAttribute.NO_EXCEPTION_HANDLER_TABLE, LineNumberTable.EMPTY, LocalVariableTable.EMPTY, null); } private ClassMethodActor generateInit(Class superClass) { final CodeAttribute codeAttributeTemplate; if (superClass == ConstructorInvocationStub.class) { if (generatedConstructorStubInitTemplate == null) { generatedConstructorStubInitTemplate = generateInitCodeAttribute(GeneratedConstructorStub_init); } codeAttributeTemplate = generatedConstructorStubInitTemplate; } else { ProgramError.check(superClass == MethodInvocationStub.class); if (generatedMethodStubInitTemplate == null) { generatedMethodStubInitTemplate = generateInitCodeAttribute(GeneratedMethodStub_init); } codeAttributeTemplate = generatedMethodStubInitTemplate; } final CodeAttribute codeAttribute = new CodeAttribute( constantPoolEditor.pool(), codeAttributeTemplate.code(), codeAttributeTemplate.maxStack, codeAttributeTemplate.maxLocals, CodeAttribute.NO_EXCEPTION_HANDLER_TABLE, LineNumberTable.EMPTY, LocalVariableTable.EMPTY, null); return new VirtualMethodActor( SymbolTable.INIT, VOID_NO_ARGS, Actor.ACC_PUBLIC | Actor.INITIALIZER | Actor.ACC_SYNTHETIC, codeAttribute, null); } private ClassMethodActor generateInvoke(Class returnType) { final ByteArrayBytecodeAssembler asm = new ByteArrayBytecodeAssembler(constantPoolEditor); final Kind returnKind = Kind.fromJava(returnType); boolean isUnsafe = returnKind.isWord; // Index of parameters: // invoke(Object obj, Object[] args) // newInstance(Object[] args) // invoke(Value... values) // newInstance(Values... values) asm.allocateLocal(Kind.REFERENCE); // this final int argsParameter; final int objParameter; int illegalArgStartPC = 0; int argSlots = 0; if (isConstructor) { argsParameter = asm.allocateLocal(Kind.REFERENCE); objParameter = -1; // Instantiate target class before continuing // new <target class type> // dup asm.new_(classToInstantiateCPI); asm.dup(); ++argSlots; } else { objParameter = boxing == Boxing.JAVA ? asm.allocateLocal(Kind.REFERENCE) : -1; argsParameter = asm.allocateLocal(Kind.REFERENCE); // Get target object on operand stack if necessary. // We need to do an explicit null check here; we won't see // NullPointerExceptions from the invoke bytecode, since it's // covered by an exception handler. if (!isStatic) { if (boxing == Boxing.JAVA) { // aload obj // ifnonnull <checkcast label> // new <NullPointerException> // dup // invokespecial <NullPointerException ctor> // athrow // <checkcast label:> // aload obj // checkcast <target class's type> asm.aload(objParameter); final Label l = asm.newLabel(); asm.ifnonnull(l); asm.new_(NullPointerException); asm.dup(); asm.invokespecial(NullPointerException_init, 1, 0); asm.athrow(); l.bind(); illegalArgStartPC = asm.currentAddress(); asm.aload(objParameter); asm.checkcast(declaringClassCPI); ++argSlots; } } } // Have to check length of incoming array and throw // IllegalArgumentException if not correct. A concession to the // JCK (isn't clearly specified in the spec): we allow null in the // case where the argument list is zero length. // if no-arg: // aload args // ifnull <success label> // aload args // arraylength // sipush <num parameter types> // if_icmpeq <success label> // new <IllegalArgumentException> // dup // invokespecial <IllegalArgumentException ctor> // athrow // <success label:> final Label successLabel = asm.newLabel(); if (runtimeParameterTypes.length == 0) { asm.aload(argsParameter); asm.ifnull(successLabel); } asm.aload(argsParameter); asm.arraylength(); asm.iconst(runtimeParameterTypes.length); asm.if_icmpeq(successLabel); // throw new IllegalArgumentException("expected %d arguments, received %d") asm.new_(IllegalArgumentException); asm.dup(); asm.new_(StringBuilder); asm.dup(); asm.ldc(incorrectArgumentCountMessageCPI); asm.invokespecial(StringBuilder_init_String, 2, 0); asm.aload(argsParameter); asm.arraylength(); asm.invokevirtual(StringBuilder_append_int, 2, 1); asm.invokevirtual(StringBuilder_toString, 1, 1); asm.invokespecial(IllegalArgumentException_init_String, 2, 0); asm.athrow(); // Iterate through incoming actual parameters, ensuring that each // is compatible with the formal parameter type, and pushing the // actual parameter on the operand stack (unboxing and widening if necessary). successLabel.bind(); for (int i = 0; i < runtimeParameterTypes.length; i++) { final Class parameterType = runtimeParameterTypes[i]; final Kind parameterKind = Kind.fromJava(parameterType); argSlots += parameterKind.stackSlots; // aload args // sipush <index> // aaload asm.aload(argsParameter); asm.iconst(i); asm.aaload(); boxing.unbox(asm, parameterType, runtimeParameterTypesPoolCPIs[i]); if (parameterKind.isWord) { isUnsafe = true; } } final int invokeStartPC = asm.currentAddress(); // OK, ready to perform the invocation. if (isConstructor) { asm.invokespecial(targetCPI, argSlots, 0); } else { final int returnValueSlots = returnKind.stackSlots; if (isStatic) { asm.invokestatic(targetCPI, argSlots, returnValueSlots); } else { if (isInterface) { asm.invokeinterface(targetCPI, argSlots, argSlots, returnValueSlots); } else { if (isPrivate && !MaxineVM.isHosted()) { // Can't do this while bootstrapping as the Hotspot verifier will reject an invokespecial to an inaccessible method asm.invokespecial(targetCPI, argSlots, returnValueSlots); } else { asm.invokevirtual(targetCPI, argSlots, returnValueSlots); } } } } final int invokeEndPC = asm.currentAddress(); boxing.box(asm, isConstructor, returnKind); asm.areturn(); assert asm.stack() == 0; // We generate two exception handlers; one which is responsible // for catching ClassCastException and NullPointerException and // throwing IllegalArgumentException, and the other which catches // all java/lang/Throwable objects thrown from the target method // and wraps them in InvocationTargetExceptions. final int classCastHandler = asm.currentAddress(); // ClassCast, etc. exception handler asm.setStack(1); asm.invokevirtual(Object_toString, 1, 1); asm.new_(IllegalArgumentException); asm.dup_x1(); asm.swap(); asm.invokespecial(IllegalArgumentException_init_String, 2, 0); asm.athrow(); final int invocationTargetHandler = asm.currentAddress(); // InvocationTargetException exception handler asm.setStack(1); asm.new_(InvocationTargetException); asm.dup_x1(); asm.swap(); asm.invokespecial(InvocationTargetException_init_Throwable, 2, 0); asm.athrow(); // Generate exception table. We cover the entire code sequence // with an exception handler which catches ClassCastException and // converts it into an IllegalArgumentException. final ExceptionHandlerEntry[] exceptionHandlerEntries = new ExceptionHandlerEntry[] { new ExceptionHandlerEntry(illegalArgStartPC, invokeStartPC, classCastHandler, ClassCastException), new ExceptionHandlerEntry(illegalArgStartPC, invokeStartPC, classCastHandler, NullPointerException), new ExceptionHandlerEntry(invokeStartPC, invokeEndPC, invocationTargetHandler, 0) }; final CodeAttribute codeAttribute = new CodeAttribute( constantPoolEditor.pool(), asm.code(), (char) asm.maxStack(), (char) asm.maxLocals(), exceptionHandlerEntries, LineNumberTable.EMPTY, LocalVariableTable.EMPTY, null); VirtualMethodActor virtualMethodActor; if (isConstructor) { final TypeDescriptor[] checkedExceptions = { JavaTypeDescriptor.INSTANTIATION_EXCEPTION, JavaTypeDescriptor.ILLEGAL_ARGUMENT_EXCEPTION, JavaTypeDescriptor.INVOCATION_TARGET_EXCEPTION }; virtualMethodActor = new VirtualMethodActor(newInstance, boxing.newInstanceSignature(), Actor.ACC_PUBLIC | Actor.ACC_SYNTHETIC, codeAttribute, null); final ClassRegistry classRegistry = ClassRegistry.makeRegistry(constantPoolEditor.pool().classLoader()); classRegistry.set(CHECKED_EXCEPTIONS, virtualMethodActor, checkedExceptions); } else { final TypeDescriptor[] checkedExceptions = { JavaTypeDescriptor.ILLEGAL_ARGUMENT_EXCEPTION, JavaTypeDescriptor.INVOCATION_TARGET_EXCEPTION }; virtualMethodActor = new VirtualMethodActor(invoke, boxing.invokeSignature(), Actor.ACC_PUBLIC | Actor.ACC_SYNTHETIC, codeAttribute, null); final ClassRegistry classRegistry = ClassRegistry.makeRegistry(constantPoolEditor.pool().classLoader()); classRegistry.set(CHECKED_EXCEPTIONS, virtualMethodActor, checkedExceptions); } if (isUnsafe) { virtualMethodActor.beUnsafe(); } return virtualMethodActor; } private void traceStubAsJavaSource(Class superClass, Utf8Constant name, Class declaringClass, Class returnType, Class[] parameterTypes, boolean isStatic, Class classToInstantiate, AccessibleObject target, Boxing boxing, final Utf8Constant stubClassName) throws IOException { final String simpleStubClassName = stubClassName.toString().substring(STUB_PACKAGE_PREFIX.length()); final IndentWriter writer = IndentWriter.traceStreamWriter(); writer.println("package " + STUB_PACKAGE_PREFIX.substring(0, STUB_PACKAGE_PREFIX.length() - 1) + ";"); writer.println(); writer.println("/**"); writer.println(" * Automatically generated stub for: " + target + "."); writer.println(" */"); writer.println("public class " + simpleStubClassName + " extends " + superClass.getSimpleName() + " {"); writer.indent(); generateInvokeAsSource(declaringClass, name, returnType, parameterTypes, isStatic, classToInstantiate, boxing, writer); writer.outdent(); writer.println("}"); writer.flush(); } private void generateInvokeAsSource( Class declaringClass, Utf8Constant name, Class returnType, Class[] parameterTypes, boolean isStatic, Class classToInstantiate, Boxing boxing, IndentWriter writer) { final boolean isConstructor = classToInstantiate != null; final Class[] runtimeParameterTypes = boxing.runtimeParameterTypes(parameterTypes, declaringClass, isStatic, isConstructor); final Kind returnKind = Kind.fromJava(returnType); final ConstantPool pool = constantPoolEditor.pool(); writer.println("@Override"); writer.println(boxing.sourceDeclaration(isConstructor) + " {"); writer.indent(); writer.println("if (" + (parameterTypes.length == 0 ? "args != null && " : "") + "args.length != " + runtimeParameterTypes.length + ") {"); writer.indent(); writer.println("throw new IllegalArgumentException(\"expected " + runtimeParameterTypes.length + " arguments, received \" + args.length);"); writer.outdent(); writer.println("}"); if (isConstructor) { writer.println("Object returnValue = new " + classToInstantiate.getName() + "("); writer.indent(); } else { final String prefix; if (returnKind == Kind.VOID) { prefix = ""; } else { prefix = "final " + returnType.getSimpleName() + " returnValue = "; } if (!isStatic) { if (boxing == Boxing.JAVA) { writer.println("if (obj == null) {"); writer.indent(); writer.println("throw new NullPointerException();"); writer.outdent(); writer.println("}"); writer.println(prefix + "(" + boxing.sourceUnbox(pool, declaringClass, "obj") + ")." + name + "("); } else { writer.println(prefix + "(" + boxing.sourceUnbox(pool, declaringClass, "args[0]") + ")." + name + "("); } } else { writer.println(prefix + declaringClass.getName().replace('$', '.') + "." + name + "("); } writer.indent(); } final int firstNonReceiverParameter = boxing == Boxing.JAVA ? 0 : (isStatic || isConstructor ? 0 : 1); for (int i = firstNonReceiverParameter; i < runtimeParameterTypes.length; i++) { final Class parameterType = runtimeParameterTypes[i]; final String unbox = boxing.sourceUnbox(pool, parameterType, "args[" + i + "]"); if (i == runtimeParameterTypes.length - 1) { writer.println(unbox); } else { writer.println(unbox + ","); } } writer.outdent(); writer.println(");"); writer.println("return " + boxing.sourceBox(pool, isConstructor, returnKind, "returnValue") + ";"); writer.outdent(); writer.println("}"); writer.flush(); } // Pool constants shared by all stubs private static List<PoolConstant> prototypeConstants; private static int register(PoolConstant constant) { if (prototypeConstants == null) { prototypeConstants = new ArrayList<PoolConstant>(); prototypeConstants.add(InvalidConstant.VALUE); } final int index = prototypeConstants.size(); prototypeConstants.add(constant); return index; } public static final Utf8Constant newInstance = SymbolTable.makeSymbol("newInstance"); public static final Utf8Constant invoke = SymbolTable.makeSymbol("invoke"); static final int NullPointerException = register(createClassConstant(NullPointerException.class)); static final int ClassCastException = register(createClassConstant(ClassCastException.class)); static final int IllegalArgumentException = register(createClassConstant(IllegalArgumentException.class)); static final int InstantiationException = register(createClassConstant(InstantiationException.class)); static final int InvocationTargetException = register(createClassConstant(InvocationTargetException.class)); static final int StringBuilder = register(createClassConstant(StringBuilder.class)); static final int GeneratedConstructorStub_init = register(createClassMethodConstant(ConstructorInvocationStub.class)); static final int GeneratedMethodStub_init = register(createClassMethodConstant(MethodInvocationStub.class)); static final int NullPointerException_init = register(createClassMethodConstant(NullPointerException.class)); static final int IllegalArgumentException_init = register(createClassMethodConstant(IllegalArgumentException.class)); static final int IllegalArgumentException_init_String = register(createClassMethodConstant(IllegalArgumentException.class, String.class)); static final int StringBuilder_init_String = register(createClassMethodConstant(StringBuilder.class, String.class)); static final int InvocationTargetException_init_Throwable = register(createClassMethodConstant(InvocationTargetException.class, Throwable.class)); static final int Object_toString = register(createClassMethodConstant(Object.class, SymbolTable.makeSymbol("toString"))); static final int Word_asOffset = register(createClassMethodConstant(Word.class, SymbolTable.makeSymbol("asOffset"))); static final int Word_asAddress = register(createClassMethodConstant(Word.class, SymbolTable.makeSymbol("asAddress"))); static final int Word_asPointer = register(createClassMethodConstant(Word.class, SymbolTable.makeSymbol("asPointer"))); static final int Word_asSize = register(createClassMethodConstant(Word.class, SymbolTable.makeSymbol("asSize"))); static final int StringBuilder_append_int = register(createClassMethodConstant(StringBuilder.class, SymbolTable.makeSymbol("append"), int.class)); static final int StringBuilder_append_String = register(createClassMethodConstant(StringBuilder.class, SymbolTable.makeSymbol("append"), String.class)); static final int StringBuilder_append_Object = register(createClassMethodConstant(StringBuilder.class, SymbolTable.makeSymbol("append"), Object.class)); static final int StringBuilder_toString = register(createClassMethodConstant(StringBuilder.class, SymbolTable.makeSymbol("toString"))); static final int BYTES_PER_OPERAND_STACK_UNIT = 4; static final Map<KindEnum, Integer> JAVA_BOX_PRIMITIVE; static final Map<KindEnum, Integer> JAVA_UNBOX_PRIMITIVE; static final Map<KindEnum, Integer> VALUE_BOX; static final Map<KindEnum, Integer> VALUE_UNBOX; @HOSTED_ONLY static final Map<KindEnum, Method> VALUE_UNBOX_METHOD = new EnumMap<KindEnum, Method>(KindEnum.class); static final Map<Class<? extends Word>, Integer> CAST_WORD; static final int VoidValue_VOID; static final SignatureDescriptor VOID_NO_ARGS; static final PoolConstant[] PROTOTYPE_CONSTANTS; @HOSTED_ONLY public static Method findValueUnboxMethod(Kind kind) { Method method = VALUE_UNBOX_METHOD.get(kind.asEnum); if (method == null) { final String kindName = kind.name.toString(); if (kind.isReference) { method = Classes.getDeclaredMethod(Value.class, "unboxObject"); } else { final String camelCaseName = Character.toUpperCase(kindName.charAt(0)) + kindName.substring(1); method = Classes.getDeclaredMethod(Value.class, "unbox" + camelCaseName); } VALUE_UNBOX_METHOD.put(kind.asEnum, method); } return method; } static { final EnumMap<KindEnum, Integer> prototype = new EnumMap<KindEnum, Integer>(KindEnum.class); final Map<KindEnum, Integer> javaBoxPrimitive = prototype.clone(); final Map<KindEnum, Integer> javaUnboxPrimitive = prototype.clone(); final Map<KindEnum, Integer> valueBox = prototype.clone(); final Map<KindEnum, Integer> valueUnbox = prototype.clone(); for (Kind kind : Kind.PRIMITIVE_VALUES) { final Class<?> boxClass = kind.boxedClass; final String kindName = kind.name.toString(); javaBoxPrimitive.put(kind.asEnum, register(createClassMethodConstant(boxClass, SymbolTable.makeSymbol("valueOf"), kind.javaClass))); javaUnboxPrimitive.put(kind.asEnum, register(createClassMethodConstant(Kind.class, SymbolTable.makeSymbol("unbox" + Strings.capitalizeFirst(kindName, true)), Object.class))); } valueBox.put(Kind.REFERENCE.asEnum, register(createClassMethodConstant(ReferenceValue.class, SymbolTable.makeSymbol("from"), Object.class))); valueUnbox.put(Kind.REFERENCE.asEnum, register(createClassMethodConstant(findValueUnboxMethod(Kind.REFERENCE)))); for (Kind kind : Kind.VALUES) { final Class<?> valueBoxClass = kind.valueClass; if (!valueUnbox.containsKey(kind.asEnum)) { valueUnbox.put(kind.asEnum, register(createClassMethodConstant(findValueUnboxMethod(kind)))); } if (!valueBox.containsKey(kind.asEnum)) { valueBox.put(kind.asEnum, register(createClassMethodConstant(valueBoxClass, SymbolTable.makeSymbol("from"), kind.javaClass))); } } VoidValue_VOID = register(createFieldConstant(VoidValue.class, SymbolTable.makeSymbol("VOID"))); VOID_NO_ARGS = SignatureDescriptor.fromJava(Void.TYPE); VALUE_BOX = Collections.unmodifiableMap(valueBox); VALUE_UNBOX = Collections.unmodifiableMap(valueUnbox); JAVA_BOX_PRIMITIVE = Collections.unmodifiableMap(javaBoxPrimitive); JAVA_UNBOX_PRIMITIVE = Collections.unmodifiableMap(javaUnboxPrimitive); Map<Class<? extends Word>, Integer> castWord = new HashMap<Class<? extends Word>, Integer>(); castWord.put(Offset.class, Word_asOffset); castWord.put(Size.class, Word_asSize); castWord.put(Address.class, Word_asAddress); castWord.put(Pointer.class, Word_asPointer); CAST_WORD = Collections.unmodifiableMap(castWord); } /** * This must be the last thing executed in the static initializer. */ static { PROTOTYPE_CONSTANTS = prototypeConstants.toArray(new PoolConstant[prototypeConstants.size()]); } }