/* * 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.classfile; import static com.sun.max.vm.classfile.ErrorContext.*; import static com.sun.max.vm.classfile.constant.PoolConstantFactory.*; import java.io.*; import java.net.*; import java.util.*; import java.util.concurrent.*; import com.sun.cri.bytecode.*; import com.sun.max.*; import com.sun.max.annotate.*; import com.sun.max.ide.*; import com.sun.max.io.*; import com.sun.max.program.*; import com.sun.max.program.option.*; 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.*; import com.sun.max.vm.classfile.constant.*; import com.sun.max.vm.hosted.*; import com.sun.max.vm.type.*; import com.sun.max.vm.value.*; /** * Writes a {@link ClassActor} out as a valid * <a href="http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html">JVM class file</a>. */ public class ClassfileWriter { final ConstantPoolEditor constantPoolEditor; DataOutputStream dataOutputStream; /** * Saves a generated class actor as a class file accessible to the {@linkplain BootClassLoader VM class loader} or to * the inspector. * * @param classInfo the class to be written as a class file * @param constantPoolEditor an editor on a constant pool associated with {@code classActor} */ public static void saveGeneratedClass(ClassInfo classInfo, final ConstantPoolEditor constantPoolEditor) throws IOException { final byte[] classfile = toByteArray(classInfo, constantPoolEditor); if (MaxineVM.isHosted()) { ClassfileReader.saveClassfile(classInfo.actor.name.string, classfile); } else { classInfo.actor.classfile = classfile; } } /** * Creates a JVM class file for a given class. * * @param classInfo the class to be written as a class file * @return a byte array containing the class file */ public static byte[] toByteArray(ClassInfo classInfo) { return toByteArray(classInfo, mutableConstantPoolEditorFor(classInfo.actor)); } /** * Creates a JVM class file for a given class. * * @param classInfo the class to be written as a class file * @param constantPoolEditor an editor on a constant pool associated with {@code classInfo} * @return a byte array containing the class file */ public static byte[] toByteArray(ClassInfo classInfo, ConstantPoolEditor constantPoolEditor) { final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); try { new ClassfileWriter(classInfo, constantPoolEditor, byteArrayOutputStream); } catch (IOException ioException) { // Should never reach here throw ProgramError.unexpected(ioException); } return byteArrayOutputStream.toByteArray(); } /** * Makes a copy of a class actor's constant pool and return an editor on it. * * @param classActor the class whose constant pool is to be copied * @return an editor on a copy of {@code classActor}'s constant pool */ public static ConstantPoolEditor mutableConstantPoolEditorFor(ClassActor classActor) { final ConstantPoolEditor immutableConstantPoolEditor = classActor.constantPool().edit(); try { return immutableConstantPoolEditor.copy(); } finally { immutableConstantPoolEditor.release(); } } /** * Writes a JVM class file for a given class to a given output stream. This operation does not modify the * given class actor's constant pool. */ public ClassfileWriter(ClassActor classActor, OutputStream outputStream) throws IOException { this(new ClassInfo(classActor), mutableConstantPoolEditorFor(classActor), outputStream); } /** * Writes a JVM class file for a given class to a given output stream. * * @param classInfo the class to be written as a class file * @param constantPoolEditor an editor on a constant pool associated with {@code classInfo}. * * <b>It's imperative that this constant pool editor was obtained AFTER {@code classInfo} was constructed * as the construction of a ClassInfo object may add more entries to the given class actor's constant * pool.</b> * * @param outputStream where to write the class file * @throws IOException if an IO error occurs while writing to {@code outputStream} */ public ClassfileWriter(ClassInfo classInfo, ConstantPoolEditor constantPoolEditor, OutputStream outputStream) throws IOException { this.constantPoolEditor = constantPoolEditor; this.constantPoolEditor.append(makeUtf8Constant("--- START OF CONSTANTS ADDED FOR CLASS FILE GENERATION ---")); // Initially write nothing: this is just to flesh out the creation of constant pool entries dataOutputStream = new DataOutputStream(new NullOutputStream()); classInfo.write(this); // Now write to the provided stream dataOutputStream = outputStream instanceof DataOutputStream ? (DataOutputStream) outputStream : new DataOutputStream(outputStream); classInfo.write(this); } /** * @see ConstantPoolEditor#indexOf(PoolConstant) */ protected int indexOf(PoolConstant poolConstant) { return constantPoolEditor.indexOf(poolConstant); } protected int indexOfUtf8(Utf8Constant utf8Constant) { return indexOf(utf8Constant); } protected int indexOfUtf8(String string) { return indexOfUtf8(makeUtf8Constant(string)); } protected int indexOfUtf8(Descriptor descriptor) { return indexOfUtf8(makeUtf8Constant(descriptor.string)); } protected int indexOfClass(ClassActor classActor) { return indexOf(createClassConstant(classActor.typeDescriptor)); } protected int indexOfClass(TypeDescriptor typeDescriptor) { return indexOf(createClassConstant(typeDescriptor)); } protected void writeUnsigned2(int value) throws IOException { dataOutputStream.writeShort(value); } protected void writeUnsigned4(int value) throws IOException { dataOutputStream.writeInt(value); } protected void writeUnsigned1Array(byte[] value) throws IOException { dataOutputStream.write(value); } protected void writeUnsigned2Array(byte[] value) throws IOException { dataOutputStream.write(value); } public void writeAttributes(List<Attribute> attributes) throws IOException { writeUnsigned2(attributes.size()); for (Attribute attribute : attributes) { attribute.write(this); } } public static class Attribute { public final Utf8Constant name; protected int length = -1; public Attribute(String name) { this.name = makeUtf8Constant(name); } protected void writeData(ClassfileWriter cf) throws IOException { } public final void write(ClassfileWriter cf) throws IOException { cf.writeUnsigned2(cf.indexOf(name)); cf.writeUnsigned4(length); final int start = cf.dataOutputStream.size(); writeData(cf); final int lth = cf.dataOutputStream.size() - start; assert this.length == lth || this.length == -1; this.length = lth; assert this.length != -1; } } public static class BytesAttribute extends Attribute { protected final byte[] bytes; public BytesAttribute(String name, byte[] bytes) { super(name); this.bytes = bytes; } @Override protected void writeData(ClassfileWriter cf) throws IOException { cf.writeUnsigned1Array(bytes); } } /** * The {@code MaxineFlags} attribute is a Maxine specific attribute for a class, method or field * in a class file generated from a {@link ClassActor}. It preserves the extra flags for these * components. * * The {@code MaxineFlags} attribute has the following format: * <pre> * MaxineFlags_attribute { * u2 attribute_name_index; * u4 attribute_length; * u4 flags; * } */ public static class MaxineFlags extends Attribute { public static final String NAME = "MaxineFlags"; public final int flags; public MaxineFlags(int flags) { super(NAME); this.flags = flags; } @Override protected void writeData(ClassfileWriter cf) throws IOException { cf.writeUnsigned4(flags); } } /** * Represents the class file info common to classes, fields and methods. */ public abstract static class Info<Actor_Type extends Actor> { protected final List<Attribute> attributes = new ArrayList<Attribute>(); public final Actor_Type actor; protected Info(Actor_Type actor) { this.actor = actor; final Utf8Constant genericSignature = actor.genericSignature(); final byte[] runtimeVisibleAnnotationsBytes = actor.runtimeVisibleAnnotationsBytes(); if (genericSignature != null) { attributes.add(new Attribute("Signature") { @Override protected void writeData(ClassfileWriter cf) throws IOException { cf.writeUnsigned2(cf.indexOfUtf8(genericSignature)); } }); } if (runtimeVisibleAnnotationsBytes != null) { attributes.add(new BytesAttribute("RuntimeVisibleAnnotations", runtimeVisibleAnnotationsBytes)); } if (actor.isSynthetic()) { final ClassActor holder; if (actor instanceof ClassActor) { holder = (ClassActor) actor; } else { holder = ((MemberActor) actor).holder(); } if (holder.majorVersion < 49) { attributes.add(new Attribute("Synthetic")); } else { // The ACC_SYNTHETIC flag was introduced in version 49 of the class file format // and so it is used in preference to the Synthetic attribute. The former is far // more compact and easier to process. } } if (actor.isDeprecated()) { attributes.add(new Attribute("Deprecated")); } } protected abstract void write(ClassfileWriter cf) throws IOException; } /** * {@link Character#isDigit(char)} answers {@code true} to some non-ascii digits. This one does not. */ private static boolean isAsciiDigit(char c) { return '0' <= c && c <= '9'; } public static String innerClassName(TypeDescriptor innerClass) { final String javaName = innerClass.toJavaString(); final int lastDotIndex = javaName.lastIndexOf('.'); final int lastDollarIndex = javaName.lastIndexOf('$'); final int chopIndex = Math.max(lastDollarIndex, lastDotIndex) + 1; final String simpleJavaName = javaName.substring(chopIndex); final int length = simpleJavaName.length(); int index = 0; while (index < length && isAsciiDigit(simpleJavaName.charAt(index))) { index++; } // Eventually, this is the empty string iff this is an anonymous class return simpleJavaName.substring(index); } public int innerClassNameIndex(TypeDescriptor innerClass) { final String innerClassName = innerClassName(innerClass); if (innerClassName.isEmpty()) { // anonymous class return 0; } return indexOfUtf8(innerClassName); } /** * Represents the class file info for a class. */ public static class ClassInfo extends Info<ClassActor> { protected final InterfaceActor[] interfaceActors; protected final MethodInfo[] methods; protected final FieldInfo[] fields; /** * Creates a representation of a class that can be written as a class file. */ public ClassInfo(ClassActor classActor) { super(classActor); interfaceActors = classActor.localInterfaceActors(); List<MethodActor> methodActors = classActor.getLocalMethodActors(); methods = new MethodInfo[methodActors.size()]; for (int i = 0; i < methods.length; ++i) { methods[i] = new MethodInfo(methodActors.get(i)); } FieldActor[] fieldActors = Utils.concat(classActor.localInstanceFieldActors(), classActor.localStaticFieldActors()); fields = new FieldInfo[fieldActors.length]; for (int i = 0; i < fieldActors.length; i++) { fields[i] = new FieldInfo(fieldActors[i]); } final TypeDescriptor outerClass = classActor.outerClassDescriptor(); final TypeDescriptor[] innerClasses = classActor.innerClassDescriptors(); final EnclosingMethodInfo enclosingMethod = classActor.enclosingMethodInfo(); final String sourceFileName = classActor.sourceFileName; if (sourceFileName != null) { attributes.add(new Attribute("SourceFile") { @Override protected void writeData(ClassfileWriter cf) throws IOException { cf.writeUnsigned2(cf.indexOfUtf8(sourceFileName)); } }); } if (enclosingMethod != null) { attributes.add(new Attribute("EnclosingMethod") { @Override protected void writeData(ClassfileWriter cf) throws IOException { cf.writeUnsigned2(cf.indexOfClass(enclosingMethod.holder())); if (enclosingMethod.name() != null) { final Utf8Constant methodName = makeUtf8Constant(enclosingMethod.name()); final Utf8Constant descriptor = makeUtf8Constant(enclosingMethod.descriptor()); final NameAndTypeConstant nameAndType = createNameAndTypeConstant(methodName, descriptor); cf.writeUnsigned2(cf.indexOf(nameAndType)); } else { cf.writeUnsigned2(0); } } }); } if (outerClass != null || innerClasses != null) { attributes.add(new Attribute("InnerClasses") { @Override protected void writeData(ClassfileWriter cf) throws IOException { cf.writeUnsigned2((outerClass == null ? 0 : 1) + (innerClasses == null ? 0 : innerClasses.length)); if (outerClass != null) { cf.writeUnsigned2(cf.indexOfClass(actor.typeDescriptor)); cf.writeUnsigned2(cf.indexOfClass(outerClass)); cf.writeUnsigned2(cf.innerClassNameIndex(actor.typeDescriptor)); cf.writeUnsigned2(actor.flags() & Actor.JAVA_CLASS_FLAGS); } if (innerClasses != null) { final int outerClassIndex = cf.indexOfClass(actor.typeDescriptor); for (TypeDescriptor innerClass : innerClasses) { // Not really correct: would require resolving inner classes first final int innerClassFlags = 0; cf.writeUnsigned2(cf.indexOfClass(innerClass)); cf.writeUnsigned2(outerClassIndex); cf.writeUnsigned2(cf.innerClassNameIndex(innerClass)); cf.writeUnsigned2(innerClassFlags); } } } }); } if ((classActor.flags() & ~Actor.JAVA_CLASS_FLAGS) != 0) { attributes.add(new MaxineFlags(classActor.flags())); } } @Override protected void write(ClassfileWriter cf) throws IOException { final String className = actor.name.string; final TypeDescriptor classDescriptor = JavaTypeDescriptor.getDescriptorForWellFormedTupleName(className); final int thisClassIndex = cf.indexOf(createClassConstant(classDescriptor)); final ClassActor superClassActor = actor.superClassActor; final int superClassIndex = superClassActor == null ? 0 : cf.indexOf(createClassConstant(superClassActor.typeDescriptor)); cf.writeUnsigned4(0xcafebabe); cf.writeUnsigned2(actor.minorVersion); cf.writeUnsigned2(actor.majorVersion); cf.constantPoolEditor.write(cf.dataOutputStream); cf.writeUnsigned2(actor.flags()/* & Actor.JAVA_CLASS_FLAGS*/); cf.writeUnsigned2(thisClassIndex); cf.writeUnsigned2(superClassIndex); cf.writeUnsigned2(interfaceActors.length); for (InterfaceActor interfaceActor : interfaceActors) { cf.writeUnsigned2(cf.indexOfClass(interfaceActor)); } cf.writeUnsigned2(fields.length); for (FieldInfo field : fields) { field.write(cf); } cf.writeUnsigned2(methods.length); for (MethodInfo method : methods) { method.write(cf); } cf.writeAttributes(attributes); } } /** * Represents the class file info common to fields and methods. */ public abstract static class MemberInfo<MemberActor_Type extends MemberActor> extends Info<MemberActor_Type> { protected MemberInfo(MemberActor_Type memberActor) { super(memberActor); } protected abstract int classfileFlags(); @Override protected void write(ClassfileWriter cf) throws IOException { cf.writeUnsigned2(classfileFlags()); cf.writeUnsigned2(cf.indexOfUtf8(actor.name)); cf.writeUnsigned2(cf.indexOfUtf8(actor.descriptor)); cf.writeAttributes(attributes); } } /** * Preserves the original bytecode loaded or generated for a method actor. This bytecode is guaranteed to be * valid JVM code which can be used to (re)generate a valid class file. This is required in hosted execution * mode when {@link ClassActor}s may have to be serialized to class files that can be loaded by the * underlying JVM. */ @HOSTED_ONLY public static final Map<MethodActor, byte[]> classfileCodeMap = new ConcurrentHashMap<MethodActor, byte[]>(); /** * Exists for the same reasons as {@link #classfileCodeMap}. The reason {@link #classfileCodeMap} is required * in addition to this map is that intrinsification occurs 'in situ'. */ @HOSTED_ONLY public static final Map<MethodActor, CodeAttribute> classfileCodeAttributeMap = new ConcurrentHashMap<MethodActor, CodeAttribute>(); /** * Represents the class file info for methods. */ public static class MethodInfo extends MemberInfo<MethodActor> { MethodInfo(MethodActor methodActor) { super(methodActor); CodeAttribute codeAttribute = methodActor instanceof ClassMethodActor ? ((ClassMethodActor) methodActor).codeAttribute() : null; final byte[] runtimeVisibleParameterAnnotationsBytes = methodActor.runtimeVisibleParameterAnnotationsBytes(); final byte[] annotationDefaultBytes = methodActor.annotationDefaultBytes(); final TypeDescriptor[] checkedExceptions = methodActor.checkedExceptions(); if ((methodActor.flags() & ~Actor.JAVA_METHOD_FLAGS) != 0) { attributes.add(new MaxineFlags(methodActor.flags())); } if (runtimeVisibleParameterAnnotationsBytes != null) { attributes.add(new BytesAttribute("RuntimeVisibleParameterAnnotations", runtimeVisibleParameterAnnotationsBytes)); } if (annotationDefaultBytes != null) { attributes.add(new BytesAttribute("AnnotationDefault", annotationDefaultBytes)); } if (checkedExceptions.length != 0) { attributes.add(new Attribute("Exceptions") { @Override protected void writeData(ClassfileWriter cf) throws IOException { cf.writeUnsigned2(checkedExceptions.length); for (TypeDescriptor checkedException : checkedExceptions) { final int checkedExceptionIndex = cf.indexOfClass(checkedException); cf.writeUnsigned2(checkedExceptionIndex); } } }); } if (codeAttribute != null) { if (MaxineVM.isHosted()) { if (classfileCodeAttributeMap.containsKey(methodActor)) { codeAttribute = classfileCodeAttributeMap.get(methodActor); } } Code code = new Code(codeAttribute); if (MaxineVM.isHosted()) { byte[] classfileCode = classfileCodeMap.get(methodActor); if (classfileCode != null) { code.classfileCode = classfileCode; } } attributes.add(code); } } @Override protected int classfileFlags() { return actor.flags() & Actor.JAVA_METHOD_FLAGS; } } public static class Code extends Attribute { protected final List<Attribute> attributes = new ArrayList<Attribute>(); protected final CodeAttribute codeAttribute; @HOSTED_ONLY protected byte[] classfileCode; public Code(CodeAttribute codeAttribute) { super("Code"); this.codeAttribute = codeAttribute; final StackMapTable stackMapTable = codeAttribute.stackMapTable(); final LineNumberTable lineNumberTable = codeAttribute.lineNumberTable(); final LocalVariableTable localVariableTable = codeAttribute.localVariableTable(); if (!lineNumberTable.isEmpty()) { attributes.add(new Attribute("LineNumberTable") { @Override protected void writeData(ClassfileWriter cf) throws IOException { lineNumberTable.writeAttributeInfo(cf.dataOutputStream, cf.constantPoolEditor); } }); } if (!localVariableTable.isEmpty()) { attributes.add(new Attribute("LocalVariableTable") { @Override protected void writeData(ClassfileWriter cf) throws IOException { localVariableTable.writeLocalVariableTableAttributeInfo(cf.dataOutputStream, cf.constantPoolEditor); } }); } if (localVariableTable.numberOfEntriesWithSignature() != 0) { attributes.add(new Attribute("LocalVariableTypeTable") { @Override protected void writeData(ClassfileWriter cf) throws IOException { localVariableTable.writeLocalVariableTypeTableAttributeInfo(cf.dataOutputStream, cf.constantPoolEditor); } }); } if (stackMapTable != null) { attributes.add(new Attribute("StackMapTable") { @Override protected void writeData(ClassfileWriter cf) throws IOException { stackMapTable.writeAttributeInfo(cf.dataOutputStream, cf.constantPoolEditor); } }); } } /** * Replace non-standard bytecodes with standard JVM bytecodes. */ private static byte[] standardizeCode(final CodeAttribute codeAttribute, final ClassfileWriter cf) { final byte[] code = codeAttribute.code(); if (code == null) { return code; } final byte[] codeCopy = code.clone(); final BytecodeAdapter bytecodeAdapter = new BytecodeAdapter() { @Override protected void jnicall(int nativeFunctionDescriptorIndex) { final Utf8Constant name = SymbolTable.makeSymbol("callnative_" + nativeFunctionDescriptorIndex); final ConstantPool pool = codeAttribute.cp; final SignatureDescriptor signature = SignatureDescriptor.create(pool.utf8At(nativeFunctionDescriptorIndex, "native function descriptor")); final ClassMethodRefConstant method = PoolConstantFactory.createClassMethodConstant(pool.holder(), name, signature); final int index = cf.indexOf(method); final int bci = bytecodeScanner().currentOpcodeBCI(); codeCopy[bci] = (byte) Bytecodes.INVOKESTATIC; codeCopy[bci + 1] = (byte) (index >> 8); codeCopy[bci + 2] = (byte) index; } }; new BytecodeScanner(bytecodeAdapter).scan(new BytecodeBlock(code)); return codeCopy; } @Override protected void writeData(ClassfileWriter cf) throws IOException { final ExceptionHandlerEntry[] exceptionHandlerTable = codeAttribute.exceptionHandlerTable(); cf.writeUnsigned2(codeAttribute.maxStack); cf.writeUnsigned2(codeAttribute.maxLocals); final byte[] code; if (MaxineVM.isHosted() && classfileCode != null) { code = classfileCode; } else { code = standardizeCode(codeAttribute, cf); } cf.writeUnsigned4(code.length); cf.writeUnsigned1Array(code); cf.writeUnsigned2(exceptionHandlerTable.length); for (ExceptionHandlerEntry info : exceptionHandlerTable) { cf.writeUnsigned2(info.startBCI()); // start_pc cf.writeUnsigned2(info.endBCI()); // end_pc cf.writeUnsigned2(info.handlerBCI()); // handler_pc cf.writeUnsigned2(info.catchTypeIndex()); // catch_type } cf.writeAttributes(attributes); } } /** * Represents the class file info for fields. */ public static class FieldInfo extends MemberInfo<FieldActor> { protected FieldInfo(FieldActor fieldActor) { super(fieldActor); final Value constantValue = fieldActor.constantValue(); if ((fieldActor.flags() & ~Actor.JAVA_FIELD_FLAGS) != 0) { attributes.add(new MaxineFlags(fieldActor.flags())); } if (constantValue != null) { attributes.add(new Attribute("ConstantValue") { @Override protected void writeData(ClassfileWriter cf) throws IOException { switch (actor.kind.asEnum) { case BOOLEAN: case BYTE: case CHAR: case SHORT: case INT: { cf.writeUnsigned2(cf.indexOf(createIntegerConstant(constantValue.toInt()))); break; } case FLOAT: { cf.writeUnsigned2(cf.indexOf(createFloatConstant(constantValue.toFloat()))); break; } case LONG: { cf.writeUnsigned2(cf.indexOf(createLongConstant(constantValue.toLong()))); break; } case DOUBLE: { cf.writeUnsigned2(cf.indexOf(createDoubleConstant(constantValue.toDouble()))); break; } case REFERENCE: { final Object object = constantValue.asObject(); if (!(object instanceof String)) { throw classFormatError("Invalid ConstantValue attribute"); } final String string = (String) object; cf.writeUnsigned2(cf.indexOfUtf8(string)); break; } default: { throw classFormatError("Cannot have ConstantValue for fields of type " + actor.kind); } } } }); } } @Override protected int classfileFlags() { return actor.flags() & Actor.JAVA_FIELD_FLAGS; } } /** * A command line interface for producing Maxine preprocessed class files for one or more classes. */ @HOSTED_ONLY public static void main(String[] args) { final OptionSet options = new OptionSet() { @Override protected void printHelpHeader(PrintStream stream) { stream.println("Usage: " + ClassfileWriter.class.getSimpleName() + " [-options] <classes>..."); stream.println(); stream.println("where options include:"); } }; final Option<File> outputDirectoryOption = options.newFileOption("d", new File("generated").getAbsoluteFile(), "Specifies where to place generated class files"); final Option<Boolean> javapOption = options.newBooleanOption("javap", false, "Runs javap on the generated class file(s)."); final Option<Boolean> helpOption = options.newBooleanOption("help", false, "Show help message and exits."); Trace.addTo(options); VMConfigurator vmConfigurator = new VMConfigurator(options); options.parseArguments(args); if (helpOption.getValue()) { options.printHelp(System.out, 80); return; } final String[] arguments = options.getArguments(); if (arguments.length == 0) { options.printHelp(System.out, 80); return; } vmConfigurator.create(); JavaPrototype.initialize(false); final Map<String, byte[]> classNameToClassfileMap = new LinkedHashMap<String, byte[]>(); for (String className : arguments) { processClass(className, outputDirectoryOption.getValue(), classNameToClassfileMap); } Trace.line(1, "Generated " + classNameToClassfileMap.size() + " class file" + (classNameToClassfileMap.size() == 1 ? "" : "s") + " to " + outputDirectoryOption.getValue()); testLoadGeneratedClasses(classNameToClassfileMap, outputDirectoryOption.getValue()); if (javapOption.getValue()) { for (String className : classNameToClassfileMap.keySet()) { final Classpath cp = Classpath.fromSystem().prepend(outputDirectoryOption.getValue().getPath()); final String[] javapArgs = {"-verbose", "-private", "-bootclasspath", cp.toString(), className}; ToolChain.javap(javapArgs); } } } @HOSTED_ONLY private static void processClass(String className, File outputDirectory, Map<String, byte[]> classNameToClassfileMap) { Class<?> javaClass; try { javaClass = Class.forName(className); if (MaxineVM.isHostedOnly(javaClass)) { ProgramWarning.message("Cannot create a class actor for prototype only class: " + className); return; } } catch (VerifyError e) { } catch (ClassNotFoundException e) { ProgramWarning.message("Could not find class: " + className); return; } TypeDescriptor typeDescriptor = JavaTypeDescriptor.getDescriptorForJavaString(className); final ClassActor classActor = typeDescriptor.resolve(null); final byte[] classfileBytes = toByteArray(new ClassInfo(classActor)); final File classfileFile = new File(outputDirectory, classActor.name.string.replace(".", File.separator) + ".class").getAbsoluteFile(); BufferedOutputStream bs = null; try { final File classfileDirectory = classfileFile.getParentFile(); if (!(classfileDirectory.exists())) { if (!classfileDirectory.mkdirs()) { throw new IOException("Could not create directory: " + classfileDirectory); } } bs = new BufferedOutputStream(new FileOutputStream(classfileFile)); bs.write(classfileBytes); classNameToClassfileMap.put(className, classfileBytes); Trace.line(1, "Generated " + classfileFile.getPath()); } catch (IOException ex) { ProgramWarning.message("Error saving class file to " + classfileFile + ": " + ex.getMessage()); return; } finally { if (bs != null) { try { bs.close(); } catch (IOException ex) { } } } } @HOSTED_ONLY static class TestClassLoader extends ClassLoader { public void load(String className, byte[] classfileBytes) { ClassfileReader.defineClassActor(className, this, classfileBytes, null, null, false); } } @HOSTED_ONLY private static void testLoadGeneratedClasses(Map<String, byte[]> classNameToClassfileMap, File outputDirectory) { try { final URL[] urls = {outputDirectory.toURI().toURL()}; final ClassLoader urlClassLoader = URLClassLoader.newInstance(urls); final TestClassLoader testClassLoader = new TestClassLoader(); ClassRegistry.testClassLoader = testClassLoader; for (Map.Entry<String, byte[]> entry : classNameToClassfileMap.entrySet()) { final String className = entry.getKey(); final byte[] classfileBytes = entry.getValue(); try { testClassLoader.load(className, classfileBytes); } catch (LinkageError linkageError) { ProgramWarning.message(linkageError.toString()); } try { Class.forName(className, true, urlClassLoader); } catch (LinkageError linkageError) { ProgramWarning.message(linkageError.toString()); } catch (ClassNotFoundException classNotFoundException) { ProgramWarning.message(classNotFoundException.toString()); } } } catch (MalformedURLException malformedURLException) { ProgramWarning.message(malformedURLException.toString()); } } }