mirror of
https://github.com/corda/corda.git
synced 2025-01-01 02:36:44 +00:00
Corda is an open source blockchain project, designed for business from the start. Only Corda allows you to build interoperable blockchain networks that transact in strict privacy. Corda's smart contract technology allows businesses to transact directly, with value.
17c1a552d5
In order to facilitate making the VM compatible with multiple class libraries, it's useful to separate the VM-specific representation of these classes from the library implementations. This commit introduces VMClass, VMField, and VMMethod for that purpose. |
||
---|---|---|
classpath | ||
src | ||
test | ||
.gitignore | ||
license.txt | ||
makefile | ||
readme.txt | ||
valgrind.supp | ||
vm.pro |
Quick Start ----------- on Linux: $ export JAVA_HOME=/usr/local/java # or wherever you have the JDK installed $ make $ build/linux-i386/avian -cp build/test Hello on Mac OS X: $ export JAVA_HOME=/Library/Java/Home $ make $ build/darwin-i386/avian -cp build/test Hello on Windows (MSYS): $ git clone git://oss.readytalk.com/win32.git ../win32 $ export JAVA_HOME="C:/Program Files/Java/jdk1.6.0_07" $ make $ build/windows-i386/avian -cp build/test Hello on Windows (Cygwin): $ git clone git://oss.readytalk.com/win32.git ../win32 $ export JAVA_HOME="/cygdrive/c/Program Files/Java/jdk1.6.0_07" $ make $ build/windows-i386/avian -cp build/test Hello Adjust JAVA_HOME according to your system, but be sure to use forward slashes in the path. Introduction ------------ Avian is a lightweight virtual machine and class library designed to provide a useful subset of Java's features, suitable for building self-contained applications. More information is available at the project web site: http://oss.readytalk.com/avian If you have any trouble building, running, or embedding Avian, please post a message to our discussion group: http://groups.google.com/group/avian That's also the place for any other questions, comments, or suggestions you might have. Supported Platforms ------------------- Avian can currently target the following platforms: Linux (i386 and x86_64) Windows (i386 and x86_64) Mac OS X (i386, x86_64 and 32-bit PowerPC) Building -------- Build requirements include: * GNU make 3.80 or later * GCC 3.4 or later (4.5 or later for Windows/x86_64) * JDK 1.5 or later * MinGW 3.4 or later (only if compiling for Windows) * zlib 1.2.3 or later Earlier versions of some of these packages may also work but have not been tested. The build is directed by a single makefile and may be influenced via certain flags described below, all of which are optional. $ make platform={linux,windows,darwin} arch={i386,x86_64,powerpc} \ process={compile,interpret} mode={debug,debug-fast,fast,small} \ bootimage={true,false} heapdump={true,false} tails={true,false} \ continuations={true,false} * platform - the target platform default: output of $(uname -s | tr [:upper:] [:lower:]), normalized in some cases (e.g. CYGWIN_NT-5.1 -> windows) * arch - the target architecture default: output of $(uname -m), normalized in some cases (e.g. i686 -> i386) * mode - which set of compilation flags to use to determine optimization level, debug symbols, and whether to enable assertions default: fast * process - choice between pure interpreter or JIT compiler default: compile * bootimage - if true, create a boot image containing the pre-parsed class library and ahead-of-time compiled methods. This option is only valid for process=compile builds. Note that you may need to specify both build-arch=x86_64 and arch=x86_64 on 64-bit systems where "uname -m" prints "i386". default: false * heapdump - if true, implement avian.Machine.dumpHeap(String), which, when called, will generate a snapshot of the heap in a simple, ad-hoc format for memory profiling purposes. See heapdump.cpp for details. default: false * tails - if true, optimize each tail call by replacing the caller's stack frame with the callee's. This convention ensures proper tail recursion, suitable for languages such as Scheme. This option is only valid for process=compile builds. default: false * continuations - if true, support continuations via the avian.Continuations methods callWithCurrentContinuation and dynamicWind. See Continuations.java for details. This option is only valid for process=compile builds. default: false 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 several different sets of options independently and even simultaneously without doing a clean build each time. If you are compiling for Windows, you may either cross-compile using MinGW or build natively on Windows under MSYS or Cygwin. Installing MSYS: 1. Download and install the current MinGW and MSYS packages from mingw.org, selecting the C and C++ compilers when prompted. Use the post-install script to create the filesystem link to the compiler. 2. Download GNU Make 3.81 from the MSYS download page (make-3.81-MSYS-1.0.11-2.tar.bz2) and extract the tar file into e.g. c:/msys/1.0. Installing Cygwin: 1. Download and run setup.exe from cygwin.com, installing the base system and these packages: make, gcc-mingw-g++, and (optionally) git. You may also find our win32 repository useful: (run this from the directory containing the avian directory) $ git clone git://oss.readytalk.com/win32.git This gives you the Windows JNI headers, zlib headers and library, and a few other useful libraries like OpenSSL, libjpeg, and libpng. There's also a win64 repository for 64-bit builds: $ git clone git://oss.readytalk.com/win64.git Building with the Microsoft Visual C++ Compiler ----------------------------------------------- You can also build using the MSVC compiler, which makes debugging with tools like WinDbg and Visual Studio much easier. Note that you will still need to have GCC installed - MSVC is only used to compile the C++ portions of the VM, while the assembly code and helper tools are built using GCC. The MSVC build has been tested with Visual Studio Express Edition versions 8 and 9. Other versions may also work. To build with MSVC, install Cygwin as described above and set the following environment variables: $ export PATH="/usr/local/bin:/usr/bin:/bin:/usr/X11R6/bin:/cygdrive/c/Program Files/Microsoft Visual Studio 9.0/Common7/IDE:/cygdrive/c/Program Files/Microsoft Visual Studio 9.0/VC/BIN:/cygdrive/c/Program Files/Microsoft Visual Studio 9.0/Common7/Tools:/cygdrive/c/WINDOWS/Microsoft.NET/Framework/v3.5:/cygdrive/c/WINDOWS/Microsoft.NET/Framework/v2.0.50727:/cygdrive/c/Program Files/Microsoft Visual Studio 9.0/VC/VCPackages:/cygdrive/c/Program Files/Microsoft SDKs/Windows/v6.0A/bin:/cygdrive/c/WINDOWS/system32:/cygdrive/c/WINDOWS:/cygdrive/c/WINDOWS/System32/Wbem" $ export LIBPATH="C:\WINDOWS\Microsoft.NET\Framework\v3.5;C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727;C:\Program Files\Microsoft Visual Studio 9.0\VC\LIB;" $ export VCINSTALLDIR="C:\Program Files\Microsoft Visual Studio 9.0\VC" $ export LIB="C:\Program Files\Microsoft Visual Studio 9.0\VC\LIB;C:\Program Files\Microsoft SDKs\Windows\v6.0A\lib;" $ export INCLUDE="C:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE;C:\Program Files\Microsoft SDKs\Windows\v6.0A\include;" Adjust these definitions as necessary according to your MSVC installation. Finally, build with the msvc flag set to the MSVC tool directory: $ make msvc="/cygdrive/c/Program Files/Microsoft Visual Studio 9.0/VC" Building with GNU Classpath --------------------------- ** Please note that this feature is still under development and is neither complete nor well-tested. ** By default, Avian uses its own lightweight class library. However, that library only contains a relatively small subset of the classes and methods included in the JRE. If your application requires features beyond that subset, you may want to tell Avian to use GNU Classpath instead. In order for this to work, you must configure Classpath with "--enable-static" and "--with-pic". For example: $ cd classpath-0.98 $ ./configure --prefix=/usr/local/classpath-0.98 --disable-plugin \ --enable-static --with-pic $ make && make install Then, when building Avian, specify the directory where Classpath is installed, e.g.: $ cd ../avian $ make clean $ make gnu=/usr/local/classpath-0.98 This build will use the classes and native code from Classpath, except that certain core classes are replaced with implementations from the Avian class library for compatibility with the VM. Installing ---------- Installing Avian is as simple as copying the executable to the desired directory: $ cp build/${platform}-${arch}/avian ~/bin/ Embedding --------- The following series of commands illustrates how to produce a stand-alone executable out of a Java application using Avian. Note: if you are building on Cygwin, add -mno-cygwin to each of the compile and link commands below. Step 1: Build Avian, create a new directory, and populate it with the VM object files and bootstrap classpath jar. $ make $ mkdir hello $ cd hello $ ar x ../build/${platform}-${arch}/libavian.a $ cp ../build/classpath.jar boot.jar Step 2: Build the Java code and add it to the jar. $ cat >Hello.java <<EOF public class Hello { public static void main(String[] args) { System.out.println("hello, world!"); } } EOF $ javac -bootclasspath boot.jar Hello.java $ jar u0f boot.jar Hello.class Step 3: Make an object file out of the jar. $ ../build/${platform}-${arch}/binaryToObject boot.jar boot-jar.o \ _binary_boot_jar_start _binary_boot_jar_end ${platform} ${arch} Step 4: Write a driver which starts the VM and runs the desired main method. Note the bootJar function, which will be called by the VM to get a handle to the embedded jar. We tell the VM about this jar by setting the classpath to "[bootJar]". $ cat >main.cpp <<EOF #include "stdint.h" #include "jni.h" #ifdef __MINGW32__ # define EXPORT __declspec(dllexport) # define SYMBOL(x) binary_boot_jar_##x #else # define EXPORT __attribute__ ((visibility("default"))) # define SYMBOL(x) _binary_boot_jar_##x #endif extern "C" { extern const uint8_t SYMBOL(start)[]; extern const uint8_t SYMBOL(end)[]; EXPORT const uint8_t* bootJar(unsigned* size) { *size = SYMBOL(end) - SYMBOL(start); return SYMBOL(start); } } // extern "C" int main(int ac, const char** av) { JavaVMInitArgs vmArgs; vmArgs.version = JNI_VERSION_1_2; vmArgs.nOptions = 1; vmArgs.ignoreUnrecognized = JNI_TRUE; JavaVMOption options[vmArgs.nOptions]; vmArgs.options = options; options[0].optionString = const_cast<char*>("-Djava.class.path=[bootJar]"); JavaVM* vm; void* env; JNI_CreateJavaVM(&vm, &env, &vmArgs); JNIEnv* e = static_cast<JNIEnv*>(env); jclass c = e->FindClass("Hello"); if (not e->ExceptionCheck()) { jmethodID m = e->GetStaticMethodID(c, "main", "([Ljava/lang/String;)V"); if (not e->ExceptionCheck()) { jclass stringClass = e->FindClass("java/lang/String"); if (not e->ExceptionCheck()) { jobjectArray a = e->NewObjectArray(ac-1, stringClass, 0); if (not e->ExceptionCheck()) { for (int i = 1; i < ac; ++i) { e->SetObjectArrayElement(a, i-1, e->NewStringUTF(av[i])); } e->CallStaticVoidMethod(c, m, a); } } } } int exitCode = 0; if (e->ExceptionCheck()) { exitCode = -1; e->ExceptionDescribe(); } vm->DestroyJavaVM(); return exitCode; } EOF on Linux: $ g++ -I$JAVA_HOME/include -I$JAVA_HOME/include/linux \ -D_JNI_IMPLEMENTATION_ -c main.cpp -o main.o on Mac OS X: $ g++ -I$JAVA_HOME/include -D_JNI_IMPLEMENTATION_ -c main.cpp -o main.o on Windows: $ g++ -I$JAVA_HOME/include -I$JAVA_HOME/include/win32 \ -D_JNI_IMPLEMENTATION_ -c main.cpp -o main.o Step 5: Link the objects produced above to produce the final executable, and optionally strip its symbols. on Linux: $ g++ -rdynamic *.o -ldl -lpthread -lz -o hello $ strip --strip-all hello on Mac OS X: $ g++ -rdynamic *.o -ldl -lpthread -lz -o hello -framework CoreFoundation $ strip -S -x hello on Windows: $ dlltool -z hello.def *.o $ dlltool -d hello.def -e hello.exp $ g++ hello.exp *.o -L../../win32/lib -lmingwthrd -lm -lz -lws2_32 \ -mwindows -mconsole -o hello.exe $ strip --strip-all hello.exe Embedding with ProGuard and a Boot Image ---------------------------------------- The following illustrates how to embed an application as above, except this time we preprocess the code using ProGuard and build a boot image from it for quicker startup. The pros and cons of using ProGuard are as follow: * Pros: ProGuard will eliminate unused code, optimize the rest, and obfuscate it as well for maximum space savings * Cons: increased build time, especially for large applications, and extra effort needed to configure it for applications which rely heavily on reflection and/or calls to Java from native code For boot image builds: * Pros: the boot image build pre-parses all the classes and compiles all the methods, obviating the need for JIT compilation at runtime. This also makes garbage collection faster, since the pre-parsed classes are never visited. * Cons: the pre-parsed classes and AOT-compiled methods take up more space in the executable than the equivalent class files. In practice, this can make the executable 30-50% larger. Also, AOT compilation does not yet yield significantly faster or smaller code than JIT compilation. 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. Please refer to the previous example for guidance on other platforms. Step 1: Build Avian, create a new directory, and populate it with the VM object files. $ make bootimage=true $ mkdir hello $ cd hello $ ar x ../build/linux-i386-bootimage/libavian.a Step 2: Create a stage1 directory and extract the contents of the class library jar into it. $ mkdir stage1 $ (cd stage1 && jar xf ../../build/classpath.jar) Step 3: Build the Java code and add it to stage1. $ cat >Hello.java <<EOF public class Hello { public static void main(String[] args) { System.out.println("hello, world!"); } } EOF $ javac -bootclasspath stage1 -d stage1 Hello.java Step 4: Create a ProGuard configuration file specifying Hello.main as the entry point. $ cat >hello.pro <<EOF -keep class Hello { public static void main(java.lang.String[]); } EOF Step 5: Run ProGuard with stage1 as input and stage2 as output. $ java -jar ../../proguard4.4/lib/proguard.jar \ -injars stage1 -outjars stage2 @../vm.pro @hello.pro (note: pass -dontusemixedcaseclassnames to ProGuard when building on systems with case-insensitive filesystems such as Windows and OS X) Step 6: Build the boot image. $ ../build/linux-i386-bootimage/bootimage-generator stage2 bootimage.bin Step 7: Make an object file out of the boot image. $ ../build/${platform}-${arch}/binaryToObject \ bootimage.bin bootimage-bin.o \ _binary_bootimage_bin_start _binary_bootimage_bin_end \ ${platform} ${arch} 8 writable executable Step 8: Write a driver which starts the VM and runs the desired main method. Note the bootimageBin function, which will be called by the VM to get a handle to the embedded boot image. We tell the VM about this function via the "avian.bootimage" property. Note also that this example includes no resources besides class files. If our application loaded resources such as images and properties files via the classloader, we would also need to embed the jar file containing them. See the previous example for instructions. $ cat >main.cpp <<EOF #include "stdint.h" #include "jni.h" #ifdef __MINGW32__ # define EXPORT __declspec(dllexport) # define BOOTIMAGE_BIN(x) binary_bootimage_bin_##x #else # define EXPORT __attribute__ ((visibility("default"))) # define BOOTIMAGE_BIN(x) _binary_bootimage_bin_##x #endif extern "C" { extern const uint8_t BOOTIMAGE_BIN(start)[]; extern const uint8_t BOOTIMAGE_BIN(end)[]; EXPORT const uint8_t* bootimageBin(unsigned* size) { *size = BOOTIMAGE_BIN(end) - BOOTIMAGE_BIN(start); return BOOTIMAGE_BIN(start); } } // extern "C" int main(int ac, const char** av) { JavaVMInitArgs vmArgs; vmArgs.version = JNI_VERSION_1_2; vmArgs.nOptions = 1; vmArgs.ignoreUnrecognized = JNI_TRUE; JavaVMOption options[vmArgs.nOptions]; vmArgs.options = options; options[0].optionString = const_cast<char*>("-Davian.bootimage=bootimageBin"); JavaVM* vm; void* env; JNI_CreateJavaVM(&vm, &env, &vmArgs); JNIEnv* e = static_cast<JNIEnv*>(env); jclass c = e->FindClass("Hello"); if (not e->ExceptionCheck()) { jmethodID m = e->GetStaticMethodID(c, "main", "([Ljava/lang/String;)V"); if (not e->ExceptionCheck()) { jclass stringClass = e->FindClass("java/lang/String"); if (not e->ExceptionCheck()) { jobjectArray a = e->NewObjectArray(ac-1, stringClass, 0); if (not e->ExceptionCheck()) { for (int i = 1; i < ac; ++i) { e->SetObjectArrayElement(a, i-1, e->NewStringUTF(av[i])); } e->CallStaticVoidMethod(c, m, a); } } } } int exitCode = 0; if (e->ExceptionCheck()) { exitCode = -1; e->ExceptionDescribe(); } vm->DestroyJavaVM(); return exitCode; } EOF $ g++ -I$JAVA_HOME/include -I$JAVA_HOME/include/linux \ -D_JNI_IMPLEMENTATION_ -c main.cpp -o main.o Step 9: Link the objects produced above to produce the final executable, and optionally strip its symbols. $ g++ -rdynamic *.o -ldl -lpthread -lz -o hello $ strip --strip-all hello