diff --git a/README.md b/README.md index 9e6430e23f..91bbcc0113 100644 --- a/README.md +++ b/README.md @@ -168,8 +168,8 @@ Library" below for details. These flags determine the name of the directory used for the build. The name always starts with _${platform}-${arch}_, and each non-default build option is appended to the name. For example, a debug build with -bootimage enabled on Linux/i386 would be built in -_build/linux-i386-debug-bootimage_. This allows you to build with +bootimage enabled on Linux/x86_64 would be built in +_build/linux-x86_64-debug-bootimage_. This allows you to build with several different sets of options independently and even simultaneously without doing a clean build each time. @@ -575,7 +575,7 @@ For boot image builds: Note you can use ProGuard without using a boot image and vice-versa, as desired. -The following instructions assume we are building for Linux/i386. +The following instructions assume we are building for Linux/x86_64. Please refer to the previous example for guidance on other platforms. __1.__ Build Avian, create a new directory, and populate it with the @@ -584,13 +584,13 @@ VM object files. $ make bootimage=true $ mkdir hello $ cd hello - $ ar x ../build/linux-i386-bootimage/libavian.a + $ ar x ../build/linux-x86_64-bootimage/libavian.a __2.__ Create a stage1 directory and extract the contents of the class library jar into it. $ mkdir stage1 - $ (cd stage1 && jar xf ../../build/linux-i386-bootimage/classpath.jar) + $ (cd stage1 && jar xf ../../build/linux-x86_64-bootimage/classpath.jar) __3.__ Build the Java code and add it to stage1. @@ -630,10 +630,11 @@ using the OpenJDK library.) __6.__ Build the boot and code images. - $ ../build/linux-i386-bootimage/bootimage-generator \ + $ ../build/linux-x86_64-bootimage/bootimage-generator \ -cp stage2 \ -bootimage bootimage-bin.o \ - -codeimage codeimage-bin.o + -codeimage codeimage-bin.o \ + -hostvm ../build/linux-x86_64-interpret/libjvm.so Note that you can override the default names for the start and end symbols in the boot/code image by also passing: diff --git a/classpath/avian/Classes.java b/classpath/avian/Classes.java index 7d428b28d4..b55408834c 100644 --- a/classpath/avian/Classes.java +++ b/classpath/avian/Classes.java @@ -423,6 +423,27 @@ public class Classes { } } + public static VMMethod findMethod(ClassLoader loader, + String class_, + String name, + String spec) + throws ClassNotFoundException + { + VMClass c = SystemClassLoader.vmClass(loader.loadClass(class_)); + VMMethod[] methodTable = c.methodTable; + if (methodTable != null) { + link(c); + + for (int i = 0; i < methodTable.length; ++i) { + VMMethod m = methodTable[i]; + if (toString(m.name).equals(name) && toString(m.spec).equals(spec)) { + return m; + } + } + } + return null; + } + public static int findMethod(VMClass vmClass, String name, Class[] parameterTypes) { diff --git a/classpath/avian/SystemClassLoader.java b/classpath/avian/SystemClassLoader.java index e9970e021d..84b76c9b92 100644 --- a/classpath/avian/SystemClassLoader.java +++ b/classpath/avian/SystemClassLoader.java @@ -20,6 +20,8 @@ import java.util.Enumeration; import java.util.NoSuchElementException; public class SystemClassLoader extends ClassLoader { + public static native ClassLoader appLoader(); + private native VMClass findVMClass(String name) throws ClassNotFoundException; diff --git a/classpath/java/lang/invoke/LambdaMetafactory.java b/classpath/java/lang/invoke/LambdaMetafactory.java index 5a814d1649..f0d57163b3 100644 --- a/classpath/java/lang/invoke/LambdaMetafactory.java +++ b/classpath/java/lang/invoke/LambdaMetafactory.java @@ -187,14 +187,28 @@ public class LambdaMetafactory { return result; } - - public static CallSite metafactory(MethodHandles.Lookup caller, - String invokedName, - MethodType invokedType, - MethodType methodType, - MethodHandle methodImplementation, - MethodType instantiatedMethodType) - throws LambdaConversionException + + public static byte[] makeLambda(String invokedName, + String invokedType, + String methodType, + String implementationClass, + String implementationName, + String implementationSpec, + int implementationKind) + { + return makeLambda(invokedName, + new MethodType(invokedType), + new MethodType(methodType), + new MethodHandle(implementationClass, + implementationName, + implementationSpec, + implementationKind)); + } + + private static byte[] makeLambda(String invokedName, + MethodType invokedType, + MethodType methodType, + MethodHandle methodImplementation) { String className; { int number; @@ -265,8 +279,19 @@ public class LambdaMetafactory { throw error; } - byte[] classData = out.toByteArray(); - + return out.toByteArray(); + } + + public static CallSite metafactory(MethodHandles.Lookup caller, + String invokedName, + MethodType invokedType, + MethodType methodType, + MethodHandle methodImplementation, + MethodType instantiatedMethodType) + throws LambdaConversionException + { + byte[] classData = makeLambda(invokedName, invokedType, methodType, methodImplementation); + try { return new CallSite (new MethodHandle diff --git a/classpath/java/lang/invoke/MethodHandle.java b/classpath/java/lang/invoke/MethodHandle.java index 4efbfbabd6..c676cadeed 100644 --- a/classpath/java/lang/invoke/MethodHandle.java +++ b/classpath/java/lang/invoke/MethodHandle.java @@ -1,6 +1,7 @@ package java.lang.invoke; import avian.Classes; +import avian.SystemClassLoader; public class MethodHandle { static final int REF_invokeStatic = 6; @@ -17,6 +18,20 @@ public class MethodHandle { this.method = method; } + MethodHandle(String class_, + String name, + String spec, + int kind) + { + this.kind = kind; + this.loader = SystemClassLoader.appLoader(); + try { + this.method = Classes.findMethod(this.loader, class_, name, spec); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + public String toString() { StringBuilder sb = new StringBuilder(); if (method.class_ != null) { diff --git a/classpath/java/lang/invoke/MethodType.java b/classpath/java/lang/invoke/MethodType.java index c8bf5bce62..e2030403d9 100644 --- a/classpath/java/lang/invoke/MethodType.java +++ b/classpath/java/lang/invoke/MethodType.java @@ -4,6 +4,7 @@ import static avian.Assembler.*; import avian.Assembler; import avian.Classes; +import avian.SystemClassLoader; import avian.VMClass; import java.util.List; @@ -25,6 +26,12 @@ public final class MethodType implements java.io.Serializable { this.spec = spec; } + MethodType(String spec) { + this.loader = SystemClassLoader.appLoader(); + this.spec = new byte[spec.length() + 1]; + spec.getBytes(0, spec.length(), this.spec, 0); + } + public String toMethodDescriptorString() { return Classes.makeString(spec, 0, spec.length - 1); } diff --git a/makefile b/makefile index ddf9a55bd3..1e7ef0ef4e 100755 --- a/makefile +++ b/makefile @@ -1333,6 +1333,12 @@ bootimage-generator-objects = \ $(call cpp-objects,$(bootimage-generator-sources),$(src),$(build)) bootimage-generator = $(build)/bootimage-generator +ifneq ($(mode),fast) + host-vm-options := -$(mode) +endif + +host-vm = build/$(build-platform)-$(build-arch)-interpret$(host-vm-options)/libjvm.so + bootimage-object = $(build)/bootimage-bin.o codeimage-object = $(build)/codeimage-bin.o @@ -2010,7 +2016,8 @@ $(bootimage-object) $(codeimage-object): $(bootimage-generator) \ @echo "generating bootimage and codeimage binaries from $(classpath-build) using $(<)" $(<) -cp $(classpath-build) -bootimage $(bootimage-object) -codeimage $(codeimage-object) \ -bootimage-symbols $(bootimage-symbols) \ - -codeimage-symbols $(codeimage-symbols) + -codeimage-symbols $(codeimage-symbols) \ + -hostvm $(host-vm) executable-objects = $(vm-objects) $(classpath-objects) $(driver-object) \ $(vm-heapwalk-objects) $(boot-object) $(vm-classpath-objects) \ @@ -2065,6 +2072,7 @@ $(unittest-executable): $(unittest-executable-objects) $(bootimage-generator): $(bootimage-generator-objects) $(vm-objects) echo building $(bootimage-generator) arch=$(build-arch) platform=$(bootimage-platform) + $(MAKE) process=interpret bootimage= mode=$(mode) $(MAKE) mode=$(mode) \ build=$(host-build-root) \ arch=$(build-arch) \ diff --git a/src/avian/bootimage.h b/src/avian/bootimage.h index 9f6cf36b7c..d14755f8b3 100644 --- a/src/avian/bootimage.h +++ b/src/avian/bootimage.h @@ -55,10 +55,13 @@ class BootImage { } PACKED; class GcField; +class GcClass; class OffsetResolver { public: virtual unsigned fieldOffset(Thread*, GcField*) = 0; + + virtual void addClass(Thread*, GcClass*, const uint8_t*, size_t) = 0; }; #define NAME(x) Target##x diff --git a/src/avian/jnienv.h b/src/avian/jnienv.h index b6e5967d3b..9947ade6ec 100644 --- a/src/avian/jnienv.h +++ b/src/avian/jnienv.h @@ -20,6 +20,7 @@ #define EMBED_PREFIX_PROPERTY "avian.embed.prefix" #define CLASSPATH_PROPERTY "java.class.path" #define JAVA_HOME_PROPERTY "java.home" +#define REENTRANT_PROPERTY "avian.reentrant" #define BOOTCLASSPATH_PREPEND_OPTION "bootclasspath/p" #define BOOTCLASSPATH_OPTION "bootclasspath" #define BOOTCLASSPATH_APPEND_OPTION "bootclasspath/a" diff --git a/src/avian/machine.h b/src/avian/machine.h index 9f5654303d..40b627cdbc 100644 --- a/src/avian/machine.h +++ b/src/avian/machine.h @@ -198,6 +198,14 @@ const unsigned ConstructorFlag = 1 << 1; #define JNI_VERSION_1_6 0x00010006 #endif +#ifndef JNI_TRUE +#define JNI_TRUE 1 +#endif + +#ifndef JNI_OK +#define JNI_OK 0 +#endif + typedef Machine JavaVM; typedef Thread JNIEnv; @@ -207,6 +215,19 @@ struct JNINativeMethod { void* function; }; +struct JavaVMOption { + char* optionString; + void* extraInfo; +}; + +struct JavaVMInitArgs { + jint version; + + jint nOptions; + JavaVMOption* options; + jboolean ignoreUnrecognized; +}; + struct JavaVMVTable { void* reserved0; void* reserved1; @@ -3737,10 +3758,10 @@ void populateMultiArray(Thread* t, GcMethod* getCaller(Thread* t, unsigned target, bool skipMethodInvoke = false); -object defineClass(Thread* t, - GcClassLoader* loader, - const uint8_t* buffer, - unsigned length); +GcClass* defineClass(Thread* t, + GcClassLoader* loader, + const uint8_t* buffer, + unsigned length); inline GcMethod* methodClone(Thread* t, GcMethod* method) { diff --git a/src/avian/processor.h b/src/avian/processor.h index 56db019ace..7e9df08ae3 100644 --- a/src/avian/processor.h +++ b/src/avian/processor.h @@ -170,7 +170,8 @@ class Processor { GcTriple** calls, avian::codegen::DelayedPromise** addresses, GcMethod* method, - OffsetResolver* resolver) = 0; + OffsetResolver* resolver, + Machine* hostVM) = 0; virtual void visitRoots(Thread* t, HeapWalker* w) = 0; diff --git a/src/builtin.cpp b/src/builtin.cpp index 8cecd382ad..46b82f4748 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -183,6 +183,12 @@ extern "C" AVIAN_EXPORT int64_t JNICALL t->m->classpath->makeString(t, array, offset, length)); } +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_SystemClassLoader_appLoader(Thread* t, object, uintptr_t*) +{ + return reinterpret_cast(roots(t)->appLoader()); +} + extern "C" AVIAN_EXPORT int64_t JNICALL Avian_avian_SystemClassLoader_findLoadedVMClass(Thread* t, object, diff --git a/src/compile.cpp b/src/compile.cpp index 74605cc9b5..b9899d3124 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -909,14 +909,16 @@ class BootContext { GcTriple* calls, avian::codegen::DelayedPromise* addresses, Zone* zone, - OffsetResolver* resolver) + OffsetResolver* resolver, + JavaVM* hostVM) : protector(t, this), constants(constants), calls(calls), addresses(addresses), addressSentinal(addresses), zone(zone), - resolver(resolver) + resolver(resolver), + hostVM(hostVM) { } @@ -927,6 +929,7 @@ class BootContext { avian::codegen::DelayedPromise* addressSentinal; Zone* zone; OffsetResolver* resolver; + JavaVM* hostVM; }; class Context { @@ -3991,6 +3994,34 @@ void checkField(Thread* t, GcField* field, bool shouldBeStatic) } } +bool isLambda(Thread* t, + GcClassLoader* loader, + GcCharArray* bootstrapArray, + GcInvocation* invocation) +{ + GcMethod* bootstrap = cast(t, + resolve(t, + loader, + invocation->pool(), + bootstrapArray->body()[0], + findMethodInClass, + GcNoSuchMethodError::Type)); + PROTECT(t, bootstrap); + + return vm::strcmp(reinterpret_cast( + "java/lang/invoke/LambdaMetafactory"), + bootstrap->class_()->name()->body().begin()) == 0 + and vm::strcmp(reinterpret_cast("metafactory"), + bootstrap->name()->body().begin()) == 0 + and vm::strcmp( + reinterpret_cast( + "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/" + "String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/" + "MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/" + "invoke/MethodType;)Ljava/lang/invoke/CallSite;"), + bootstrap->spec()->body().begin()) == 0; +} + void compile(MyThread* t, Frame* initialFrame, unsigned initialIp, @@ -5054,36 +5085,168 @@ loop: invocation->setClass(t, context->method->class_()); - unsigned index = addDynamic(t, invocation); + BootContext* bc = context->bootContext; + if (bc) { + // When we're AOT-compiling an application, we can't handle + // invokedynamic in general, since it usually implies runtime + // code generation. However, Java 8 lambda expressions are a + // special case for which we can generate code ahead of time. + // + // The only tricky part about it is that the class synthesis + // code resides in LambdaMetaFactory, which means we need to + // call out to a separate Java VM to execute it (the VM we're + // currently executing in won't work because it only knows how + // to compile code for the target machine, which might not be + // the same as the host; plus we don't want to pollute the + // runtime heap image with stuff that's only needed at compile + // time). - GcMethod* template_ = invocation->template_(); - unsigned returnCode = template_->returnCode(); - unsigned rSize = resultSize(t, returnCode); - unsigned parameterFootprint = template_->parameterFootprint(); + GcClass* c = context->method->class_(); + PROTECT(t, c); - // TODO: can we allow tailCalls in general? - // e.g. what happens if the call site is later bound to a method that can't be tail called? - // NOTE: calling isTailCall right now would cause an segfault, since - // invocation->template_()->class_() will be null. - // bool tailCall - // = isTailCall(t, code, ip, context->method, invocation->template_()); - bool tailCall = false; + GcCharArray* bootstrapArray = cast( + t, + cast(t, c->addendum()->bootstrapMethodTable()) + ->body()[invocation->bootstrap()]); + PROTECT(t, bootstrapArray); - // todo: do we need to tell the compiler to add a load barrier - // here for VolatileCallSite instances? + if (isLambda(t, c->loader(), bootstrapArray, invocation)) { + JNIEnv* e; + if (bc->hostVM->vtable->AttachCurrentThread(bc->hostVM, &e, 0) == 0) { + e->vtable->PushLocalFrame(e, 256); - ir::Value* result = c->stackCall( - c->memory(c->memory(c->threadRegister(), ir::Type::object(), - TARGET_THREAD_DYNAMICTABLE), - ir::Type::object(), index * TargetBytesPerWord), - tailCall ? Compiler::TailJump : 0, frame->trace(0, 0), - operandTypeForFieldCode(t, returnCode), - frame->peekMethodArguments(parameterFootprint)); + jclass lmfClass + = e->vtable->FindClass(e, "java/lang/invoke/LambdaMetafactory"); + jmethodID makeLambda = e->vtable->GetStaticMethodID( + e, + lmfClass, + "makeLambda", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/" + "lang/String;Ljava/lang/String;Ljava/lang/String;I)[B"); - frame->popFootprint(parameterFootprint); + GcReference* reference = cast( + t, + singletonObject( + t, invocation->pool(), bootstrapArray->body()[2])); + int kind = reference->kind(); - if (rSize) { - frame->pushReturnValue(returnCode, result); + GcMethod* method + = cast(t, + resolve(t, + c->loader(), + invocation->pool(), + bootstrapArray->body()[2], + findMethodInClass, + GcNoSuchMethodError::Type)); + + jarray lambda = e->vtable->CallStaticObjectMethod( + e, + lmfClass, + makeLambda, + e->vtable->NewStringUTF( + e, + reinterpret_cast( + invocation->template_()->name()->body().begin())), + e->vtable->NewStringUTF( + e, + reinterpret_cast( + invocation->template_()->spec()->body().begin())), + e->vtable->NewStringUTF( + e, + reinterpret_cast( + cast( + t, + singletonObject(t, + invocation->pool(), + bootstrapArray->body()[1])) + ->body() + .begin())), + e->vtable->NewStringUTF( + e, + reinterpret_cast( + method->class_()->name()->body().begin())), + e->vtable->NewStringUTF(e, + reinterpret_cast( + method->name()->body().begin())), + e->vtable->NewStringUTF(e, + reinterpret_cast( + method->spec()->body().begin())), + kind); + + uint8_t* bytes = reinterpret_cast( + e->vtable->GetPrimitiveArrayCritical(e, lambda, 0)); + + GcClass* lambdaClass + = defineClass(t, + roots(t)->appLoader(), + bytes, + e->vtable->GetArrayLength(e, lambda)); + + bc->resolver->addClass( + t, lambdaClass, bytes, e->vtable->GetArrayLength(e, lambda)); + + e->vtable->ReleasePrimitiveArrayCritical(e, lambda, bytes, 0); + + e->vtable->PopLocalFrame(e, 0); + + THREAD_RUNTIME_ARRAY( + t, char, spec, invocation->template_()->spec()->length()); + memcpy(RUNTIME_ARRAY_BODY(spec), + invocation->template_()->spec()->body().begin(), + invocation->template_()->spec()->length()); + + GcMethod* target = resolveMethod( + t, lambdaClass, "make", RUNTIME_ARRAY_BODY(spec)); + + bool tailCall = isTailCall(t, code, ip, context->method, target); + compileDirectInvoke(t, frame, target, tailCall); + } else { + throwNew( + t, GcVirtualMachineError::Type, "unable to attach to host VM"); + } + } else { + throwNew(t, + GcVirtualMachineError::Type, + "invokedynamic not supported for AOT-compiled code except " + "in the case of lambda expressions"); + } + } else { + unsigned index = addDynamic(t, invocation); + + GcMethod* template_ = invocation->template_(); + unsigned returnCode = template_->returnCode(); + unsigned rSize = resultSize(t, returnCode); + unsigned parameterFootprint = template_->parameterFootprint(); + + // TODO: can we allow tailCalls in general? + // e.g. what happens if the call site is later bound to a method that + // can't be tail called? + // NOTE: calling isTailCall right now would cause an segfault, since + // invocation->template_()->class_() will be null. + // bool tailCall + // = isTailCall(t, code, ip, context->method, + // invocation->template_()); + bool tailCall = false; + + // todo: do we need to tell the compiler to add a load barrier + // here for VolatileCallSite instances? + + ir::Value* result + = c->stackCall(c->memory(c->memory(c->threadRegister(), + ir::Type::object(), + TARGET_THREAD_DYNAMICTABLE), + ir::Type::object(), + index * TargetBytesPerWord), + tailCall ? Compiler::TailJump : 0, + frame->trace(0, 0), + operandTypeForFieldCode(t, returnCode), + frame->peekMethodArguments(parameterFootprint)); + + frame->popFootprint(parameterFootprint); + + if (rSize) { + frame->pushReturnValue(returnCode, result); + } } } break; @@ -9134,10 +9297,12 @@ class MyProcessor : public Processor { GcTriple** calls, avian::codegen::DelayedPromise** addresses, GcMethod* method, - OffsetResolver* resolver) + OffsetResolver* resolver, + JavaVM* hostVM) { MyThread* t = static_cast(vmt); - BootContext bootContext(t, *constants, *calls, *addresses, zone, resolver); + BootContext bootContext( + t, *constants, *calls, *addresses, zone, resolver, hostVM); compile(t, &codeAllocator, &bootContext, method); diff --git a/src/interpret.cpp b/src/interpret.cpp index 67871b40d8..7dac9262b7 100644 --- a/src/interpret.cpp +++ b/src/interpret.cpp @@ -3512,7 +3512,8 @@ class MyProcessor : public Processor { GcTriple**, avian::codegen::DelayedPromise**, GcMethod*, - OffsetResolver*) + OffsetResolver*, + JavaVM*) { abort(s); } diff --git a/src/jnienv.cpp b/src/jnienv.cpp index fdfe120347..82eb3d3169 100644 --- a/src/jnienv.cpp +++ b/src/jnienv.cpp @@ -3617,6 +3617,7 @@ extern "C" AVIAN_EXPORT jint JNICALL const char* bootLibraries = 0; const char* classpath = 0; const char* javaHome = AVIAN_JAVA_HOME; + bool reentrant = false; const char* embedPrefix = AVIAN_EMBED_PREFIX; const char* bootClasspathPrepend = ""; const char* bootClasspath = 0; @@ -3667,6 +3668,9 @@ extern "C" AVIAN_EXPORT jint JNICALL } else if (strncmp(p, JAVA_HOME_PROPERTY "=", sizeof(JAVA_HOME_PROPERTY)) == 0) { javaHome = p + sizeof(JAVA_HOME_PROPERTY); + } else if (strncmp(p, REENTRANT_PROPERTY "=", sizeof(REENTRANT_PROPERTY)) + == 0) { + reentrant = strcmp(p + sizeof(REENTRANT_PROPERTY), "true") == 0; } else if (strncmp(p, EMBED_PREFIX_PROPERTY "=", sizeof(EMBED_PREFIX_PROPERTY)) == 0) { @@ -3689,7 +3693,7 @@ extern "C" AVIAN_EXPORT jint JNICALL ++propertyCount; } - System* s = makeSystem(); + System* s = makeSystem(reentrant); Heap* h = makeHeap(s, heapLimit); Classpath* c = makeClasspath(s, h, javaHome, embedPrefix); diff --git a/src/machine.cpp b/src/machine.cpp index 87cdf5f626..4fbae697eb 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -5889,14 +5889,14 @@ GcMethod* getCaller(Thread* t, unsigned target, bool skipMethodInvoke) return v.method; } -object defineClass(Thread* t, - GcClassLoader* loader, - const uint8_t* buffer, - unsigned length) +GcClass* defineClass(Thread* t, + GcClassLoader* loader, + const uint8_t* buffer, + unsigned length) { PROTECT(t, loader); - object c = parseClass(t, loader, buffer, length); + GcClass* c = parseClass(t, loader, buffer, length); // char name[byteArrayLength(t, className(t, c))]; // memcpy(name, &byteArrayBody(t, className(t, c), 0), @@ -5915,7 +5915,7 @@ object defineClass(Thread* t, PROTECT(t, c); - saveLoadedClass(t, loader, cast(t, c)); + saveLoadedClass(t, loader, c); return c; } diff --git a/src/system/windows.cpp b/src/system/windows.cpp index 2d5dfa4707..5833c28274 100644 --- a/src/system/windows.cpp +++ b/src/system/windows.cpp @@ -1018,6 +1018,7 @@ class MySystem : public System { } HANDLE mutex; + bool reentrant; }; } // namespace diff --git a/src/tools/bootimage-generator/main.cpp b/src/tools/bootimage-generator/main.cpp index 9d5ab08769..1b271a22dd 100644 --- a/src/tools/bootimage-generator/main.cpp +++ b/src/tools/bootimage-generator/main.cpp @@ -306,10 +306,414 @@ unsigned targetFieldOffset(Thread* t, GcHashMap* typeMaps, GcField* field) return offset; } +void addClass(Thread* t, + GcClass* c, + const uint8_t* start, + size_t length, + GcHashMap* typeMaps) +{ + PROTECT(t, c); + PROTECT(t, typeMaps); + + { + class Client : public Stream::Client { + public: + Client(Thread* t) : t(t) + { + } + + virtual void NO_RETURN handleError() + { + abort(t); + } + + private: + Thread* t; + } client(t); + + Stream s(&client, start, length); + + uint32_t magic = s.read4(); + expect(t, magic == 0xCAFEBABE); + s.read2(); // minor version + s.read2(); // major version + + unsigned count = s.read2() - 1; + if (count) { + THREAD_RUNTIME_ARRAY(t, Type, types, count + 2); + RUNTIME_ARRAY_BODY(types)[0] = Type_object; + RUNTIME_ARRAY_BODY(types)[1] = Type_intptr_t; + + for (unsigned i = 2; i < count + 2; ++i) { + unsigned constType = s.read1(); + switch (constType) { + case CONSTANT_Class: + case CONSTANT_String: + RUNTIME_ARRAY_BODY(types)[i] = Type_object; + s.skip(2); + break; + + case CONSTANT_Integer: + case CONSTANT_Float: + RUNTIME_ARRAY_BODY(types)[i] = Type_int32_t; + s.skip(4); + break; + + case CONSTANT_NameAndType: + case CONSTANT_Fieldref: + case CONSTANT_Methodref: + case CONSTANT_InterfaceMethodref: + RUNTIME_ARRAY_BODY(types)[i] = Type_object; + s.skip(4); + break; + + case CONSTANT_Long: + RUNTIME_ARRAY_BODY(types)[i++] = Type_int64_t; + RUNTIME_ARRAY_BODY(types)[i] = Type_int64_t_pad; + s.skip(8); + break; + + case CONSTANT_Double: + RUNTIME_ARRAY_BODY(types)[i++] = Type_double; + RUNTIME_ARRAY_BODY(types)[i] = Type_double_pad; + s.skip(8); + break; + + case CONSTANT_Utf8: + RUNTIME_ARRAY_BODY(types)[i] = Type_object; + s.skip(s.read2()); + break; + + case CONSTANT_MethodHandle: + RUNTIME_ARRAY_BODY(types)[i] = Type_object; + s.skip(3); + break; + + case CONSTANT_MethodType: + RUNTIME_ARRAY_BODY(types)[i] = Type_object; + s.skip(2); + break; + + case CONSTANT_InvokeDynamic: + RUNTIME_ARRAY_BODY(types)[i] = Type_object; + s.skip(4); + break; + + default: + fprintf(stderr, "unknown class constant: %d\n", constType); + abort(t); + } + } + + GcByteArray* array + = makeByteArray(t, TypeMap::sizeInBytes(count + 2, count + 2)); + + TypeMap* map = new (array->body().begin()) + TypeMap(count + 2, count + 2, count + 2, TypeMap::PoolKind); + + for (unsigned i = 0; i < count + 2; ++i) { + expect(t, i < map->buildFixedSizeInWords); + + map->targetFixedOffsets()[i * BytesPerWord] = i * TargetBytesPerWord; + + init(new (map->fixedFields() + i) Field, + RUNTIME_ARRAY_BODY(types)[i], + i* BytesPerWord, + BytesPerWord, + i* TargetBytesPerWord, + TargetBytesPerWord); + } + + hashMapInsert( + t, + typeMaps, + reinterpret_cast(hashMapFind(t, + roots(t)->poolMap(), + reinterpret_cast(c), + objectHash, + objectEqual)), + reinterpret_cast(array), + objectHash); + } + } + + { + GcByteArray* array = 0; + PROTECT(t, array); + + unsigned count = 0; + GcVector* fields = allFields(t, typeMaps, c, &count, &array); + PROTECT(t, fields); + + THREAD_RUNTIME_ARRAY(t, Field, memberFields, count + 1); + + unsigned memberIndex; + unsigned buildMemberOffset; + unsigned targetMemberOffset; + + if (array) { + memberIndex = 0; + buildMemberOffset = 0; + targetMemberOffset = 0; + + TypeMap* map = reinterpret_cast(array->body().begin()); + + for (unsigned j = 0; j < map->fixedFieldCount; ++j) { + Field* f = map->fixedFields() + j; + + RUNTIME_ARRAY_BODY(memberFields)[memberIndex] = *f; + + targetMemberOffset = f->targetOffset + f->targetSize; + + ++memberIndex; + } + } else { + init(new (RUNTIME_ARRAY_BODY(memberFields)) Field, + Type_object, + 0, + BytesPerWord, + 0, + TargetBytesPerWord); + + memberIndex = 1; + buildMemberOffset = BytesPerWord; + targetMemberOffset = TargetBytesPerWord; + } + + const unsigned StaticHeader = 3; + + THREAD_RUNTIME_ARRAY(t, Field, staticFields, count + StaticHeader); + + init(new (RUNTIME_ARRAY_BODY(staticFields)) Field, + Type_object, + 0, + BytesPerWord, + 0, + TargetBytesPerWord); + + init(new (RUNTIME_ARRAY_BODY(staticFields) + 1) Field, + Type_intptr_t, + BytesPerWord, + BytesPerWord, + TargetBytesPerWord, + TargetBytesPerWord); + + init(new (RUNTIME_ARRAY_BODY(staticFields) + 2) Field, + Type_object, + BytesPerWord * 2, + BytesPerWord, + TargetBytesPerWord * 2, + TargetBytesPerWord); + + unsigned staticIndex = StaticHeader; + unsigned buildStaticOffset = BytesPerWord * StaticHeader; + unsigned targetStaticOffset = TargetBytesPerWord * StaticHeader; + + for (unsigned i = 0; i < fields->size(); ++i) { + GcField* field = cast(t, fields->body()[i]); + if (field) { + unsigned buildSize = fieldSize(t, field->code()); + unsigned targetSize = buildSize; + + Type type; + switch (field->code()) { + case ObjectField: + type = Type_object; + targetSize = TargetBytesPerWord; + break; + + case ByteField: + case BooleanField: + type = Type_int8_t; + break; + + case CharField: + case ShortField: + type = Type_int8_t; + break; + + case FloatField: + case IntField: + type = Type_int32_t; + break; + + case LongField: + case DoubleField: + type = Type_int64_t; + break; + + default: + abort(t); + } + + if (field->flags() & ACC_STATIC) { + targetStaticOffset = pad(targetStaticOffset, targetSize); + + buildStaticOffset = field->offset(); + + init(new (RUNTIME_ARRAY_BODY(staticFields) + staticIndex) Field, + type, + buildStaticOffset, + buildSize, + targetStaticOffset, + targetSize); + + targetStaticOffset += targetSize; + + ++staticIndex; + } else { + targetMemberOffset = pad(targetMemberOffset, targetSize); + + buildMemberOffset = field->offset(); + + init(new (RUNTIME_ARRAY_BODY(memberFields) + memberIndex) Field, + type, + buildMemberOffset, + buildSize, + targetMemberOffset, + targetSize); + + targetMemberOffset += targetSize; + + ++memberIndex; + } + } else { + targetMemberOffset = pad(targetMemberOffset, TargetBytesPerWord); + } + } + + if (hashMapFind( + t, typeMaps, reinterpret_cast(c), objectHash, objectEqual) + == 0) { + GcByteArray* array = makeByteArray( + t, + TypeMap::sizeInBytes(ceilingDivide(c->fixedSize(), BytesPerWord), + memberIndex)); + + TypeMap* map = new (array->body().begin()) + TypeMap(ceilingDivide(c->fixedSize(), BytesPerWord), + ceilingDivide(targetMemberOffset, TargetBytesPerWord), + memberIndex); + + for (unsigned i = 0; i < memberIndex; ++i) { + Field* f = RUNTIME_ARRAY_BODY(memberFields) + i; + + expect(t, f->buildOffset < map->buildFixedSizeInWords * BytesPerWord); + + map->targetFixedOffsets()[f->buildOffset] = f->targetOffset; + + map->fixedFields()[i] = *f; + } + + hashMapInsert(t, + typeMaps, + reinterpret_cast(c), + reinterpret_cast(array), + objectHash); + } + + if (c->staticTable()) { + GcByteArray* array = makeByteArray( + t, + TypeMap::sizeInBytes(singletonCount(t, c->staticTable()) + 2, + staticIndex)); + + TypeMap* map = new (array->body().begin()) + TypeMap(singletonCount(t, c->staticTable()) + 2, + ceilingDivide(targetStaticOffset, TargetBytesPerWord), + staticIndex, + TypeMap::SingletonKind); + + for (unsigned i = 0; i < staticIndex; ++i) { + Field* f = RUNTIME_ARRAY_BODY(staticFields) + i; + + expect(t, f->buildOffset < map->buildFixedSizeInWords * BytesPerWord); + + map->targetFixedOffsets()[f->buildOffset] = f->targetOffset; + + map->fixedFields()[i] = *f; + } + + hashMapInsert(t, + typeMaps, + reinterpret_cast(c->staticTable()), + reinterpret_cast(array), + objectHash); + } + } +} + +void compileMethods(Thread* t, + GcClass* c, + Zone* zone, + GcTriple** constants, + GcTriple** calls, + GcPair** methods, + DelayedPromise** addresses, + OffsetResolver* resolver, + JavaVM* hostVM, + const char* methodName, + const char* methodSpec) +{ + PROTECT(t, c); + + if (GcArray* mtable = cast(t, c->methodTable())) { + PROTECT(t, mtable); + for (unsigned i = 0; i < mtable->length(); ++i) { + GcMethod* method = cast(t, mtable->body()[i]); + if (((methodName == 0 + or ::strcmp(reinterpret_cast(method->name()->body().begin()), + methodName) == 0) + and (methodSpec == 0 + or ::strcmp( + reinterpret_cast(method->spec()->body().begin()), + methodSpec) == 0))) { + if (method->code() or (method->flags() & ACC_NATIVE)) { + PROTECT(t, method); + + t->m->processor->compileMethod( + t, zone, constants, calls, addresses, method, resolver, hostVM); + + if (method->code()) { + *methods = makePair(t, + reinterpret_cast(method), + reinterpret_cast(*methods)); + } + } + + GcMethodAddendum* addendum = method->addendum(); + if (addendum and addendum->exceptionTable()) { + PROTECT(t, addendum); + GcShortArray* exceptionTable + = cast(t, addendum->exceptionTable()); + PROTECT(t, exceptionTable); + + // resolve exception types now to avoid trying to update + // immutable references at runtime + for (unsigned i = 0; i < exceptionTable->length(); ++i) { + uint16_t index = exceptionTable->body()[i] - 1; + + object o = singletonObject(t, addendum->pool(), index); + + if (objectClass(t, o) == type(t, GcReference::Type)) { + o = reinterpret_cast(resolveClass( + t, roots(t)->bootLoader(), cast(t, o)->name())); + + addendum->pool()->setBodyElement( + t, index, reinterpret_cast(o)); + } + } + } + } + } + } +} + GcTriple* makeCodeImage(Thread* t, Zone* zone, BootImage* image, uint8_t* code, + JavaVM* hostVM, const char* className, const char* methodName, const char* methodSpec, @@ -319,20 +723,13 @@ GcTriple* makeCodeImage(Thread* t, t->m->classpath->interceptMethods(t); - GcTriple* constants = 0; - PROTECT(t, constants); - - GcTriple* calls = 0; - PROTECT(t, calls); - - GcPair* methods = 0; - PROTECT(t, methods); - - DelayedPromise* addresses = 0; + GcPair* classes = 0; + PROTECT(t, classes); class MyOffsetResolver : public OffsetResolver { public: - MyOffsetResolver(GcHashMap** typeMaps) : typeMaps(typeMaps) + MyOffsetResolver(GcHashMap** typeMaps, GcPair** classes) + : typeMaps(typeMaps), classes(classes) { } @@ -341,8 +738,22 @@ GcTriple* makeCodeImage(Thread* t, return targetFieldOffset(t, *typeMaps, field); } + virtual void addClass(Thread* t, + GcClass* c, + const uint8_t* start, + size_t length) + { + PROTECT(t, c); + + *classes = makePair( + t, reinterpret_cast(c), reinterpret_cast(*classes)); + + return ::addClass(t, c, start, length, *typeMaps); + } + GcHashMap** typeMaps; - } resolver(&typeMaps); + GcPair** classes; + } resolver(&typeMaps, &classes); Finder* finder = static_cast( roots(t)->bootLoader()->as(t)->finder()); @@ -356,354 +767,32 @@ GcTriple* makeCodeImage(Thread* t, if (false) { fprintf(stderr, "pass 1 %.*s\n", (int)nameSize - 6, name); } + GcClass* c = resolveSystemClass(t, roots(t)->bootLoader(), makeByteArray(t, "%.*s", nameSize - 6, name), true); - PROTECT(t, c); - System::Region* region = finder->find(name); - { - THREAD_RESOURCE(t, System::Region*, region, region->dispose()); + THREAD_RESOURCE(t, System::Region*, region, region->dispose()); - class Client : public Stream::Client { - public: - Client(Thread* t) : t(t) - { - } - - virtual void NO_RETURN handleError() - { - abort(t); - } - - private: - Thread* t; - } client(t); - - Stream s(&client, region->start(), region->length()); - - uint32_t magic = s.read4(); - expect(t, magic == 0xCAFEBABE); - s.read2(); // minor version - s.read2(); // major version - - unsigned count = s.read2() - 1; - if (count) { - THREAD_RUNTIME_ARRAY(t, Type, types, count + 2); - RUNTIME_ARRAY_BODY(types)[0] = Type_object; - RUNTIME_ARRAY_BODY(types)[1] = Type_intptr_t; - - for (unsigned i = 2; i < count + 2; ++i) { - unsigned constType = s.read1(); - switch (constType) { - case CONSTANT_Class: - case CONSTANT_String: - RUNTIME_ARRAY_BODY(types)[i] = Type_object; - s.skip(2); - break; - - case CONSTANT_Integer: - case CONSTANT_Float: - RUNTIME_ARRAY_BODY(types)[i] = Type_int32_t; - s.skip(4); - break; - - case CONSTANT_NameAndType: - case CONSTANT_Fieldref: - case CONSTANT_Methodref: - case CONSTANT_InterfaceMethodref: - RUNTIME_ARRAY_BODY(types)[i] = Type_object; - s.skip(4); - break; - - case CONSTANT_Long: - RUNTIME_ARRAY_BODY(types)[i++] = Type_int64_t; - RUNTIME_ARRAY_BODY(types)[i] = Type_int64_t_pad; - s.skip(8); - break; - - case CONSTANT_Double: - RUNTIME_ARRAY_BODY(types)[i++] = Type_double; - RUNTIME_ARRAY_BODY(types)[i] = Type_double_pad; - s.skip(8); - break; - - case CONSTANT_Utf8: - RUNTIME_ARRAY_BODY(types)[i] = Type_object; - s.skip(s.read2()); - break; - - - - case CONSTANT_MethodHandle: - RUNTIME_ARRAY_BODY(types)[i] = Type_object; - s.skip(3); - break; - - case CONSTANT_MethodType: - RUNTIME_ARRAY_BODY(types)[i] = Type_object; - s.skip(2); - break; - - case CONSTANT_InvokeDynamic: - RUNTIME_ARRAY_BODY(types)[i] = Type_object; - s.skip(4); - break; - - default: - fprintf(stderr, "unknown class constant: %d\n", constType); - abort(t); - } - } - - GcByteArray* array - = makeByteArray(t, TypeMap::sizeInBytes(count + 2, count + 2)); - - TypeMap* map = new (array->body().begin()) - TypeMap(count + 2, count + 2, count + 2, TypeMap::PoolKind); - - for (unsigned i = 0; i < count + 2; ++i) { - expect(t, i < map->buildFixedSizeInWords); - - map->targetFixedOffsets()[i * BytesPerWord] = i - * TargetBytesPerWord; - - init(new (map->fixedFields() + i) Field, - RUNTIME_ARRAY_BODY(types)[i], - i* BytesPerWord, - BytesPerWord, - i* TargetBytesPerWord, - TargetBytesPerWord); - } - - hashMapInsert( - t, - typeMaps, - reinterpret_cast(hashMapFind(t, - roots(t)->poolMap(), - reinterpret_cast(c), - objectHash, - objectEqual)), - reinterpret_cast(array), - objectHash); - } - } - - { - GcByteArray* array = 0; - PROTECT(t, array); - - unsigned count = 0; - GcVector* fields = allFields(t, typeMaps, c, &count, &array); - PROTECT(t, fields); - - THREAD_RUNTIME_ARRAY(t, Field, memberFields, count + 1); - - unsigned memberIndex; - unsigned buildMemberOffset; - unsigned targetMemberOffset; - - if (array) { - memberIndex = 0; - buildMemberOffset = 0; - targetMemberOffset = 0; - - TypeMap* map = reinterpret_cast(array->body().begin()); - - for (unsigned j = 0; j < map->fixedFieldCount; ++j) { - Field* f = map->fixedFields() + j; - - RUNTIME_ARRAY_BODY(memberFields)[memberIndex] = *f; - - targetMemberOffset = f->targetOffset + f->targetSize; - - ++memberIndex; - } - } else { - init(new (RUNTIME_ARRAY_BODY(memberFields)) Field, - Type_object, - 0, - BytesPerWord, - 0, - TargetBytesPerWord); - - memberIndex = 1; - buildMemberOffset = BytesPerWord; - targetMemberOffset = TargetBytesPerWord; - } - - const unsigned StaticHeader = 3; - - THREAD_RUNTIME_ARRAY(t, Field, staticFields, count + StaticHeader); - - init(new (RUNTIME_ARRAY_BODY(staticFields)) Field, - Type_object, - 0, - BytesPerWord, - 0, - TargetBytesPerWord); - - init(new (RUNTIME_ARRAY_BODY(staticFields) + 1) Field, - Type_intptr_t, - BytesPerWord, - BytesPerWord, - TargetBytesPerWord, - TargetBytesPerWord); - - init(new (RUNTIME_ARRAY_BODY(staticFields) + 2) Field, - Type_object, - BytesPerWord * 2, - BytesPerWord, - TargetBytesPerWord * 2, - TargetBytesPerWord); - - unsigned staticIndex = StaticHeader; - unsigned buildStaticOffset = BytesPerWord * StaticHeader; - unsigned targetStaticOffset = TargetBytesPerWord * StaticHeader; - - for (unsigned i = 0; i < fields->size(); ++i) { - GcField* field = cast(t, fields->body()[i]); - if (field) { - unsigned buildSize = fieldSize(t, field->code()); - unsigned targetSize = buildSize; - - Type type; - switch (field->code()) { - case ObjectField: - type = Type_object; - targetSize = TargetBytesPerWord; - break; - - case ByteField: - case BooleanField: - type = Type_int8_t; - break; - - case CharField: - case ShortField: - type = Type_int8_t; - break; - - case FloatField: - case IntField: - type = Type_int32_t; - break; - - case LongField: - case DoubleField: - type = Type_int64_t; - break; - - default: - abort(t); - } - - if (field->flags() & ACC_STATIC) { - targetStaticOffset = pad(targetStaticOffset, targetSize); - - buildStaticOffset = field->offset(); - - init(new (RUNTIME_ARRAY_BODY(staticFields) + staticIndex) Field, - type, - buildStaticOffset, - buildSize, - targetStaticOffset, - targetSize); - - targetStaticOffset += targetSize; - - ++staticIndex; - } else { - targetMemberOffset = pad(targetMemberOffset, targetSize); - - buildMemberOffset = field->offset(); - - init(new (RUNTIME_ARRAY_BODY(memberFields) + memberIndex) Field, - type, - buildMemberOffset, - buildSize, - targetMemberOffset, - targetSize); - - targetMemberOffset += targetSize; - - ++memberIndex; - } - } else { - targetMemberOffset = pad(targetMemberOffset, TargetBytesPerWord); - } - } - - if (hashMapFind(t, - typeMaps, - reinterpret_cast(c), - objectHash, - objectEqual) == 0) { - GcByteArray* array = makeByteArray( - t, - TypeMap::sizeInBytes(ceilingDivide(c->fixedSize(), BytesPerWord), - memberIndex)); - - TypeMap* map = new (array->body().begin()) - TypeMap(ceilingDivide(c->fixedSize(), BytesPerWord), - ceilingDivide(targetMemberOffset, TargetBytesPerWord), - memberIndex); - - for (unsigned i = 0; i < memberIndex; ++i) { - Field* f = RUNTIME_ARRAY_BODY(memberFields) + i; - - expect(t, - f->buildOffset < map->buildFixedSizeInWords * BytesPerWord); - - map->targetFixedOffsets()[f->buildOffset] = f->targetOffset; - - map->fixedFields()[i] = *f; - } - - hashMapInsert(t, - typeMaps, - reinterpret_cast(c), - reinterpret_cast(array), - objectHash); - } - - if (c->staticTable()) { - GcByteArray* array = makeByteArray( - t, - TypeMap::sizeInBytes(singletonCount(t, c->staticTable()) + 2, - staticIndex)); - - TypeMap* map = new (array->body().begin()) - TypeMap(singletonCount(t, c->staticTable()) + 2, - ceilingDivide(targetStaticOffset, TargetBytesPerWord), - staticIndex, - TypeMap::SingletonKind); - - for (unsigned i = 0; i < staticIndex; ++i) { - Field* f = RUNTIME_ARRAY_BODY(staticFields) + i; - - expect(t, - f->buildOffset < map->buildFixedSizeInWords * BytesPerWord); - - map->targetFixedOffsets()[f->buildOffset] = f->targetOffset; - - map->fixedFields()[i] = *f; - } - - hashMapInsert(t, - typeMaps, - reinterpret_cast(c->staticTable()), - reinterpret_cast(array), - objectHash); - } - } + addClass(t, c, region->start(), region->length(), typeMaps); } } + GcTriple* constants = 0; + PROTECT(t, constants); + + GcTriple* calls = 0; + PROTECT(t, calls); + + GcPair* methods = 0; + PROTECT(t, methods); + + DelayedPromise* addresses = 0; + for (Finder::Iterator it(finder); it.hasMore();) { size_t nameSize = 0; const char* name = it.next(&nameSize); @@ -713,73 +802,39 @@ GcTriple* makeCodeImage(Thread* t, if (false) { fprintf(stderr, "pass 2 %.*s\n", (int)nameSize - 6, name); } - GcClass* c = 0; - PROTECT(t, c); - c = resolveSystemClass(t, - roots(t)->bootLoader(), - makeByteArray(t, "%.*s", nameSize - 6, name), - true); + GcClass* c + = resolveSystemClass(t, + roots(t)->bootLoader(), + makeByteArray(t, "%.*s", nameSize - 6, name), + true); - if (GcArray* mtable = cast(t, c->methodTable())) { - PROTECT(t, mtable); - for (unsigned i = 0; i < mtable->length(); ++i) { - GcMethod* method = cast(t, mtable->body()[i]); - if (((methodName == 0 - or ::strcmp( - reinterpret_cast(method->name()->body().begin()), - methodName) == 0) - and (methodSpec == 0 - or ::strcmp(reinterpret_cast( - method->spec()->body().begin()), - methodSpec) == 0))) { - if (method->code() or (method->flags() & ACC_NATIVE)) { - PROTECT(t, method); + classes = makePair( + t, reinterpret_cast(c), reinterpret_cast(classes)); + } + } - t->m->processor->compileMethod( - t, - zone, - reinterpret_cast(&constants), - reinterpret_cast(&calls), - &addresses, - method, - &resolver); + // Each method compilation may result in the creation of new, + // synthetic classes (e.g. for lambda expressions), so we must + // iterate until we've visited them all: + while (classes) { + GcPair* myClasses = classes; + PROTECT(t, myClasses); - if (method->code()) { - methods = makePair(t, - reinterpret_cast(method), - reinterpret_cast(methods)); - } - } + classes = 0; - GcMethodAddendum* addendum = method->addendum(); - if (addendum and addendum->exceptionTable()) { - PROTECT(t, addendum); - GcShortArray* exceptionTable - = cast(t, addendum->exceptionTable()); - PROTECT(t, exceptionTable); - - // resolve exception types now to avoid trying to update - // immutable references at runtime - for (unsigned i = 0; i < exceptionTable->length(); ++i) { - uint16_t index = exceptionTable->body()[i] - 1; - - object o = singletonObject(t, addendum->pool(), index); - - if (objectClass(t, o) == type(t, GcReference::Type)) { - o = reinterpret_cast( - resolveClass(t, - roots(t)->bootLoader(), - cast(t, o)->name())); - - addendum->pool()->setBodyElement( - t, index, reinterpret_cast(o)); - } - } - } - } - } - } + for (; myClasses; myClasses = cast(t, myClasses->second())) { + compileMethods(t, + cast(t, myClasses->first()), + zone, + &constants, + &calls, + &methods, + &addresses, + &resolver, + hostVM, + methodName, + methodSpec); } } @@ -1436,6 +1491,7 @@ void writeBootImage2(Thread* t, OutputStream* codeOutput, BootImage* image, uint8_t* code, + JavaVM* hostVM, const char* className, const char* methodName, const char* methodSpec, @@ -1656,8 +1712,15 @@ void writeBootImage2(Thread* t, objectHash); } - constants = makeCodeImage( - t, &zone, image, code, className, methodName, methodSpec, typeMaps); + constants = makeCodeImage(t, + &zone, + image, + code, + hostVM, + className, + methodName, + methodSpec, + typeMaps); PROTECT(t, constants); @@ -1927,21 +1990,23 @@ uint64_t writeBootImage(Thread* t, uintptr_t* arguments) OutputStream* codeOutput = reinterpret_cast(arguments[1]); BootImage* image = reinterpret_cast(arguments[2]); uint8_t* code = reinterpret_cast(arguments[3]); - const char* className = reinterpret_cast(arguments[4]); - const char* methodName = reinterpret_cast(arguments[5]); - const char* methodSpec = reinterpret_cast(arguments[6]); + JavaVM* hostVM = reinterpret_cast(arguments[4]); + const char* className = reinterpret_cast(arguments[5]); + const char* methodName = reinterpret_cast(arguments[6]); + const char* methodSpec = reinterpret_cast(arguments[7]); - const char* bootimageStart = reinterpret_cast(arguments[7]); - const char* bootimageEnd = reinterpret_cast(arguments[8]); - const char* codeimageStart = reinterpret_cast(arguments[9]); - const char* codeimageEnd = reinterpret_cast(arguments[10]); - bool useLZMA = arguments[11]; + const char* bootimageStart = reinterpret_cast(arguments[8]); + const char* bootimageEnd = reinterpret_cast(arguments[9]); + const char* codeimageStart = reinterpret_cast(arguments[10]); + const char* codeimageEnd = reinterpret_cast(arguments[11]); + bool useLZMA = arguments[12]; writeBootImage2(t, bootimageOutput, codeOutput, image, code, + hostVM, className, methodName, methodSpec, @@ -1969,6 +2034,8 @@ class Arguments { const char* bootimage; const char* codeimage; + const char* hostvm; + char* entryClass; char* entryMethod; char* entrySpec; @@ -2008,6 +2075,7 @@ class Arguments { Arg classpath(parser, true, "cp", ""); Arg bootimage(parser, true, "bootimage", ""); Arg codeimage(parser, true, "codeimage", ""); + Arg hostvm(parser, false, "hostvm", ""); Arg entry( parser, false, "entry", "[.[]]"); Arg bootimageSymbols(parser, @@ -2028,6 +2096,7 @@ class Arguments { this->classpath = classpath.value; this->bootimage = bootimage.value; this->codeimage = codeimage.value; + this->hostvm = hostvm.value; this->useLZMA = useLZMA.value != 0; if (entry.value) { @@ -2100,6 +2169,7 @@ class Arguments { "classpath = %s\n" "bootimage = %s\n" "codeimage = %s\n" + "hostvm = %s\n" "entryClass = %s\n" "entryMethod = %s\n" "entrySpec = %s\n" @@ -2110,6 +2180,7 @@ class Arguments { classpath, bootimage, codeimage, + hostvm, entryClass, entryMethod, entrySpec, @@ -2168,10 +2239,66 @@ int main(int ac, const char** av) return -1; } + JavaVM* hostVM = 0; + System::Library* hostVMLibrary = 0; + if (args.hostvm) { + if (s->success(s->load(&hostVMLibrary, args.hostvm))) { + typedef jint(JNICALL * CreateVM)(Machine**, Thread**, void*); + const char* name = "JNI_CreateJavaVM"; + CreateVM createVM + = reinterpret_cast(hostVMLibrary->resolve(name)); + + if (createVM) { + JavaVMInitArgs vmArgs; + vmArgs.version = JNI_VERSION_1_6; + vmArgs.nOptions = 2; + vmArgs.ignoreUnrecognized = JNI_TRUE; + +#define CLASSPATH_PROPERTY "-Xbootclasspath:" + + const char* classpath = args.classpath; + size_t classpathSize = strlen(classpath); + size_t classpathPropertyBufferSize = sizeof(CLASSPATH_PROPERTY) + + classpathSize; + + RUNTIME_ARRAY( + char, classpathPropertyBuffer, classpathPropertyBufferSize); + memcpy(RUNTIME_ARRAY_BODY(classpathPropertyBuffer), + CLASSPATH_PROPERTY, + sizeof(CLASSPATH_PROPERTY) - 1); + memcpy(RUNTIME_ARRAY_BODY(classpathPropertyBuffer) + + sizeof(CLASSPATH_PROPERTY) - 1, + classpath, + classpathSize + 1); + + JavaVMOption options[2]; + options[0].optionString = RUNTIME_ARRAY_BODY(classpathPropertyBuffer); + options[1].optionString = const_cast("-Davian.reentrant=true"); + + vmArgs.options = options; + + Thread* dummy; + if (JNI_OK != createVM(&hostVM, &dummy, &vmArgs)) { + fprintf(stderr, "unable to initialize host VM\n"); + hostVMLibrary->disposeAll(); + return -1; + } + } else { + fprintf(stderr, "unable to find %s in %s\n", name, args.hostvm); + hostVMLibrary->disposeAll(); + return -1; + } + } else { + fprintf(stderr, "unable to open %s\n", args.hostvm); + return -1; + } + } + uintptr_t arguments[] = {reinterpret_cast(&bootimageOutput), reinterpret_cast(&codeOutput), reinterpret_cast(&image), reinterpret_cast(code.begin()), + reinterpret_cast(hostVM), reinterpret_cast(args.entryClass), reinterpret_cast(args.entryMethod), reinterpret_cast(args.entrySpec), @@ -2183,6 +2310,11 @@ int main(int ac, const char** av) run(t, writeBootImage, arguments); + if (hostVM) { + hostVM->vtable->DestroyJavaVM(hostVM); + hostVMLibrary->disposeAll(); + } + if (t->exception) { printTrace(t, t->exception); return -1;