From d5a5b5309a43028363750d5a9ec7a10b052366dc Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Sat, 12 Sep 2015 20:15:46 -0600 Subject: [PATCH] support AOT-compilation of Java 8 lambda expressions These expressions are tricky because they rely on invokedynamic, which normally implies runtime code generation. However, since lambdas don't actually use the "dynamicness" of invokedynamic, we can convert them into static calls to synthetic classes at compile time. Since I had already written code to synthesize such classes in Java and I didn't want to rewrite it in C++, I needed to add support for running Java code to the bootimage generator. And since the primary VM used by the generator is purpose-built to generate AOT-compiled code for a specific target architecture and is not capable of generating or running JIT-compiled code for the host architecture, I added support for loading a second, independent, host-specific VM for running Java code. The rest of the patch handles the fact that each method compilation might cause new, synthetic classes to be created, so we need to make sure those classes and their methods are included in the final heap and code images. This required breaking some giant code blocks out of makeCodeImage into their own methods, which makes the diff look scarier than it really is. --- README.md | 15 +- classpath/avian/Classes.java | 21 + classpath/avian/SystemClassLoader.java | 2 + .../java/lang/invoke/LambdaMetafactory.java | 45 +- classpath/java/lang/invoke/MethodHandle.java | 15 + classpath/java/lang/invoke/MethodType.java | 7 + makefile | 10 +- src/avian/bootimage.h | 3 + src/avian/jnienv.h | 1 + src/avian/machine.h | 29 +- src/avian/processor.h | 3 +- src/builtin.cpp | 6 + src/compile.cpp | 221 +++- src/interpret.cpp | 3 +- src/jnienv.cpp | 6 +- src/machine.cpp | 12 +- src/system/windows.cpp | 1 + src/tools/bootimage-generator/main.cpp | 972 ++++++++++-------- 18 files changed, 893 insertions(+), 479 deletions(-) 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;