diff --git a/sgx-jvm/avian/.clang-format b/sgx-jvm/avian/.clang-format new file mode 100644 index 0000000000..910160fcbe --- /dev/null +++ b/sgx-jvm/avian/.clang-format @@ -0,0 +1,7 @@ +--- +BasedOnStyle: Chromium +IndentCaseLabels: false +BreakBeforeBraces: Stroustrup +AllowShortFunctionsOnASingleLine: false +BreakBeforeBinaryOperators: true +... diff --git a/sgx-jvm/avian/.gitignore b/sgx-jvm/avian/.gitignore new file mode 100644 index 0000000000..989c2222e8 --- /dev/null +++ b/sgx-jvm/avian/.gitignore @@ -0,0 +1,17 @@ +.gdb_history +/build +*~ +.classpath +.project +.settings +bin +/lib +/distrib +*.pdb +*.swp +/.gradle +/*.sublime-* +workspace/ +src/.cproject +/cmake-build +/cmake-build diff --git a/sgx-jvm/avian/.mailmap b/sgx-jvm/avian/.mailmap new file mode 100644 index 0000000000..12fd7d9627 --- /dev/null +++ b/sgx-jvm/avian/.mailmap @@ -0,0 +1,27 @@ +Carsten Elton Sørensen +Carsten Elton Sørensen +Dain Darnell +Dain Darnell +Edison Guo +Jason Treadwell +Jason Treadwell +Jason Treadwell +Jason Treadwell +Jason Treadwell +Jason Treadwell +Jason Treadwell +Jason Treadwell +Jason Treadwell +Joel Dice +Joel Dice +Joel Dice +Joel Dice +Anonymous +Joshua Warner +Matt Weaver +Mike Jensen +Mike Jensen +Mike Jensen +Terek Campbell +Thiago Bedin Frustaci +Zsombor Gegesy diff --git a/sgx-jvm/avian/.travis.yml b/sgx-jvm/avian/.travis.yml new file mode 100644 index 0000000000..42265efb47 --- /dev/null +++ b/sgx-jvm/avian/.travis.yml @@ -0,0 +1,25 @@ +language: cpp +cache: apt + +os: + - linux + - osx + +env: + matrix: + - BUILD_STEP="" +# disabled until/unless jfrog.org credentials are updated and someone +# decides they care about published artifacts: +# - BUILD_STEP="PUBLISH" + global: + - TERM=dumb + - secure: rh1utD4shKmYtokItuRYEF9WsfTnvZO5XqnTU4DHTS7quHHgLihtOO2/3+B+2W2hEd5Obr2or8zx+zmzWcNUyLokZ0j/FRLWSScNkLzTtm12pupLrncY+/g1NIdfbhn+OLRIzBz6zB6m6a2qWFEJ+bScUNGD/7wZVtzkujqlDEE= + - secure: j9DOzZMCYk/BzhKK9u4XMKpCzyGOsvP2cLTp6cXE7/tkWDAPVv6BFmeqNbiLTEqk0aGX+HYbY/2YVtpRZmDzfeWtnBFF5mL1Y1tgzx1Kf155C+P6rZgt5PiQTUdXlp2umuRifY1BbXAPc3DZ2UOPUjWKnLHVbZLQRgO1zimmMx8= + +matrix: + fast_finish: true + exclude: + - os: osx + env: BUILD_STEP="" + +script: ./test/ci.sh ${BUILD_STEP} diff --git a/sgx-jvm/avian/.utility/push-javadoc-to-gh-pages.sh b/sgx-jvm/avian/.utility/push-javadoc-to-gh-pages.sh new file mode 100755 index 0000000000..c3690b49de --- /dev/null +++ b/sgx-jvm/avian/.utility/push-javadoc-to-gh-pages.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# This script was originally written by maxiaohao in the aws-mock GitHub project. +# https://github.com/treelogic-swe/aws-mock/ + +if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then + + echo "Start to publish lastest Javadoc to gh-pages..." + + cd build/ + + if test -d gh-pages/avian-web + then + cd gh-pages/avian-web + git pull + else + git clone --quiet https://${GH_TOKEN}@github.com/ReadyTalk/readytalk.github.io gh-pages > /dev/null + cd gh-pages/avian-web + git config user.email "travis@travis-ci.org" + git config user.name "travis-ci" + fi + + git rm -rf ./javadoc + cp -Rf ../javadoc ./javadoc + git add -f . + git commit -m "Latest javadoc on successful Travis build $TRAVIS_BUILD_NUMBER auto-pushed to readytalk.github.io" + if ! git push -fq origin master &> /dev/null; then + echo "Error pushing gh-pages to origin. Bad GH_TOKEN? GitHub down?" + else + echo "Done magic with auto publishment to readytalk.github.io." + fi +fi diff --git a/sgx-jvm/avian/CMakeLists.txt b/sgx-jvm/avian/CMakeLists.txt new file mode 100644 index 0000000000..19c4b900b6 --- /dev/null +++ b/sgx-jvm/avian/CMakeLists.txt @@ -0,0 +1,29 @@ +# NOTE that this CMake file doesn't current build all of avian. +# It only builds what's required for example/kaleidoscope. + +cmake_minimum_required (VERSION 2.6) +project (avian) + +include_directories (include src) + +add_definitions ( + -DAVIAN_TARGET_FORMAT=AVIAN_FORMAT_MACHO + + -DAVIAN_TARGET_ARCH=AVIAN_ARCH_X86_64 + + -DTARGET_BYTES_PER_WORD=8 + -D__STDC_LIMIT_MACROS + -D__STDC_CONSTANT_MACROS +) + +include ("cmake/Platform.cmake") + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${PLATFORM_CXX_FLAGS}") + +include (CTest) + +# Sadly, we can't use the 'test' target, as that's coopted by ctest +add_custom_target(check ${CMAKE_CTEST_COMMAND} -V) + +add_subdirectory (src) +add_subdirectory (unittest) diff --git a/sgx-jvm/avian/LICENSE.txt b/sgx-jvm/avian/LICENSE.txt new file mode 100644 index 0000000000..71731a35a9 --- /dev/null +++ b/sgx-jvm/avian/LICENSE.txt @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2008-2015, Avian Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/sgx-jvm/avian/README.md b/sgx-jvm/avian/README.md new file mode 100644 index 0000000000..80434004dc --- /dev/null +++ b/sgx-jvm/avian/README.md @@ -0,0 +1,750 @@ +Avian - A lightweight Java Virtual Machine (JVM) +================================================ + +[![Build Status](https://travis-ci.org/ReadyTalk/avian.png?branch=master)](https://travis-ci.org/ReadyTalk/avian) + +Quick Start +----------- + +These are examples of building Avian on various operating systems for +the x86_64 architecture. You may need to modify JAVA_HOME according +to where the JDK is installed on your system. In all cases, be sure +to use forward slashes in the path. + +#### on Linux: + $ export JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64 + $ make + $ build/linux-x86_64/avian -cp build/linux-x86_64/test Hello + +#### on Mac OS X: + $ export JAVA_HOME=$(/usr/libexec/java_home) + $ make + $ build/macosx-x86_64/avian -cp build/macosx-x86_64/test Hello + +#### on Windows (Cygwin): + $ git clone git@github.com:ReadyTalk/win64.git ../win64 + $ export JAVA_HOME="/cygdrive/c/Program Files/Java/jdk1.7.0_45" + $ make + $ build/windows-x86_64/avian -cp build/windows-x86_64/test Hello + +#### on FreeBSD: + $ export JAVA_HOME=/usr/local/openjdk7 + $ gmake + $ build/freebsd-x86_64/avian -cp build/freebsd-x86_64/test Hello + + +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://readytalk.github.io/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, x86_64, ARM, and ARM64) + * Windows (i386 and x86_64) + * Mac OS X (i386 and x86_64) + * Apple iOS (i386, x86_64, ARM, and ARM64) + * FreeBSD (i386, x86_64) + + +Building +-------- + +Build requirements include: + + * GNU make 3.80 or later + * GCC 4.6 or later + or LLVM Clang 3.1 or later (see use-clang option below) + * JDK 1.6 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,macosx,ios,freebsd} \ + arch={i386,x86_64,arm,arm64} \ + process={compile,interpret} \ + mode={debug,debug-fast,fast,small} \ + lzma= \ + bootimage={true,false} \ + tails={true,false} \ + continuations={true,false} \ + use-clang={true,false} \ + openjdk= \ + openjdk-src= \ + android= + + * `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) + + * `process` - choice between pure interpreter or JIT compiler + * _default:_ compile + + * `mode` - which set of compilation flags to use to determine +optimization level, debug symbols, and whether to enable +assertions + * _default:_ fast + + * `lzma` - if set, support use of LZMA to compress embedded JARs and +boot images. The value of this option should be a directory +containing a recent LZMA SDK (available [here](http://www.7-zip.org/sdk.html)). Currently, only version 9.20 of +the SDK has been tested, but other versions might work. + * _default:_ not set + + * `armv6` - if true, don't use any instructions newer than armv6. By +default, we assume the target is armv7 or later, and thus requires explicit +memory barrier instructions to ensure cache coherency + + * `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 + + * `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 + + * `use-clang` - if true, use LLVM's clang instead of GCC to build. +Note that this does not currently affect cross compiles, only +native builds. + * _default:_ false + + * `openjdk` - if set, use the OpenJDK class library instead of the +default Avian class library. See "Building with the OpenJDK Class +Library" below for details. + * _default:_ not set + + * `openjdk-src` - if this and the openjdk option above are both set, +build an embeddable VM using the OpenJDK class library. The JNI +components of the OpenJDK class library will be built from the +sources found under the specified directory. See "Building with +the OpenJDK Class Library" below for details. + * _default:_ not set + + * `android` - if set, use the Android class library instead of the +default Avian class library. See "Building with the Android Class +Library" below for details. + * _default:_ not set + +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/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. + +Note that not all combinations of these flags are valid. For instance, +non-jailbroken iOS devices do not allow JIT compilation, so only +process=interpret or bootimage=true builds will run on such +devices. See [here](https://github.com/ReadyTalk/hello-ios) for an +example of an Xcode project for iOS which uses Avian. + +If you are compiling for Windows, you may either cross-compile using +MinGW or build natively on Windows under Cygwin. + +#### Installing Cygwin: + + __1.__ Download and run setup.exe from [cygwin's website](http://www.cygwin.com), installing the base + system and these packages: make, gcc-mingw-g++, + mingw64-i686-gcc-g++, mingw64-x86_64-gcc-g++, and (optionally) git. + +You may also find our win32 repository useful: (run this from the +directory containing the avian directory) + + $ git clone git@github.com:ReadyTalk/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@github.com:ReadyTalk/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. + +*Note that the MSVC build isn't tested regularly, so is fairly likely to be broken.* + +Avian targets MSVC 11 and above (it uses c++ features not available in older versions). + +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 11.0/Common7/IDE:/cygdrive/c/Program Files/Microsoft Visual Studio 11.0/VC/BIN:/cygdrive/c/Program Files/Microsoft Visual Studio 11.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 11.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 11.0\VC\LIB;" + $ export VCINSTALLDIR="C:\Program Files\Microsoft Visual Studio 11.0\VC" + $ export LIB="C:\Program Files\Microsoft Visual Studio 11.0\VC\LIB;C:\Program Files\Microsoft SDKs\Windows\v6.0A\lib;" + $ export INCLUDE="C:\Program Files\Microsoft Visual Studio 11.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 11.0/VC" + + +Building with the OpenJDK Class Library +--------------------------------------- + +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 +OpenJDK's class library instead. To do so, specify the directory +where OpenJDK is installed, e.g.: + + $ make openjdk=/usr/lib/jvm/java-7-openjdk + +This will build Avian as a conventional JVM (e.g. libjvm.so) which +loads its boot class library and native libraries (e.g. libjava.so) +from _/usr/lib/jvm/java-7-openjdk/jre_ at runtime. Note that you must +use an absolute path here, or else the result will not work when run +from other directories. In this configuration, OpenJDK needs to +remain installed for Avian to work, and you can run applications like +this: + + $ build/linux-x86_64-openjdk/avian-dynamic -cp /path/to/my/application \ + com.example.MyApplication + +Alternatively, you can enable a stand-alone build using OpenJDK by +specifying the location of the OpenJDK source code, e.g.: + + $ make openjdk=$(pwd)/../jdk7/build/linux-amd64/j2sdk-image \ + openjdk-src=$(pwd)/../jdk7/jdk/src + +You must ensure that the path specified for openjdk-src does not have +any spaces in it; make gets confused when dependency paths include +spaces, and we haven't found away around that except to avoid paths +with spaces entirely. + +The result of such a build is a self-contained binary which does not +depend on external libraries, jars, or other files. In this case, the +specified paths are used only at build time; anything needed at +runtime is embedded in the binary. Thus, the process of running an +application is simplified: + + $ build/linux-x86_64-openjdk-src/avian -cp /path/to/my/application \ + com.example.MyApplication + +Note that the resulting binary will be very large due to the size of +OpenJDK's class library. This can be mitigated using UPX, preferably +an LZMA-enabled version: + + $ upx --lzma --best build/linux-x86_64-openjdk-src/avian + +You can reduce the size futher for embedded builds by using ProGuard +and the supplied openjdk.pro configuration file (see "Embedding with +ProGuard and a Boot Image" below). Note that you'll still need to use +vm.pro in that case -- openjdk.pro just adds additional constraints +specific to the OpenJDK port. Also see +[app.mk](https://github.com/ReadyTalk/avian-swt-examples/blob/master/app.mk) +in the _avian-swt-examples_ project for an example of using Avian, +OpenJDK, ProGuard, and UPX in concert. + +Here are some examples of how to install OpenJDK and build Avian with +it on various OSes: + +#### Debian-based Linux: +_Conventional build:_ + + $ apt-get install openjdk-7-jdk + $ make openjdk=/usr/lib/jvm/java-7-openjdk test + +_Stand-alone build:_ + + $ apt-get install openjdk-7-jdk + $ apt-get source openjdk-7-jdk + $ apt-get build-dep openjdk-7-jdk + $ (cd openjdk-7-7~b147-2.0 && dpkg-buildpackage) + $ make openjdk=/usr/lib/jvm/java-7-openjdk \ + openjdk-src=$(pwd)/openjdk-7-7~b147-2.0/build/openjdk/jdk/src \ + test + +####Mac OS X: +_Prerequisite:_ Build OpenJDK 7 according to [this site](https://wikis.oracle.com/display/OpenJDK/Mac+OS+X+Port). + +_Conventional build:_ + + $ make openjdk=$(pwd)/../jdk7u-dev/build/macosx-amd64/j2sdk-image test + +_Stand-alone build:_ + + $ make openjdk=$(pwd)/../jdk7u-dev/build/macosx-amd64/j2sdk-image \ + openjdk-src=$(pwd)/../p/jdk7u-dev/jdk/src test + +####Windows (Cygwin): +_Prerequisite:_ Build OpenJDK 7 according to [this site](http://weblogs.java.net/blog/simonis/archive/2011/10/28/yaojowbi-yet-another-openjdk-windows-build-instruction). Alternatively, use https://github.com/alexkasko/openjdk-unofficial-builds. + +_Conventional build:_ + + $ make openjdk=$(pwd)/../jdk7u-dev/build/windows-i586/j2sdk-image test + +_Stand-alone build:_ + + $ make openjdk=$(pwd)/../jdk7u-dev/build/windows-i586/j2sdk-image \ + openjdk-src=$(pwd)/../p/jdk7u-dev/jdk/src test + +Currently, only OpenJDK 7 is supported. Later versions might work, +but have not yet been tested. + + +Building with the Android Class Library +--------------------------------------- +As an alternative to both the Avian and OpenJDK class libaries, you +can also build with the Android class library. Now it should work on Linux, OS X and Windows. + +The simpliest way to build Avian with Android classpath is to use `avian-pack` project: https://github.com/bigfatbrowncat/avian-pack + +Avian-pack consists of Avian itself with some Android components (such as libcore and icu4c). + +Note that we use the upstream OpenSSL repository and apply the +Android patches to it. This is because it is not clear how to build +the Android fork of OpenSSL directly without checking out and building +the entire platform. As of this writing, the patches apply cleanly +against OpenSSL 1.0.1h, so that's the tag we check out, but this may +change in the future when the Android fork rebases against a new +OpenSSL version. + +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, prepend "x86_64-w64-mingw32-" or +"i686-w64-mingw32-" to the ar, g++, gcc, strip, and dlltool commands +below (e.g. x86_64-w64-mingw32-gcc). + +__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/${platform}-${arch}/classpath.jar boot.jar + +__2.__ Build the Java code and add it to the jar. + + $ cat >Hello.java <embedded-jar-main.cpp <("-Xbootclasspath:[bootJar]"); + + JavaVM* vm; + void* env; + JNI_CreateJavaVM(&vm, &env, &vmArgs); + JNIEnv* e = static_cast(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 embedded-jar-main.cpp -o main.o + +__on Mac OS X:__ + + $ g++ -I$JAVA_HOME/include -I$JAVA_HOME/include/darwin \ + -D_JNI_IMPLEMENTATION_ -c embedded-jar-main.cpp -o main.o + +__on Windows:__ + + $ g++ -fno-exceptions -fno-rtti -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/win32" \ + -D_JNI_IMPLEMENTATION_ -c embedded-jar-main.cpp -o main.o + +__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 + $ gcc hello.exp *.o -L../../win32/lib -lmingwthrd -lm -lz -lws2_32 \ + -lIphlpapi -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. Finally, floating point code may be slower + on 32-bit x86 since the compiler cannot assume SSE2 support will be + available at runtime, and the x87 FPU is not supported except via + out-of-line helper functions. + +Note you can use ProGuard without using a boot image and vice-versa, +as desired. + +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 +VM object files. + + $ make bootimage=true + $ mkdir hello + $ cd hello + $ 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-x86_64-bootimage/classpath.jar) + +__3.__ Build the Java code and add it to stage1. + + $ cat >Hello.java <hello.pro <bootimage-main.cpp <("-Davian.bootimage=bootimageBin"); + + options[1].optionString + = const_cast("-Davian.codeimage=codeimageBin"); + + JavaVM* vm; + void* env; + JNI_CreateJavaVM(&vm, &env, &vmArgs); + JNIEnv* e = static_cast(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 bootimage-main.cpp -o main.o + +__8.__ 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 + + +Trademarks +---------- + +Oracle and Java are registered trademarks of Oracle and/or its +affiliates. Other names may be trademarks of their respective owners. + +The Avian project is not affiliated with Oracle. diff --git a/sgx-jvm/avian/android.pro b/sgx-jvm/avian/android.pro new file mode 100644 index 0000000000..00941ff874 --- /dev/null +++ b/sgx-jvm/avian/android.pro @@ -0,0 +1,89 @@ +# these are referenced in JniConstants.cpp: + +-keep class java.text.Bidi$Run +-keep class java.math.BigDecimal +-keep class java.lang.Boolean +-keep class java.lang.Byte +-keep class java.nio.charset.CharsetICU { + CharsetICU(java.lang.String, java.lang.String, java.lang.String[]); + } +-keep class java.lang.reflect.Constructor +-keep class java.util.zip.Deflater +-keep class java.lang.Double +-keep class libcore.io.ErrnoException +-keep class java.lang.reflect.Field +-keep class libcore.icu.NativeDecimalFormat$FieldPositionIterator { + void setData(int[]); + } +-keep class java.io.FileDescriptor +-keep class libcore.io.GaiException +-keep class java.net.Inet6Address +-keep class java.net.InetAddress +-keep class java.net.InetSocketAddress +-keep class java.net.InetUnixAddress +-keep class java.util.zip.Inflater +-keep class java.lang.Integer +-keep class libcore.icu.LocaleData +-keep class java.lang.Long +-keep class java.lang.reflect.Method +-keep class libcore.util.MutableInt +-keep class libcore.util.MutableLong +-keep class java.text.ParsePosition +-keep class java.util.regex.PatternSyntaxException +-keep class java.lang.RealToString +-keep class java.net.Socket +-keep class java.net.SocketImpl +-keep class java.lang.String +-keep class libcore.io.StructAddrinfo { + ; + } +-keep class libcore.io.StructFlock +-keep class libcore.io.StructGroupReq +-keep class libcore.io.StructLinger +-keep class libcore.io.StructPasswd { + StructPasswd(java.lang.String, int, int, java.lang.String, java.lang.String); + } +-keep class libcore.io.StructPollfd +-keep class libcore.io.StructStat { + StructStat(long, long, int, long, int, int, long, long, long, long, long, long, long); + } +-keep class libcore.io.StructStatFs +-keep class libcore.io.StructTimeval +-keep class libcore.io.StructUtsname { + StructUtsname(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String); + } +-keep class libcore.io.StructUcred + +# referenced from libcore native code + +-keep class libcore.icu.LocaleData { + ; + } + +-keep class org.conscrypt.OpenSSLBIOInputStream { + ; + } + +-keep class java.util.Calendar { + void set(int, int, int, int, int, int); + } + +# called from the VM + +-keep class java.lang.Thread { + Thread(java.lang.ThreadGroup, java.lang.String, int, boolean); + } + +-keep class avian.Classes { + java.lang.Class forName(java.lang.String, boolean, java.lang.ClassLoader); + int findField(avian.VMClass, java.lang.String); + int findMethod(avian.VMClass, java.lang.String, java.lang.Class[]); + java.lang.annotation.Annotation getAnnotation(java.lang.ClassLoader, java.lang.Object[]); + } + +-keep class java.lang.VMThread { + VMThread(java.lang.Thread); + } + +# loaded reflectively to handle embedded resources: +-keep class avian.avianvmresource.Handler diff --git a/sgx-jvm/avian/build.gradle b/sgx-jvm/avian/build.gradle new file mode 100644 index 0000000000..7c49e302bc --- /dev/null +++ b/sgx-jvm/avian/build.gradle @@ -0,0 +1,323 @@ +buildscript { + repositories { + jcenter() + } + + dependencies { + classpath 'org.jfrog.buildinfo:build-info-extractor-gradle:2.2.4' + } +} + +apply plugin: 'native-component' +apply plugin: 'ivy-publish' +apply plugin: 'java' +apply plugin: 'artifactory-publish' + +enum SupportedOS implements OperatingSystem { + LINUX, WINDOWS, MACOSX; + + public static final SupportedOS CURRENT; + + static { + String p = System.properties['os.name'] + switch(p.replaceAll(' ', '').toLowerCase()) { + case ~/.*linux.*/: CURRENT = LINUX; break; + case ~/.*darwin.*/: CURRENT = MACOSX; break; + case ~/.*osx.*/: CURRENT = MACOSX; break; + case ~/.*win.*/: CURRENT = WINDOWS; break; + default: + String m = "SupportedOS: unrecognized platform: ${p}" + println(m) + throw new IllegalArgumentException(m) + } + } + + public String getName() { + return toString().toLowerCase() + } + + public String getDisplayName() { + return getName() + } + public boolean isCurrent() { return this == CURRENT } + public boolean isFreeBSD() { return false } + public boolean isLinux() { return this == LINUX } + public boolean isMacOsX() { return this == MACOSX } + public boolean isSolaris() { return false } + public boolean isWindows() { return this == WINDOWS } + +} + +public String adjustArch(String arch) { + switch(arch) { + case ~/.*64.*/: return 'x86_64' + default: return 'i386' + } +} + +ext { + currentPlatform = SupportedOS.CURRENT.getName() + currentArch = adjustArch(System.properties['os.arch']) + currentPlatformArch = "${currentPlatform}-${currentArch}" + + platform = project.hasProperty('platform') ? platform : currentPlatform + arch = project.hasProperty('arch') ? arch : currentArch + platformArch = "${platform}-${arch}" + + java_home = System.properties.'java.home' + if(java_home.endsWith("/jre")) { + java_home = java_home.substring(0, java_home.length() - "/jre".length()) + } + java_home = java_home + + libDir = "${buildDir}/lib" + lzmaDir = "${libDir}/lzma" + + buildOptions = ['', '-lzma'] +} + +repositories { + ivy { + name "ivyLocal" + url "${System.env.HOME}/.ivy2/local" + layout 'maven' + } + + ivy { + name "jcenter" + if(version.contains("SNAPSHOT")) { + url "http://oss.jfrog.org/artifactory/oss-snapshot-local" + } else { + url "http://oss.jfrog.org/artifactory/oss-release-local" + } + layout 'maven' + } +} + +configurations { + create('windows-i386') + create('windows-x86_64') +} + +dependencies { + 'windows-i386' "com.readytalk:win32:1.0.0-SNAPSHOT" + 'windows-x86_64' "com.readytalk:win64:1.0.0-SNAPSHOT" +} + +model { + platforms { + create(platformArch) { + operatingSystem SupportedOS.valueOf(platform.toUpperCase()) + architecture "${arch}" + } + } + + tasks { + buildOptions.each { buildOption -> + platforms.each { platform -> + if(platform.operatingSystem.name == "windows") { + def artifactName = platform.architecture.name == "i386" ? 'win32' : 'win64' + + task "extract${platform.name}${buildOption}"(type: Copy) { + from { + tarTree(configurations."${platform.name}".find { it.name =~ artifactName }) + } + into "${libDir}/tools" + } + } + + task "build${platform.name}${buildOption}"(type: Exec) { + executable "make" + args "platform=${platform.operatingSystem.name}", + "arch=${platform.architecture.name}" + + if(buildOption == "-lzma") { + dependsOn 'extractLzma' + args "lzma=${lzmaDir}" + } + + if(platform.operatingSystem.name == "windows") { + dependsOn "extract${platform.name}${buildOption}" + args "win32=${libDir}/tools/win32", + "win64=${libDir}/tools/win64" + } + environment JAVA_HOME: java_home + } + + assemble { + dependsOn "build${platform.name}${buildOption}" + } + } + } + } +} + +tasks.withType(JavaCompile) { + sourceCompatibility = "1.6" + targetCompatibility = "1.6" + + options.with { + encoding = "UTF-8" + bootClasspath = sourceSets.main.output.classesDir + } +} + +sourceSets { + main { + java { + srcDir 'classpath' + } + } +} + +javadoc { + title = "Avian v${version} Class Library API" +} + +task javadocJar(type: Jar) { + dependsOn javadoc + classifier = 'javadoc' + from { + javadoc.destinationDir + } +} + +jar { + baseName "classpath-avian" +} + +task downloadLzma(type: Exec) { + commandLine "curl" + args "--create-dirs", "-o", "${lzmaDir}/lzma920.tar.bz2", "-f", "http://oss.readytalk.com/avian-web/lzma920.tar.bz2" +} + +task extractLzma(type: Copy) { + dependsOn downloadLzma + from { + tarTree(resources.bzip2("${lzmaDir}/lzma920.tar.bz2")) + } + into lzmaDir +} + +task install { + dependsOn assemble, publish +} + +publishing { + repositories { + add(project.repositories."ivyLocal") + } + + publications { + + ivy(IvyPublication) { + from components.java + + artifact(javadocJar) + + artifact("vm.pro") { + name "vm" + type "proguard" + extension "pro" + } + + module "classpath-avian" + } + + create("tools-avian-${currentPlatformArch}", IvyPublication) { + module "tools-avian-${currentPlatformArch}" + + def publishBinSuffix = currentPlatform == "windows" ? "exe" : "bin" + def binSuffix = currentPlatform == "windows" ? ".exe" : "" + artifact("${buildDir}/${currentPlatform}-${currentArch}/binaryToObject/binaryToObject${binSuffix}") { + name "binaryToObject" + type publishBinSuffix + extension publishBinSuffix + } + } + + buildOptions.each { buildOption -> + platforms.each { platform -> + def binSuffix="" + def publishBinSuffix="bin" + + create("${platform.name}${buildOption}", IvyPublication) { + def nativeBuildDir = "${buildDir}/${platform.operatingSystem.name}-${platform.architecture.name}${buildOption}" + + if(platform.operatingSystem.name == "windows") { + publishBinSuffix = "exe" + binSuffix = ".${publishBinSuffix}" + } + + module "runtime-avian${buildOption}-${platform.name}" + + artifact("${nativeBuildDir}/avian${binSuffix}") { + name "avian" + type publishBinSuffix + extension publishBinSuffix + } + + artifact("${nativeBuildDir}/libavian.a") { + name "libavian" + type "a" + extension "a" + } + + if (buildOption == "-lzma") { + artifact("${nativeBuildDir}/libavian-lzma.a") { + name "libavian-lzma" + type "a" + extension "a" + } + + artifact("${nativeBuildDir}/lzma/lzma${binSuffix}") { + name "lzma" + type publishBinSuffix + extension publishBinSuffix + } + } + } + } + } + } +} + +artifactoryPublish { + onlyIf { + // TRAVIS_BRANCH reports master if it is a master build or a PR going to master + // TRAVIS_PULL_REQUEST reports false if not a pull request and the PR number if it is + System.env.'TRAVIS_BRANCH' == "master" && System.env.'TRAVIS_PULL_REQUEST' == "false" + } + dependsOn assemble +} + +artifactory { + contextUrl = "http://oss.jfrog.org" + + resolve { + repository { + repoKey = 'libs-releases' + } + } + + publish { + repository { + repoKey = 'oss-snapshot-local' + username = System.env.BINTRAY_USER + password = System.env.BINTRAY_API_KEY + ivy { + ivyLayout = "[organisation]/[module]/[revision]/ivy-[revision].xml" + } + } + + defaults { + platforms.each { + publications it.name + } + } + } +} + +task wrapper(type: Wrapper) { + distributionUrl = 'http://services.gradle.org/distributions/gradle-2.0-bin.zip' +} diff --git a/sgx-jvm/avian/classpath/avian/Addendum.java b/sgx-jvm/avian/classpath/avian/Addendum.java new file mode 100644 index 0000000000..79c9a75577 --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/Addendum.java @@ -0,0 +1,17 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +public class Addendum { + public Singleton pool; + public Object annotationTable; + public Object signature; +} diff --git a/sgx-jvm/avian/classpath/avian/AnnotationInvocationHandler.java b/sgx-jvm/avian/classpath/avian/AnnotationInvocationHandler.java new file mode 100644 index 0000000000..a15e716f2e --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/AnnotationInvocationHandler.java @@ -0,0 +1,32 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +import java.lang.reflect.Method; +import java.lang.reflect.InvocationHandler; + +public class AnnotationInvocationHandler implements InvocationHandler { + private Object[] data; + + public AnnotationInvocationHandler(Object[] data) { + this.data = data; + } + + public Object invoke(Object proxy, Method method, Object[] arguments) { + String name = method.getName(); + for (int i = 2; i < data.length; i += 2) { + if (name.equals(data[i])) { + return data[i + 1]; + } + } + return method.getDefaultValue(); + } +} diff --git a/sgx-jvm/avian/classpath/avian/Assembler.java b/sgx-jvm/avian/classpath/avian/Assembler.java new file mode 100644 index 0000000000..64a06fdf81 --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/Assembler.java @@ -0,0 +1,135 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +import static avian.Stream.write1; +import static avian.Stream.write2; +import static avian.Stream.write4; + +import avian.ConstantPool.PoolEntry; + +import java.util.List; +import java.io.OutputStream; +import java.io.IOException; + +public class Assembler { + public static final int ACC_PUBLIC = 1 << 0; + public static final int ACC_STATIC = 1 << 3; + + public static final int aaload = 0x32; + public static final int aastore = 0x53; + public static final int aload = 0x19; + public static final int aload_0 = 0x2a; + public static final int aload_1 = 0x2b; + public static final int astore_0 = 0x4b; + public static final int anewarray = 0xbd; + public static final int areturn = 0xb0; + public static final int dload = 0x18; + public static final int dreturn = 0xaf; + public static final int dup = 0x59; + public static final int fload = 0x17; + public static final int freturn = 0xae; + public static final int getfield = 0xb4; + public static final int goto_ = 0xa7; + public static final int iload = 0x15; + public static final int invokeinterface = 0xb9; + public static final int invokespecial = 0xb7; + public static final int invokestatic = 0xb8; + public static final int invokevirtual = 0xb6; + public static final int ireturn = 0xac; + public static final int jsr = 0xa8; + public static final int ldc_w = 0x13; + public static final int lload = 0x16; + public static final int lreturn = 0xad; + public static final int new_ = 0xbb; + public static final int pop = 0x57; + public static final int putfield = 0xb5; + public static final int ret = 0xa9; + public static final int return_ = 0xb1; + + public static void writeClass(OutputStream out, + List pool, + int name, + int super_, + int[] interfaces, + FieldData[] fields, + MethodData[] methods) + throws IOException + { + int codeAttributeName = ConstantPool.addUtf8(pool, "Code"); + + write4(out, 0xCAFEBABE); + write2(out, 0); // minor version + write2(out, 50); // major version + + write2(out, pool.size() + 1); + for (PoolEntry e: pool) { + e.writeTo(out); + } + + write2(out, ACC_PUBLIC); // flags + write2(out, name + 1); + write2(out, super_ + 1); + + write2(out, interfaces.length); + for (int i: interfaces) { + write2(out, i + 1); + } + + write2(out, fields.length); + for (FieldData f: fields) { + write2(out, f.flags); + write2(out, f.nameIndex + 1); + write2(out, f.specIndex + 1); + write2(out, 0); // attribute count + } + + write2(out, methods.length); + for (MethodData m: methods) { + write2(out, m.flags); + write2(out, m.nameIndex + 1); + write2(out, m.specIndex + 1); + + write2(out, 1); // attribute count + write2(out, codeAttributeName + 1); + write4(out, m.code.length); + out.write(m.code); + } + + write2(out, 0); // attribute count + } + + public static class MethodData { + public final int flags; + public final int nameIndex; + public final int specIndex; + public final byte[] code; + + public MethodData(int flags, int nameIndex, int specIndex, byte[] code) { + this.flags = flags; + this.nameIndex = nameIndex; + this.specIndex = specIndex; + this.code = code; + } + } + + public static class FieldData { + public final int flags; + public final int nameIndex; + public final int specIndex; + + public FieldData(int flags, int nameIndex, int specIndex) { + this.flags = flags; + this.nameIndex = nameIndex; + this.specIndex = specIndex; + } + } +} diff --git a/sgx-jvm/avian/classpath/avian/Atomic.java b/sgx-jvm/avian/classpath/avian/Atomic.java new file mode 100644 index 0000000000..53b52da665 --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/Atomic.java @@ -0,0 +1,20 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +import java.lang.reflect.Field; + +public class Atomic { + public static native long getOffset(Field field); + + public static native boolean compareAndSwapObject + (Object o, long offset, Object old, Object new_); +} diff --git a/sgx-jvm/avian/classpath/avian/Callback.java b/sgx-jvm/avian/classpath/avian/Callback.java new file mode 100644 index 0000000000..57c0976452 --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/Callback.java @@ -0,0 +1,16 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +public interface Callback { + public void handleResult(T result); + public void handleException(Throwable exception); +} diff --git a/sgx-jvm/avian/classpath/avian/Cell.java b/sgx-jvm/avian/classpath/avian/Cell.java new file mode 100644 index 0000000000..1425955fec --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/Cell.java @@ -0,0 +1,54 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +public class Cell { + public T value; + public Cell next; + + public Cell(T value, Cell next) { + this.value = value; + this.next = next; + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("("); + for (Cell c = this; c != null; c = c.next) { + sb.append(value); + if (c.next != null) { + sb.append(" "); + } + } + sb.append(")"); + return sb.toString(); + } + + public static Cell cons(Car car, Cell cdr) { + return new Cell(car, cdr); + } + + public static boolean equal(T a, T b) { + return (a == null && b == null) || (a != null && a.equals(b)); + } + + public static boolean equal(Cell a, Cell b) { + while (a != null) { + if (b == null || (! equal(a.value, b.value))) { + return false; + } + a = a.next; + b = b.next; + } + + return b == null; + } +} diff --git a/sgx-jvm/avian/classpath/avian/ClassAddendum.java b/sgx-jvm/avian/classpath/avian/ClassAddendum.java new file mode 100644 index 0000000000..ef558b59c4 --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/ClassAddendum.java @@ -0,0 +1,32 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +public class ClassAddendum extends Addendum { + public Object[] interfaceTable; + public InnerClassReference[] innerClassTable; + /** + * If this value is negative, all the methods in VMClass.methodTable + * were declared in that class. Otherwise, only the first + * declaredMethodCount methods in that table were declared in that + * class, while the rest were declared in interfaces implemented or + * extended by that class. + */ + public int declaredMethodCount; + + public byte[] enclosingClass; + + public Pair enclosingMethod; + + public VMMethod[] bootstrapMethodTable; + + public VMMethod[] bootstrapLambdaTable; +} diff --git a/sgx-jvm/avian/classpath/avian/Classes.java b/sgx-jvm/avian/classpath/avian/Classes.java new file mode 100644 index 0000000000..b55408834c --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/Classes.java @@ -0,0 +1,604 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +import static avian.Stream.read1; +import static avian.Stream.read2; + +import java.net.URL; +import java.net.MalformedURLException; +import java.security.CodeSource; +import java.security.AllPermission; +import java.security.Permissions; +import java.security.ProtectionDomain; +import java.security.cert.Certificate; +import java.lang.reflect.Modifier; +import java.lang.reflect.Method; +import java.lang.reflect.Field; +import java.lang.reflect.Proxy; +import java.lang.annotation.Annotation; +import java.io.InputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; + +public class Classes { + private static final int LinkFlag = 1 << 8; + + public static native VMClass defineVMClass + (ClassLoader loader, byte[] b, int offset, int length); + + public static native VMClass primitiveClass(char name); + + public static native void initialize(VMClass vmClass); + + public static native boolean isAssignableFrom(VMClass a, VMClass b); + + public static native VMClass getVMClass(Object o); + + public static native VMClass toVMClass(Class c); + + public static native VMMethod toVMMethod(Method m); + + private static native VMClass resolveVMClass(ClassLoader loader, byte[] spec) + throws ClassNotFoundException; + + public static VMClass loadVMClass(ClassLoader loader, + byte[] nameBytes, int offset, int length) + { + byte[] spec = new byte[length + 1]; + System.arraycopy(nameBytes, offset, spec, 0, length); + + try { + VMClass c = resolveVMClass(loader, spec); + if (c == null) { + throw new NoClassDefFoundError(); + } + return c; + } catch (ClassNotFoundException e) { + NoClassDefFoundError error = new NoClassDefFoundError + (new String(nameBytes, offset, length)); + error.initCause(e); + throw error; + } + } + + private static Object parseAnnotationValue(ClassLoader loader, + Object pool, + InputStream in) + throws IOException + { + switch (read1(in)) { + case 'Z': + return Boolean.valueOf(Singleton.getInt(pool, read2(in) - 1) != 0); + + case 'B': + return Byte.valueOf((byte) Singleton.getInt(pool, read2(in) - 1)); + + case 'C': + return Character.valueOf((char) Singleton.getInt(pool, read2(in) - 1)); + + case 'S': + return Short.valueOf((short) Singleton.getInt(pool, read2(in) - 1)); + + case 'I': + return Integer.valueOf(Singleton.getInt(pool, read2(in) - 1)); + + case 'F': + return Float.valueOf + (Float.intBitsToFloat(Singleton.getInt(pool, read2(in) - 1))); + + case 'J': { + return Long.valueOf(Singleton.getLong(pool, read2(in) - 1)); + } + + case 'D': { + return Double.valueOf + (Double.longBitsToDouble(Singleton.getLong(pool, read2(in) - 1))); + } + + case 's': { + byte[] data = (byte[]) Singleton.getObject(pool, read2(in) - 1); + + return new String(data, 0, data.length - 1); + } + + case 'e': { + byte[] typeName = (byte[]) Singleton.getObject(pool, read2(in) - 1); + byte[] name = (byte[]) Singleton.getObject(pool, read2(in) - 1); + + return Enum.valueOf + (SystemClassLoader.getClass + (loadVMClass(loader, typeName, 1, typeName.length - 3)), + new String(name, 0, name.length - 1)); + } + + case 'c':{ + byte[] name = (byte[]) Singleton.getObject(pool, read2(in) - 1); + + return SystemClassLoader.getClass + (loadVMClass(loader, name, 1, name.length - 3)); + } + + case '@': + return getAnnotation(loader, parseAnnotation(loader, pool, in)); + + case '[': { + Object[] array = new Object[read2(in)]; + for (int i = 0; i < array.length; ++i) { + array[i] = parseAnnotationValue(loader, pool, in); + } + return array; + } + + default: throw new AssertionError(); + } + } + + private static Object[] parseAnnotation(ClassLoader loader, + Object pool, + InputStream in) + throws IOException + { + byte[] typeName = (byte[]) Singleton.getObject(pool, read2(in) - 1); + Object[] annotation = new Object[(read2(in) + 1) * 2]; + annotation[1] = SystemClassLoader.getClass + (loadVMClass(loader, typeName, 1, typeName.length - 3)); + + for (int i = 2; i < annotation.length; i += 2) { + byte[] name = (byte[]) Singleton.getObject(pool, read2(in) - 1); + annotation[i] = new String(name, 0, name.length - 1); + annotation[i + 1] = parseAnnotationValue(loader, pool, in); + } + + return annotation; + } + + private static Object[] parseAnnotationTable(ClassLoader loader, + Object pool, + InputStream in) + throws IOException + { + Object[] table = new Object[read2(in)]; + for (int i = 0; i < table.length; ++i) { + table[i] = parseAnnotation(loader, pool, in); + } + return table; + } + + private static void parseAnnotationTable(ClassLoader loader, + Addendum addendum) + { + if (addendum != null && addendum.annotationTable instanceof byte[]) { + try { + addendum.annotationTable = parseAnnotationTable + (loader, addendum.pool, new ByteArrayInputStream + ((byte[]) addendum.annotationTable)); + } catch (IOException e) { + AssertionError error = new AssertionError(); + error.initCause(e); + throw error; + } + } + } + + private static int resolveSpec(ClassLoader loader, byte[] spec, int start) { + int result; + int end; + switch (spec[start]) { + case 'L': + ++ start; + end = start; + while (spec[end] != ';') ++ end; + result = end + 1; + break; + + case '[': + end = start + 1; + while (spec[end] == '[') ++ end; + switch (spec[end]) { + case 'L': + ++ end; + while (spec[end] != ';') ++ end; + ++ end; + break; + + default: + ++ end; + } + result = end; + break; + + default: + return start + 1; + } + + loadVMClass(loader, spec, start, end - start); + + return result; + } + + private static int declaredMethodCount(VMClass c) { + ClassAddendum a = c.addendum; + if (a != null) { + int count = a.declaredMethodCount; + if (count >= 0) { + return count; + } + } + VMMethod[] table = c.methodTable; + return table == null ? 0 : table.length; + } + + public static void link(VMClass c, ClassLoader loader) { + acquireClassLock(); + try { + if ((c.vmFlags & LinkFlag) == 0) { + if (c.super_ != null) { + link(c.super_, loader); + } + + parseAnnotationTable(loader, c.addendum); + + if (c.interfaceTable != null) { + int stride = ((c.flags & Modifier.INTERFACE) != 0 ? 1 : 2); + for (int i = 0; i < c.interfaceTable.length; i += stride) { + link((VMClass) c.interfaceTable[i], loader); + } + } + + VMMethod[] methodTable = c.methodTable; + if (methodTable != null) { + for (int i = 0; i < methodTable.length; ++i) { + VMMethod m = methodTable[i]; + + for (int j = 1; j < m.spec.length;) { + j = resolveSpec(loader, m.spec, j); + } + + parseAnnotationTable(loader, m.addendum); + } + } + + if (c.fieldTable != null) { + for (int i = 0; i < c.fieldTable.length; ++i) { + VMField f = c.fieldTable[i]; + + resolveSpec(loader, f.spec, 0); + + parseAnnotationTable(loader, f.addendum); + } + } + + c.vmFlags |= LinkFlag; + } + } finally { + releaseClassLock(); + } + } + + public static void link(VMClass c) { + link(c, c.loader); + } + + public static Class forName(String name, boolean initialize, + ClassLoader loader) + throws ClassNotFoundException + { + if (loader == null) { + loader = Class.class.getClassLoader(); + } + Class c = loader.loadClass(name); + VMClass vmc = SystemClassLoader.vmClass(c); + link(vmc, loader); + if (initialize) { + initialize(vmc); + } + return c; + } + + public static Class forCanonicalName(String name) { + return forCanonicalName(null, name); + } + + public static Class forCanonicalName(ClassLoader loader, String name) { + try { + if (name.startsWith("[")) { + return forName(name, true, loader); + } else if (name.startsWith("L")) { + return forName(name.substring(1, name.length() - 1), true, loader); + } else { + if (name.length() == 1) { + return SystemClassLoader.getClass + (primitiveClass(name.charAt(0))); + } else { + throw new ClassNotFoundException(name); + } + } + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + private static int next(char c, String s, int start) { + for (int i = start; i < s.length(); ++i) { + if (s.charAt(i) == c) return i; + } + throw new RuntimeException(); + } + + public static Class[] getParameterTypes(VMMethod vmMethod) { + int count = vmMethod.parameterCount; + + Class[] types = new Class[count]; + int index = 0; + + String spec = new String + (vmMethod.spec, 1, vmMethod.spec.length - 2); + + try { + for (int i = 0; i < spec.length(); ++i) { + char c = spec.charAt(i); + if (c == ')') { + break; + } else if (c == 'L') { + int start = i + 1; + i = next(';', spec, start); + String name = spec.substring(start, i).replace('/', '.'); + types[index++] = Class.forName(name, true, vmMethod.class_.loader); + } else if (c == '[') { + int start = i; + while (spec.charAt(i) == '[') ++i; + + if (spec.charAt(i) == 'L') { + i = next(';', spec, i + 1); + String name = spec.substring(start, i).replace('/', '.'); + types[index++] = Class.forName + (name, true, vmMethod.class_.loader); + } else { + String name = spec.substring(start, i + 1); + types[index++] = forCanonicalName(vmMethod.class_.loader, name); + } + } else { + String name = spec.substring(i, i + 1); + types[index++] = forCanonicalName(vmMethod.class_.loader, name); + } + } + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + + return types; + } + + public static int findField(VMClass vmClass, String name) { + if (vmClass.fieldTable != null) { + link(vmClass); + + for (int i = 0; i < vmClass.fieldTable.length; ++i) { + if (toString(vmClass.fieldTable[i].name).equals(name)) { + return i; + } + } + } + return -1; + } + + public static String toString(byte[] array) { + return new String(array, 0, array.length - 1); + } + + private static boolean match(VMClass a, VMClass b) { + // TODO: in theory we should be able to just do an == comparison + // here instead of recursively comparing array element types. + // However, the VM currently can create multiple array classes for + // the same element type. We should fix that so that there's only + // ever one of each per classloader, eliminating the need for a + // recursive comparison. See also the native implementation of + // isAssignableFrom. + if (a.arrayDimensions > 0) { + return match(a.arrayElementClass, b.arrayElementClass); + } else { + return a == b; + } + } + + public static boolean match(Class[] a, Class[] b) { + if (a.length == b.length) { + for (int i = 0; i < a.length; ++i) { + if (! match(toVMClass(a[i]), toVMClass(b[i]))) { + return false; + } + } + return true; + } else { + return false; + } + } + + 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) + { + VMMethod[] methodTable = vmClass.methodTable; + if (methodTable != null) { + link(vmClass); + + if (parameterTypes == null) { + parameterTypes = new Class[0]; + } + + for (int i = 0; i < methodTable.length; ++i) { + VMMethod m = methodTable[i]; + if (toString(m.name).equals(name) + && match(parameterTypes, getParameterTypes(m))) + { + return i; + } + } + } + return -1; + } + + public static int countMethods(VMClass vmClass, boolean publicOnly) { + int count = 0; + VMMethod[] methodTable = vmClass.methodTable; + if (methodTable != null) { + for (int i = 0, j = declaredMethodCount(vmClass); i < j; ++i) { + VMMethod m = methodTable[i]; + if (((! publicOnly) || ((m.flags & Modifier.PUBLIC)) != 0) + && (! toString(m.name).startsWith("<"))) + { + ++ count; + } + } + } + return count; + } + + public static Method[] getMethods(VMClass vmClass, boolean publicOnly) { + Method[] array = new Method[countMethods(vmClass, publicOnly)]; + VMMethod[] methodTable = vmClass.methodTable; + if (methodTable != null) { + link(vmClass); + + int ai = 0; + for (int i = 0, j = declaredMethodCount(vmClass); i < j; ++i) { + VMMethod m = methodTable[i]; + if (((! publicOnly) || ((m.flags & Modifier.PUBLIC) != 0)) + && ! toString(m.name).startsWith("<")) + { + array[ai++] = makeMethod(SystemClassLoader.getClass(vmClass), i); + } + } + } + + return array; + } + + public static int countFields(VMClass vmClass, boolean publicOnly) { + int count = 0; + if (vmClass.fieldTable != null) { + for (int i = 0; i < vmClass.fieldTable.length; ++i) { + if ((! publicOnly) + || ((vmClass.fieldTable[i].flags & Modifier.PUBLIC)) + != 0) + { + ++ count; + } + } + } + return count; + } + + public static Field[] getFields(VMClass vmClass, boolean publicOnly) { + Field[] array = new Field[countFields(vmClass, publicOnly)]; + if (vmClass.fieldTable != null) { + link(vmClass); + + int ai = 0; + for (int i = 0; i < vmClass.fieldTable.length; ++i) { + if (((vmClass.fieldTable[i].flags & Modifier.PUBLIC) != 0) + || (! publicOnly)) + { + array[ai++] = makeField(SystemClassLoader.getClass(vmClass), i); + } + } + } + + return array; + } + + public static Annotation getAnnotation(ClassLoader loader, Object[] a) { + if (a[0] == null) { + a[0] = Proxy.newProxyInstance + (loader, new Class[] { (Class) a[1] }, + new AnnotationInvocationHandler(a)); + } + return (Annotation) a[0]; + } + + public static Object getAnnotationDefaultValue(ClassLoader loader, + MethodAddendum addendum) { + if (addendum == null) { + return null; + } + byte[] annotationDefault = (byte[]) addendum.annotationDefault; + if (annotationDefault == null) { + return null; + } + try { + return parseAnnotationValue(loader, addendum.pool, + new ByteArrayInputStream(annotationDefault)); + } catch (IOException e) { + AssertionError error = new AssertionError(); + error.initCause(e); + throw error; + } + } + + private static int index(VMMethod m) { + VMMethod[] table = m.class_.methodTable; + for (int i = 0; i < table.length; ++i) { + if (m == table[i]) return i; + } + throw new AssertionError(); + } + + public static Method makeMethod(VMMethod m) { + return makeMethod(SystemClassLoader.getClass(m.class_), index(m)); + } + + public static ProtectionDomain getProtectionDomain(VMClass c) { + CodeSource source = null; + if (c.source != null) { + try { + source = new CodeSource + (new URL(new String(c.source, 0, c.source.length - 1)), + (Certificate[]) null); + } catch (MalformedURLException ignored) { } + } + + Permissions p = new Permissions(); + p.add(new AllPermission()); + + return new ProtectionDomain(source, p); + } + + public static native Method makeMethod(Class c, int slot); + + public static native Field makeField(Class c, int slot); + + private static native void acquireClassLock(); + + private static native void releaseClassLock(); + + public static native String makeString(byte[] array, int offset, int length); +} diff --git a/sgx-jvm/avian/classpath/avian/Code.java b/sgx-jvm/avian/classpath/avian/Code.java new file mode 100644 index 0000000000..957099107d --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/Code.java @@ -0,0 +1,5 @@ +package avian; + +abstract class Code { + // VM-visible fields in types.def +} diff --git a/sgx-jvm/avian/classpath/avian/ConstantPool.java b/sgx-jvm/avian/classpath/avian/ConstantPool.java new file mode 100644 index 0000000000..f795c19e7e --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/ConstantPool.java @@ -0,0 +1,242 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +import static avian.Stream.write1; +import static avian.Stream.write2; +import static avian.Stream.write4; + +import java.util.List; +import java.io.OutputStream; +import java.io.IOException; + +public class ConstantPool { + private static final int CONSTANT_Integer = 3; + private static final int CONSTANT_Utf8 = 1; + private static final int CONSTANT_String = 8; + private static final int CONSTANT_Class = 7; + private static final int CONSTANT_NameAndType = 12; + private static final int CONSTANT_Fieldref = 9; + private static final int CONSTANT_Methodref = 10; + + public static int add(List pool, PoolEntry e) { + int i = 0; + for (PoolEntry existing: pool) { + if (existing.equals(e)) { + return i; + } else { + ++i; + } + } + pool.add(e); + return pool.size() - 1; + } + + public static int addInteger(List pool, int value) { + return add(pool, new IntegerPoolEntry(value)); + } + + public static int addUtf8(List pool, String value) { + return add(pool, new Utf8PoolEntry(value)); + } + + public static int addString(List pool, String value) { + return add(pool, new StringPoolEntry(addUtf8(pool, value))); + } + + public static int addClass(List pool, String name) { + return add(pool, new ClassPoolEntry(addUtf8(pool, name))); + } + + public static int addNameAndType(List pool, + String name, + String type) + { + return add(pool, new NameAndTypePoolEntry + (addUtf8(pool, name), + addUtf8(pool, type))); + } + + public static int addFieldRef(List pool, + String className, + String name, + String spec) + { + return add(pool, new FieldRefPoolEntry + (addClass(pool, className), + addNameAndType(pool, name, spec))); + } + + public static int addMethodRef(List pool, + String className, + String name, + String spec) + { + return add(pool, new MethodRefPoolEntry + (addClass(pool, className), + addNameAndType(pool, name, spec))); + } + + public interface PoolEntry { + public void writeTo(OutputStream out) throws IOException; + } + + private static class IntegerPoolEntry implements PoolEntry { + private final int value; + + public IntegerPoolEntry(int value) { + this.value = value; + } + + public void writeTo(OutputStream out) throws IOException { + write1(out, CONSTANT_Integer); + write4(out, value); + } + + public boolean equals(Object o) { + return o instanceof IntegerPoolEntry + && ((IntegerPoolEntry) o).value == value; + } + } + + private static class Utf8PoolEntry implements PoolEntry { + private final String data; + + public Utf8PoolEntry(String data) { + this.data = data; + } + + public void writeTo(OutputStream out) throws IOException { + write1(out, CONSTANT_Utf8); + byte[] bytes = data.getBytes(); + write2(out, bytes.length); + out.write(bytes); + } + + public boolean equals(Object o) { + return o instanceof Utf8PoolEntry + && ((Utf8PoolEntry) o).data.equals(data); + } + } + + private static class StringPoolEntry implements PoolEntry { + private final int valueIndex; + + public StringPoolEntry(int valueIndex) { + this.valueIndex = valueIndex; + } + + public void writeTo(OutputStream out) throws IOException { + write1(out, CONSTANT_String); + write2(out, valueIndex + 1); + } + + public boolean equals(Object o) { + return o instanceof StringPoolEntry + && ((StringPoolEntry) o).valueIndex == valueIndex; + } + } + + private static class ClassPoolEntry implements PoolEntry { + private final int nameIndex; + + public ClassPoolEntry(int nameIndex) { + this.nameIndex = nameIndex; + } + + public void writeTo(OutputStream out) throws IOException { + write1(out, CONSTANT_Class); + write2(out, nameIndex + 1); + } + + public boolean equals(Object o) { + return o instanceof ClassPoolEntry + && ((ClassPoolEntry) o).nameIndex == nameIndex; + } + } + + private static class NameAndTypePoolEntry implements PoolEntry { + private final int nameIndex; + private final int typeIndex; + + public NameAndTypePoolEntry(int nameIndex, int typeIndex) { + this.nameIndex = nameIndex; + this.typeIndex = typeIndex; + } + + public void writeTo(OutputStream out) throws IOException { + write1(out, CONSTANT_NameAndType); + write2(out, nameIndex + 1); + write2(out, typeIndex + 1); + } + + public boolean equals(Object o) { + if (o instanceof NameAndTypePoolEntry) { + NameAndTypePoolEntry other = (NameAndTypePoolEntry) o; + return other.nameIndex == nameIndex && other.typeIndex == typeIndex; + } else { + return false; + } + } + } + + private static class FieldRefPoolEntry implements PoolEntry { + private final int classIndex; + private final int nameAndTypeIndex; + + public FieldRefPoolEntry(int classIndex, int nameAndTypeIndex) { + this.classIndex = classIndex; + this.nameAndTypeIndex = nameAndTypeIndex; + } + + public void writeTo(OutputStream out) throws IOException { + write1(out, CONSTANT_Fieldref); + write2(out, classIndex + 1); + write2(out, nameAndTypeIndex + 1); + } + + public boolean equals(Object o) { + if (o instanceof FieldRefPoolEntry) { + FieldRefPoolEntry other = (FieldRefPoolEntry) o; + return other.classIndex == classIndex + && other.nameAndTypeIndex == nameAndTypeIndex; + } else { + return false; + } + } + } + + private static class MethodRefPoolEntry implements PoolEntry { + private final int classIndex; + private final int nameAndTypeIndex; + + public MethodRefPoolEntry(int classIndex, int nameAndTypeIndex) { + this.classIndex = classIndex; + this.nameAndTypeIndex = nameAndTypeIndex; + } + + public void writeTo(OutputStream out) throws IOException { + write1(out, CONSTANT_Methodref); + write2(out, classIndex + 1); + write2(out, nameAndTypeIndex + 1); + } + + public boolean equals(Object o) { + if (o instanceof MethodRefPoolEntry) { + MethodRefPoolEntry other = (MethodRefPoolEntry) o; + return other.classIndex == classIndex + && other.nameAndTypeIndex == nameAndTypeIndex; + } else { + return false; + } + } + } +} diff --git a/sgx-jvm/avian/classpath/avian/Continuations.java b/sgx-jvm/avian/classpath/avian/Continuations.java new file mode 100644 index 0000000000..2c930970b0 --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/Continuations.java @@ -0,0 +1,327 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +import java.util.concurrent.Callable; + +/** + * This class provides methods to capture continuations and manage + * control flow when calling continuations. + * + *

A continuation is a snapshot of a thread's call stack which can + * be captured via callWithCurrentContinuation and later + * restored any number of times. The program may restore this + * snapshot by either feeding it a result (to be returned by + * callWithCurrentContinuation) or feeding it an + * exception (to be thrown by + * callWithCurrentContinuation). Continuations may be + * used to implement features such as coroutines, generators, and + * cooperative multitasking. + * + *

This class provides two static methods, + * callWithCurrentContinuation and + * dynamicWind, with similar semantics to the Scheme + * functions call-with-current-continuation and + * dynamic-wind, respectively. In addition, we define + * how continuations work with respect to native code, exceptions, + * try/finally blocks, synchronized blocks, and multithreading. + * + *

Continuations and Continuation Contexts

+ * + *

A continuation can be thought of as a singly-linked list of + * stack frames representing the call trace, where the head of the + * list is the frame of the method most recently called (i.e. the top + * of the stack). However, this trace only extends as far as the most + * recent chain of Java frames - it ends just prior to the most recent + * native frame in the stack. The reason for this is that the VM + * cannot, in general, safely capture and restore native frames. + * Therefore, each call from native code to Java (including the + * original invocation of main(String[]) or + * Thread.run()) represents a new continuation context in + * which continuations may be captured, and these will only contain + * frames from within that context. + * + *

Calling a continuation (i.e. feeding it a result or exception) + * causes the current continuation to be replaced with the called + * continuation. When the last method in this new continuation + * returns, it returns to the native frame which created the current + * context, which may or may not be the same as the context in which + * that continuation was created. + * + *

We define the return type of a continuation context as the + * return type of the first method called in that context. A + * continuation may be called from a different context than the one in + * which it was created, provided the return type of the latter is + * compatible with the current context. + * + *

Given a thread executing in context "A" which wants to call a + * continuation created in context "B", the following rules apply: + * + *

    + * + *
  • If the return type of "A" is void, the return + * type of "B" may be anything, including void
  • + * + *
  • If the return type of "A" is a primitive type, the return + * type of "B" must match exactly
  • + * + *
  • If the return type of "A" is an object type, that type must + * be assignable from the return type of "B" (i.e. the latter must + * either be the same as the former or a superclass or + * superinterface of it)
  • + * + *
+ * + *

A thread may call a continuation created by a different thread + * provided the return types are compatible. Multiple threads may + * safely call the same continuation simultaneously without + * synchronization. Any attempt to call a continuation from a context + * with an incompatible return type will throw an {@link + * avian.IncompatibleContinuationException}. + * + *

Winding, Unwinding, and Rewinding

+ * + *

Traditionally, Java provides one way to wind the execution stack + * (method calls) and two ways to unwind it (normal returns and + * exception unwinding). With continuations, we add a new way to + * rewind the stack and a new way to unwind it. + * + *

The call stack of a continuation may share frames with other + * continuations - in which case they share a common history. When + * calling a continuation "B" from the current continuation "A", the + * VM must unwind past any frames which are in "A" but not in "B" and + * rewind past any frames in "B" but not in "A". During this + * unwinding and rewinding, control may pass through synchronized and + * try/finally blocks while going down the old stack and up the new + * stack. + * + *

However, unlike the traditional processes of winding and + * unwinding, the VM will ignore these blocks - monitors will not be + * released or acquired and finally blocks will not execute. This is + * by design. The purpose of such a block is to acquire a resource, + * such as a file handle or monitor, once before executing a task and + * release it after the task is finished, regardless of how often the + * task might temporarily yield control to other continuations. + * + *

Alternatively, one might wish to acquire and release a resource + * each time control (re)winds to or unwinds from a continuation, + * respectively. In this case, one may use dynamicWind + * to register functions which will run every time that frame is + * passed, regardless of how the stack is wound or unwound. + */ +public class Continuations { + private Continuations() { } + + private static final ThreadLocal latestReset = new ThreadLocal(); + + /** + * Captures the current continuation, passing a reference to the + * specified receiver. + * + *

This method will either return the result returned by + * receiver.call(Callback), propagate the exception + * thrown by that method, return the result passed to the + * handleResult(T) method of the continuation, or throw the + * exception passed to the handleException(Throwable) method of the + * continuation. + */ + public static native T callWithCurrentContinuation + (Function,T> receiver) throws Exception; + + /** + * Calls the specified "before" and "after" tasks each time a + * continuation containing the call is wound or unwound, + * respectively. + * + *

This method first calls before.run(), then + * thunk.call(), and finally after.run(), + * returning the result of the second call. If + * before.run() does not return normally, the second + * and third calls will not happen. If thunk.call() + * throws an exception, after.run(), will be called + * before the exception is propagated. + * + *

If thunk.call() calls a continuation (directly or + * via a subroutine) which does not include the current call to + * dynamicWind, after.run() will be called + * before control passes to that continuation. If this call throws + * an exception, the exception will propagate to the current caller + * of dynamicWind. + * + *

If thunk.call() creates a continuation which is + * later called from a continuation which does not include the + * current call to dynamicWind, + * before.run() will be called before control passes to + * that continuation. As above, if this call throws an exception, + * the exception will propagate to the current caller of + * dynamicWind. + */ + public static T dynamicWind(Runnable before, + Callable thunk, + Runnable after) + throws Exception + { + UnwindResult result = dynamicWind2(before, thunk, after); + if (result.continuation != null) { + after.run(); + if (result.exception != null) { + result.continuation.handleException(result.exception); + } else { + result.continuation.handleResult(result.result); + } + throw new AssertionError(); + } else { + return (T) result.result; + } + } + + public static C reset(final Callable thunk) throws Exception { + final Reset reset = new Reset(latestReset.get()); + latestReset.set(reset); + try { + Object result = callWithCurrentContinuation + (new Function,Object>() { + public Object call(Callback continuation) throws Exception { + reset.continuation = continuation; + return thunk.call(); + } + }); + + while (true) { + Cell shift = reset.shifts; + if (shift != null) { + reset.shifts = shift.next; + result = shift.value.call(result); + } else { + return (C) result; + } + } + } finally { + latestReset.set(reset.next); + } + } + + public static A shift + (final Function,C> receiver) + throws Exception + { + return (A) callWithCurrentContinuation + (new Function,Object>() { + public Object call(final Callback continuation) { + final Reset reset = latestReset.get(); + reset.shifts = new Cell(new Function() { + public Object call(Object ignored) throws Exception { + return receiver.call + (new Function() { + public Object call(final Object argument) + throws Exception + { + return callWithCurrentContinuation + (new Function,Object>() { + public Object call + (final Callback shiftContinuation) + throws Exception + { + reset.shifts = new Cell + (new Function() { + public Object call(Object result) + throws Exception + { + shiftContinuation.handleResult(result); + throw new AssertionError(); + } + }, + reset.shifts); + + continuation.handleResult(argument); + throw new AssertionError(); + } + }); + } + }); + } + + public void handleException(Throwable exception) { + throw new AssertionError(); + } + }, reset.shifts); + + reset.continuation.handleResult(null); + throw new AssertionError(); + } + }); + } + + private static native UnwindResult dynamicWind2(Runnable before, + Callable thunk, + Runnable after) + throws Exception; + + private static UnwindResult wind(Runnable before, + Callable thunk, + Runnable after) + throws Exception + { + before.run(); + + try { + return new UnwindResult(null, thunk.call(), null); + } finally { + after.run(); + } + } + + private static void rewind(Runnable before, + Callback continuation, + Object result, + Throwable exception) + throws Exception + { + before.run(); + + if (exception != null) { + continuation.handleException(exception); + } else { + continuation.handleResult(result); + } + + throw new AssertionError(); + } + + private static class Continuation implements Callback { + public native void handleResult(T result); + public native void handleException(Throwable exception); + } + + private static class UnwindResult { + public final Continuation continuation; + public final Object result; + public final Throwable exception; + + public UnwindResult(Continuation continuation, Object result, + Throwable exception) + { + this.continuation = continuation; + this.result = result; + this.exception = exception; + } + } + + private static class Reset { + public Callback continuation; + public final Reset next; + public Cell shifts; + + public Reset(Reset next) { + this.next = next; + } + } +} diff --git a/sgx-jvm/avian/classpath/avian/Data.java b/sgx-jvm/avian/classpath/avian/Data.java new file mode 100644 index 0000000000..0914362e83 --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/Data.java @@ -0,0 +1,310 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.AbstractSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.Collections; + +public class Data { + public static int nextPowerOfTwo(int n) { + int r = 1; + while (r < n) r <<= 1; + return r; + } + + public static boolean equal(V a, V b) { + return a == null ? b == null : a.equals(b); + } + + public static T[] toArray(Collection collection, T[] array) { + Class c = array.getClass().getComponentType(); + + if (array.length < collection.size()) { + array = (T[]) java.lang.reflect.Array.newInstance(c, collection.size()); + } + + int i = 0; + for (Object o: collection) { + if (c.isInstance(o)) { + array[i++] = (T) o; + } else { + throw new ArrayStoreException(); + } + } + + return array; + } + + public static String toString(Collection c) { + StringBuilder sb = new StringBuilder(); + sb.append("["); + for (Iterator it = c.iterator(); it.hasNext();) { + sb.append(it.next()); + if (it.hasNext()) { + sb.append(","); + } + } + sb.append("]"); + return sb.toString(); + } + + public static String toString(Map m) { + StringBuilder sb = new StringBuilder(); + sb.append("{"); + for (Iterator it = m.entrySet().iterator(); it.hasNext();) { + Entry e = it.next(); + sb.append(e.getKey()) + .append("=") + .append(e.getValue()); + if (it.hasNext()) { + sb.append(","); + } + } + sb.append("}"); + return sb.toString(); + } + + public interface EntryMap { + public int size(); + + public Entry find(Object key); + + public Entry remove(Object key); + + public void clear(); + + public Iterator> iterator(); + } + + public static class EntrySet extends AbstractSet> { + private final EntryMap map; + + public EntrySet(EntryMap map) { + this.map = map; + } + + public int size() { + return map.size(); + } + + public boolean isEmpty() { + return map.size() == 0; + } + + public boolean contains(Object o) { + return (o instanceof Entry) + && map.find(((Entry)o).getKey()) != null; + } + + public boolean add(Entry e) { + throw new UnsupportedOperationException(); + } + + public boolean remove(Object o) { + return (o instanceof Entry) + && map.remove(((Entry) o).getKey()) != null; + } + + public boolean remove(Entry e) { + return map.remove(e.getKey()) != null; + } + + public Object[] toArray() { + return toArray(new Object[size()]); + } + + public T[] toArray(T[] array) { + return Data.toArray(this, array); + } + + public void clear() { + map.clear(); + } + + public Iterator> iterator() { + return map.iterator(); + } + } + + public static class KeySet extends AbstractSet { + private final EntryMap map; + + public KeySet(EntryMap map) { + this.map = map; + } + + public int size() { + return map.size(); + } + + public boolean isEmpty() { + return map.size() == 0; + } + + public boolean contains(Object key) { + return map.find(key) != null; + } + + public boolean add(K key) { + throw new UnsupportedOperationException(); + } + + public boolean remove(Object key) { + return map.remove(key) != null; + } + + public Object[] toArray() { + return toArray(new Object[size()]); + } + + public T[] toArray(T[] array) { + return Data.toArray(this, array); + } + + public void clear() { + map.clear(); + } + + public Iterator iterator() { + return new KeyIterator(map.iterator()); + } + } + + public static class Values implements Collection { + private final EntryMap map; + + public Values(EntryMap map) { + this.map = map; + } + + public int size() { + return map.size(); + } + + public boolean isEmpty() { + return map.size() == 0; + } + + public boolean contains(Object value) { + for (Iterator> it = map.iterator(); it.hasNext();) { + if (equal(it.next().getValue(), value)) { + return true; + } + } + return false; + } + + public boolean containsAll(Collection c) { + if (c == null) { + throw new NullPointerException("collection is null"); + } + + for (Iterator it = c.iterator(); it.hasNext();) { + if (! contains(it.next())) { + return false; + } + } + + return true; + } + + public boolean add(V value) { + throw new UnsupportedOperationException(); + } + + public boolean addAll(Collection collection) { + throw new UnsupportedOperationException(); + } + + public boolean remove(Object value) { + for (Iterator> it = map.iterator(); + it.hasNext();) + { + if (equal(it.next().getValue(), value)) { + it.remove(); + return true; + } + } + return false; + } + + public boolean removeAll(Collection c) { + boolean changed = false; + for (Iterator> it = map.iterator(); it.hasNext();) { + if (c.contains(it.next().getValue())) { + it.remove(); + changed = true; + } + } + return changed; + } + + public Object[] toArray() { + return toArray(new Object[size()]); + } + + public T[] toArray(T[] array) { + return Data.toArray(this, array); + } + + public void clear() { + map.clear(); + } + + public Iterator iterator() { + return new ValueIterator(map.iterator()); + } + } + + public static class KeyIterator implements Iterator { + private final Iterator> it; + + public KeyIterator(Iterator> it) { + this.it = it; + } + + public K next() { + return it.next().getKey(); + } + + public boolean hasNext() { + return it.hasNext(); + } + + public void remove() { + it.remove(); + } + } + + public static class ValueIterator implements Iterator { + private final Iterator> it; + + public ValueIterator(Iterator> it) { + this.it = it; + } + + public V next() { + return it.next().getValue(); + } + + public boolean hasNext() { + return it.hasNext(); + } + + public void remove() { + it.remove(); + } + } +} diff --git a/sgx-jvm/avian/classpath/avian/FieldAddendum.java b/sgx-jvm/avian/classpath/avian/FieldAddendum.java new file mode 100644 index 0000000000..525dfc6f3c --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/FieldAddendum.java @@ -0,0 +1,13 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +public class FieldAddendum extends Addendum { } diff --git a/sgx-jvm/avian/classpath/avian/FormatString.java b/sgx-jvm/avian/classpath/avian/FormatString.java new file mode 100644 index 0000000000..a66c3cfa9e --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/FormatString.java @@ -0,0 +1,992 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.IllegalFormatException; +import java.util.List; + +// ------------------------------------------------------------------------- // +// things that must be done in order to call this semi-complete: +// ------------------------------------------------------------------------- // +// * get the date formatter working for individual fields at a minimum +// ------------------------------------------------------------------------- // + +/** + * A Java flavored printf for classpath-challenged JVMs. + * + * Each instance of this class is a threadsafe pre-parsed format pattern. Refer + * to the very detailed class description of java.util.Formatter in the OpenJDK + * API documentation for an explanation of the supported formats. + * + * Should be easily portable to other Java runtimes that do not include the + * printf functionality that was introduced in Java 5. + * + * Aims to be lightweight and reasonably fast, and provide reasonably complete + * API compatibility with the OpenJDK implementation. To clarify what + * "reasonably complete" means in this context, this implementation should + * accept any valid format string that the OpenJDK version accepts. However, + * it should not be relied upon to throw an Exception for every format pattern + * that the OpenJDK implementation does. + * + * If your program's behavior relies on the side effects from an Exception + * being thrown for an invalid format string, this might not be for you. + * + * Perhaps more troubling is the fact that the correct localization of numbers + * and temporal values is barely even attempted, even though the parser accepts + * the flags without even a warning. However, now you have been warned. + * + * @author bcg + */ +public final class FormatString { + + /** Parses a format string and returns a compiled representation of it. */ + public static final FormatString compile(String fmt) { + return new FormatString(fmt); + } + + /** The original string value that was parsed */ + public String source() { + return _source; + } + + /** Processes the supplied arguments through the compiled format string + and returns the result as a String. */ + public final String format(Object... args) { + final StringBuilder bldr = new StringBuilder(); + try { + format(bldr, args); + return bldr.toString(); + } catch (IOException e) { + throw new IllegalStateException( + "Should not get IOException when writing to StringBuilder", e + ); + } + } + + /** Processes the supplied arguments through the compiled format string + and writes the result of each component directly to an Appendable */ + public final void format(final Appendable a, Object... fmt_args) + throws IOException { + final Object[] args = fmt_args != null ? fmt_args : new Object[0]; + int cntr = 0; + for (final FmtCmpnt cmp : _components) { + if (cmp._conversion == CONV_LITRL) { + a.append(cmp._source); + continue; + } + final Object arg; + if (!acceptsArgument(cmp._conversion)) { + arg = null; + } else { + final int index = cmp._argument_index; + switch (index) { + case AIDX_NONE: + if ((cntr) >= args.length) { + throw new IllegalFormatException( + "Format specified at least " + (cntr+1) + + " arguments, but " + cntr + " were supplied." + ); + } + arg = args[cntr++]; + break; + case AIDX_PREV: + arg = args[cntr]; + break; + default: + if (index < 1) { + throw new IllegalArgumentException(); + } else if (index > args.length) { + throw new IllegalArgumentException(); + } else { + arg = args[index - 1]; + } + } + } + convert(a, arg, cmp._conversion, cmp._flags, cmp._width, cmp._precision); + } + } + + //- conversions + static final byte CONV_LITRL = 0x0; + static final byte CONV_NLINE = 0x1; + static final byte CONV_PRCNT = 0x2; + static final byte CONV_BOOLN = 0x3; + static final byte CONV_DTIME = 0x4; + static final byte CONV_STRNG = 0x5; + static final byte CONV_HCODE = 0x6; + static final byte CONV_CHRCT = 0x7; + static final byte CONV_DECML = 0x8; + static final byte CONV_OCTAL = 0x9; + static final byte CONV_HXDEC = 0xA; + static final byte CONV_CPSCI = 0xB; + static final byte CONV_GNSCI = 0xC; + static final byte CONV_FLOAT = 0xD; + static final byte CONV_HXEXP = 0xE; + + //- format component flags + static final byte FLAG_FORCE_UPPER_CASE = (byte)(1<<7); + static final byte FLAG_NEGATIVES_IN_PARENS = (byte)(1<<6); // ('(') + static final byte FLAG_GROUPING_SEPARATORS = (byte)(1<<5); // (',') + static final byte FLAG_LEADING_ZERO_PADDED = (byte)(1<<4); // ('0') + static final byte FLAG_LEADING_SPACE_PADDED = (byte)(1<<3); // (' ') + static final byte FLAG_ALWAYS_INCLUDES_SIGN = (byte)(1<<2); // ('+') + static final byte FLAG_ALTERNATE_FORM = (byte)(1<<1); // ('#') + static final byte FLAG_LEFT_JUSTIFIED = (byte)(1<<0); // ('-') + + //- conversion capability flags + static final byte CFLG_WDTH_SUPPRT = CONV_PRCNT; + static final byte CFLG_ACCEPTS_ARG = CONV_BOOLN; + static final byte CFLG_NUMERIC_VAL = CONV_DECML; + static final byte CFLG_PREC_SUPPRT = CONV_STRNG; + + //- special argument indices + static final int AIDX_PREV = -1; + static final int AIDX_NONE = 0; + + /** the original serialized format string */ + private final String _source; + + /** array of components parsed from the source string */ + private final FmtCmpnt[] _components; + + /*/ keeping this private for now to encourage access through the static + compile method, which might allow caching format string instances if it + turns out there is an advantage to that. /*/ + /** Constructor */ + private FormatString(final String fmt) { + this._source = fmt; + final List cmps = new ArrayList(); + for ( int i = 0; (i = next(fmt, cmps, i)) > -1; ); + this._components = cmps.toArray(new FmtCmpnt[cmps.size()]); + } + + /** Iterates over the tokens in an input string to extract the components */ + private static final int next( + final String fmt, final List cmps, final int startIndex) { + final int strln = fmt.length(); + if (startIndex >= strln) { + return -1; + } + final char c = fmt.charAt(startIndex); + if (c == '%') { + // this is the start of a specifier + final FmtSpecBldr bldr = new FmtSpecBldr(); + for (int i = startIndex + 1; i < strln; i++) { + final char ch = fmt.charAt(i); + final FmtCmpnt cmp = bldr.append(ch); + if (cmp != null) { + cmps.add(cmp); + return (i+1); + } + } + throw new IllegalFormatException("Incomplete specifier at end of fmt"); + } else { + // this is the start of a literal + final StringBuilder literal = new StringBuilder(); + literal.append(c); + for (int i = startIndex + 1; i < strln; i++) { + final char ch = fmt.charAt(i); + // write the current buffer if the next character starts a specifier + if (ch == '%') { + final FmtCmpnt cmp = new FmtCmpnt(literal.toString()); + cmps.add(cmp); + return i; + } + literal.append(ch); + } + // write the current buffer if the end of the format has been reached + final FmtCmpnt cmp = new FmtCmpnt(literal.toString()); + cmps.add(cmp); + return -1; + } + } + + /** Checks a flag byte to see if a given flag is set. Only FLAG_* constants + from the enclosing class should be passed in for toCheck... otherwise the + behavior is undefined. */ + static final boolean checkFlag(final byte flags, final byte toCheck) { + return (flags & toCheck) != 0; + } + + /** Checks if a given conversion accepts(requires) an argument. Only the CONV_ + flags from the enclosing class should be passed in, otherwise the result + of this method is undefined as should not be used. */ + static final boolean acceptsArgument(final byte conversion) { + return conversion >= CFLG_ACCEPTS_ARG; + } + + /** Checks if a given conversion allows specifying a precision. Only the CONV_ + flags from the enclosing class should be passed in, otherwise the result + of this method is undefined as should not be used. */ + static final boolean precisionSupported(final byte conversion) { + return conversion >= CFLG_PREC_SUPPRT; + } + + /** Checks if a given conversion allows specifying a width. Only the CONV_ + flags from the enclosing class should be passed in, otherwise the result + of this method is undefined and should not be trusted. */ + static final boolean widthSupported(final byte conversion) { + return conversion >= CFLG_WDTH_SUPPRT; + } + + /** Checks if a given conversion expects a numeric value. Only the CONV_ + flags from the enclosing class should be passed in, otherwise the result + of this method is undefined and should not be trusted. */ + static final boolean isNumeric(final byte conversion) { + return conversion >= CFLG_NUMERIC_VAL; + } + + /** The newline character for the current platform. */ + static final String NEWLINE = System.getProperty("line.separator"); + + /** Performs conversion on the supplied argument */ + static final void convert( + final Appendable appendable, + final Object arg, + final byte conversion, + final byte flags, + final int width, + final int precision) throws IOException { + int radix = 0; + switch (conversion) { + case CONV_LITRL: + throw new IllegalArgumentException("cannot convert a literal"); + case CONV_NLINE: + appendable.append(NEWLINE); + return; + case CONV_PRCNT: + convertPercent(appendable, arg, flags, width, precision); + return; + case CONV_BOOLN: + convertBoolean(appendable, arg, flags, width, precision); + return; + case CONV_DTIME: + convertDate(appendable, arg, flags, width, precision); + return; + case CONV_STRNG: + convertString(appendable, arg, flags, width, precision); + return; + case CONV_HCODE: + convertHashcode(appendable, arg, flags, width, precision); + return; + case CONV_CHRCT: + convertChar(appendable, arg, flags, width, precision); + return; + case CONV_DECML: if (radix == 0) { radix = 10; }; + case CONV_OCTAL: if (radix == 0) { radix = 8; }; + case CONV_HXDEC: if (radix == 0) { radix = 16; }; + if (arg instanceof Long) { + convertLong(appendable, (Long) arg, flags, width, precision, radix); + } else { + convertInteger(appendable, arg, flags, width, precision, radix); + } + return; + case CONV_CPSCI: + case CONV_GNSCI: + case CONV_FLOAT: + case CONV_HXEXP: + convertFloat(appendable, arg, flags, width, precision, 10); + return; + } + throw new IllegalStateException("not implemented: " + conversion); + } + + static void convertPercent( + final Appendable a, + final Object arg, + final byte flags, + final int width, + final int precision) throws IOException { + final String val = "%"; + appendify(a, val, flags, width, precision); + } + + static void convertDate( + final Appendable a, + final Object arg, + final byte flags, + final int width, + final int precision) throws IOException { + final String val = (arg == null) ? "null" : arg.toString(); + appendify(a, val, flags, width, precision); + } + + static void convertString( + final Appendable a, + final Object arg, + final byte flags, + final int width, + final int precision) throws IOException { + final String val = (arg == null) ? "null" : arg.toString(); + appendify(a, val, flags, width, precision); + } + + static void convertHashcode( + final Appendable a, + final Object arg, + final byte flags, + final int width, + final int precision) throws IOException { + final String val = (arg == null) + ? "null" + : Integer.toHexString(arg.hashCode()); + appendify(a, val, flags, width, precision); + } + + static void convertBoolean( + final Appendable a, + final Object arg, + final byte flags, + final int width, + final int precision) throws IOException { + final String val; + if (arg == null) { + val = "false"; + } else if (arg instanceof Boolean) { + val = String.valueOf(arg); + } else { + val = "true"; + } + appendify(a, val, flags, width, precision); + } + + static void convertChar( + final Appendable a, + final Object arg, + final byte flags, + final int width, + final int precision) throws IOException { + final String val; + if (arg instanceof Character) { + val = ((Character) arg).toString(); + } else if ( arg instanceof Byte || + arg instanceof Short || + arg instanceof Integer ){ + final int codePoint = ((Number) arg).intValue(); + if (codePoint >= 0 && codePoint <= 0x10FFFF) { //<-- isValidCodePoint()? + val = new String(Character.toChars(codePoint)); + } else { + throw new IllegalFormatException("Invalid code point: " + arg); + } + } else { + throw new IllegalFormatException("Cannot do char conversion: " + arg); + } + appendify(a, val, flags, width, precision); + } + + // FIXME: this is broken for octal formats with negative values + static void convertLong( + final Appendable a, + final Long arg, + final byte flags, + final int width, + final int precision, + final int radix) throws IOException { + final String val; + final Long n = arg; + final long longValue = n.longValue(); + if (radix == 10 || longValue > -1) { + val = Long.toString(longValue, radix); + } else { + final long upper = 0xFFFFFFFFL&(longValue>>31); + final long lower = 0xFFFFFFFFL&(longValue); + val = Long.toString(upper, radix) + Long.toString(lower, radix); + } + appendify(a, val, flags, width, precision); + } + + static void convertInteger( + final Appendable a, + final Object arg, + final byte flags, + final int width, + final int precision, + final int radix) throws IOException { + final String val; + final Number n = (Number) arg; + final long longValue = n.longValue(); + final long modifier; + if (arg instanceof Integer) modifier = 0xFFFFFFFFL+1; else + if (arg instanceof Short) modifier = 0xFFFFL+1; else + if (arg instanceof Byte) modifier = 0xFFL+1; + else throw new IllegalFormatException( + "not an integer number: " + (arg != null ? arg.getClass() : null) + ); + if (radix != 10 && longValue < 0) { + val = Long.toString(longValue + modifier, radix); + } else { + val = Long.toString(longValue, radix); + } + appendify(a, val, flags, width, precision); + } + + // FIXME: I'm lazy, so hexidecimal exponential isn't implemented, sorry - bcg + static void convertFloat( + final Appendable a, + final Object arg, + final byte flags, + final int width, + final int precision, + final int radix) throws IOException { + final String val; + final Number n = (Number) arg; + if (arg instanceof Float) { + val = Float.toString(n.floatValue()); + } else if (arg instanceof Double) { + val = Double.toString(n.doubleValue()); + } else { + throw new IllegalFormatException( + "not a floating point number: " + (arg != null ? arg.getClass() : null) + ); + } + appendify(a, val, flags, width, precision); + } + + static void appendify( + final Appendable a, + final String val, + final byte flags, + final int width, + final int precision) throws IOException { + String result = val; + if (checkFlag(flags, FLAG_FORCE_UPPER_CASE)) { + result = result.toUpperCase(); + } + // TODO: implement other flags + // (+) always include sign + // (,) grouping separators + // (() negatives in parentheses + if (precision > 0) { + // FIXME: this behavior should be different for floating point numbers + final int difference = result.length() - precision; + if (difference > 0) { + result = result.substring(0, precision); + a.append(result); + return; + } + } + if (width > 0) { + final int difference = width - result.length(); + final boolean leftJustified = checkFlag(flags, FLAG_LEFT_JUSTIFIED); + if (!leftJustified && difference > 0) { + char fill = checkFlag(flags, FLAG_LEADING_ZERO_PADDED) ? '0' : ' '; + fill(a, difference, fill); + } + a.append(result); + if (leftJustified && difference > 0) { + fill(a, difference, ' '); + } + return; + } + a.append(result); + } + + private static void fill(Appendable a, int num, char c) throws IOException { + while (num > 0) { + a.append(c); + num--; + } + } + + /** Represents a single chunk of the format string, either a literal or one of + the specifiers documented in OpenJDK's javadoc for java.util.Formatter. + This struct is immutable so can be safely shared across threads. */ + private static final class FmtCmpnt { + + private final String _source; + private final byte _conversion; + private final int _argument_index; + private final int _width; + private final int _precision; + private final byte _flags; + + private FmtCmpnt(final String literal) { + this(literal, CONV_LITRL, 0, 0, 0, (byte)0); + } + private FmtCmpnt( + final String src, + final byte conversion, + final int argumentIndex, + final int width, + final int precision, + final byte flags) { + this._source = src; + this._conversion = conversion; + this._argument_index = argumentIndex; + this._width = width; + this._precision = precision; + this._flags = flags; + } + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("{ ") + .append("source = '").append(_source).append("', ") + .append("conversion = ").append(_conversion).append(", ") + .append("flags = ").append(Byte.toString(_flags, 2)).append(", ") + .append("arg_index = ").append(_argument_index).append(", ") + .append("width = ").append(_width).append(", ") + .append("precision = ").append(_precision).append(", ") + .append("}"); + return sb.toString(); + } + } + + /** + * Helper class for parsing a stream of characters into FmtCmpnt objects. + */ + private static final class FmtSpecBldr { + + private StringBuilder _source; + private byte _conversion; + private int _argument_index; + private int _width; + private int _precision; + private byte _flags; + + private FmtSpecBldr() { + this.init(); + } + + private final void init() { + _argument_index = + _width = + _precision = + _conversion = + _flags = 0; + _source = null; + } + + private final FmtCmpnt build() { + final FmtCmpnt result = new FmtCmpnt( + _source.toString(), + _conversion, + _argument_index, + _width, + _precision, + _flags + ); + init(); + return result; + } + + private final FmtCmpnt append(char c) { + + if (_source == null) { + _source = new StringBuilder(); + } + _source.append(c); + + // FIXME: none of these date formats are implemented, because lazy + // if a datetime is specified, after the conversion character a time + // format specifier is expected. This is the only case where a character + // is allowed after the conversion character. + if (_conversion == CONV_DTIME) switch (c) { + + // Hour of the day for the 24-hour clock, formatted as two digits with a + // leading zero as necessary i.e. 00 - 23. + case 'H': + + // Hour for the 12-hour clock, formatted as two digits with a leading + // zero as necessary, i.e. 01 - 12. + case 'I': + + // Hour of the day for the 24-hour clock, i.e. 0 - 23. + case 'k': + + // Hour for the 12-hour clock, i.e. 1 - 12. + case 'l': + + // Minute within the hour formatted as two digits with a leading zero + // as necessary, i.e. 00 - 59. + case 'M': + + // Seconds within the minute, formatted as two digits with a leading + // zero as necessary, i.e. 00 - 60 ("60" is a special value required to + // support leap seconds). + case 'S': + + // Millisecond within the second formatted as three digits with leading + // zeros as necessary, i.e. 000 - 999. + case 'L': + + // Nanosecond within the second, formatted as nine digits with leading + // zeros as necessary, i.e. 000000000 - 999999999. + case 'N': + + // Locale-specific morning or afternoon marker in lower case, + // e.g."am" or "pm". Use of the conversion prefix 'T' forces this + // output to upper case. + case 'p': + + // RFC 822 style numeric time zone offset from GMT, e.g. -0800. + case 'z': + + // A string representing the abbreviation for the time zone. The + // Formatter's locale will supersede the locale of the argument + // (if any). + case 'Z': + + // Seconds since the beginning of the epoch + // starting at 1 January 1970 00:00:00 UTC, + // i.e. Long.MIN_VALUE/1000 to Long.MAX_VALUE/1000. + case 's': + + // Milliseconds since the beginning of the epoch + // starting at 1 January 1970 00:00:00 UTC, + // i.e. Long.MIN_VALUE to Long.MAX_VALUE. + case 'Q': + + // ------------------------------------------------------------------ + // The following conversion characters are used for formatting dates: + // ------------------------------------------------------------------ + + // Locale-specific full month name, e.g. "January", "February". + case 'B': + + // Locale-specific abbreviated month name, e.g. "Jan", "Feb". + case 'b': + + // Same as 'b'. + case 'h': + + // Locale-specific full name of the day of the week, e.g. "Sunday" + case 'A': + + // Locale-specific short name of the day of the week, e.g. "Sun" + case 'a': + + // Four-digit year divided by 100, formatted as two digits with leading + // zero as necessary, i.e. 00 - 99 + case 'C': + + // Year, formatted as at least four digits with leading zeros + // as necessary, e.g. 0092 equals 92 CE for the Gregorian calendar. + case 'Y': + + // Last two digits of the year, formatted with leading zeros + // as necessary, i.e. 00 - 99. + case 'y': + + // Day of year, formatted as three digits with leading zeros + // as necessary, e.g. 001 - 366 for the Gregorian calendar. + case 'j': + + // Month, formatted as two digits with leading zeros as necessary, + // i.e. 01 - 13. + case 'm': + + // Day of month, formatted as two digits with leading zeros as + // necessary, i.e. 01 - 31 + case 'd': + + // Day of month, formatted as two digits, i.e. 1 - 31. + case 'e': + + // ------------------------------------------------------------------- + // The following conversion characters are used for formatting common + // date/time compositions. + // ------------------------------------------------------------------- + + // Time formatted for the 24-hour clock as "%tH:%tM" + case 'R': + + // Time formatted for the 24-hour clock as "%tH:%tM:%tS". + case 'T': + + // Time formatted for the 12-hour clock as "%tI:%tM:%tS %Tp". The location + // of the morning or afternoon marker ('%Tp') may be locale-dependent. + case 'r': + + // Date formatted as "%tm/%td/%ty". + case 'D': + + // ISO 8601 complete date formatted as "%tY-%tm-%td". + case 'F': + + // Date and time formatted as "%ta %tb %td %tT %tZ %tY", e.g. + // "Sun Jul 20 16:17:00 EDT 1969". + case 'c': + return seal(CONV_DTIME); + + default: + throw new IllegalFormatException("Illegal date/time modifier: " + c); + } + + // -- Flags and Switches ----------------------------------------------- + // these are the possible characters for flags, width, etc. if the input + // is not one of these characters, it needs to be a valid conversion char. + // because the possible flags can vary based on the type of conversion, + // it is easiest to just buffer the flags, argument index, etc. until a + // conversion character has been reached. The seal() method will then + // work out if the specified flags are valid for the given conversion. + switch (c) { + case '$': + case '<': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + case '+': + case '\'': + case ' ': + case '#': + case ',': + case '.': + case '(': + return null; + // -- End of Flags and Switches ---------------------------------------- + + // -- Conversion characters -------------------------------------------- + // If this point is reached, then the current character must be a valid + // conversion character. If it is not, it should fall through the rest + // of the switch statement below and throw an IllegalFormatException + + // string conversion + case 'S': + setFlagTrue(FLAG_FORCE_UPPER_CASE); + case 's': + return seal(CONV_STRNG); + + // newline conversion + case 'n': + return seal(CONV_NLINE); + + // percent conversion + case '%': + return seal(CONV_PRCNT); + + // decimal numeric conversion + case 'd': + return seal(CONV_DECML); + + // hexidecimal numeric conversion + case 'X': + setFlagTrue(FLAG_FORCE_UPPER_CASE); + case 'x': + return seal(CONV_HXDEC); + + // datetime conversion + case 'T': + setFlagTrue(FLAG_FORCE_UPPER_CASE); + case 't': + _conversion = CONV_DTIME; + return null; + + // boolean conversion + case 'B': + setFlagTrue(FLAG_FORCE_UPPER_CASE); + case 'b': + return seal(CONV_BOOLN); + + // hashcode conversion + case 'H': + setFlagTrue(FLAG_FORCE_UPPER_CASE); + case 'h': + return seal(CONV_HCODE); + + // character conversion + case 'C': + setFlagTrue(FLAG_FORCE_UPPER_CASE); + case 'c': + return seal(CONV_CHRCT); + + // octal numeric conversion + case 'o': + return seal(CONV_OCTAL); + + // computerized scientific conversion + case 'E': + setFlagTrue(FLAG_FORCE_UPPER_CASE); + case 'e': + return seal(CONV_CPSCI); + + // floating point conversion + case 'f': + return seal(CONV_FLOAT); + + // general scientific floating point conversion + case 'G': + setFlagTrue(FLAG_FORCE_UPPER_CASE); + case 'g': + return seal(CONV_GNSCI); + + // hexidecimal exponential floating point conversion + case 'A': + setFlagTrue(FLAG_FORCE_UPPER_CASE); + case 'a': + return seal(CONV_HXEXP); + // -- End of Conversion characters -------------------------------------- + + default: + throw new IllegalFormatException( + "Invalid character encountered while parsing specifier: " + c); + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("{ ") + .append("source = '").append(_source).append("', ") + .append("conversion = ").append(_conversion).append(", ") + .append("flags = ").append(Byte.toString(_flags, 2)).append(", ") + .append("arg_index = ").append(_argument_index).append(", ") + .append("width = ").append(_width).append(", ") + .append("precision = ").append(_precision).append(", ") + .append("}"); + return sb.toString(); + } + + private final FmtCmpnt seal(final byte conversion) { + + // throwing IllegalStateException instead of IllegalFormatException + // because this should only occur if there is a bug in the append() + // method. In that case I'd prefer to fail fast even if the user is + // explicitly trying to catch IllegalFormatException. + if (conversion < 0 || conversion > 0xE) { + throw new IllegalArgumentException(); + } + + this._conversion = conversion; + + // if the length is less than 2, it must mean that only the conversion + // character was specified. Since a conversion character by itself is a + // valid pattern, just build and return + if (_source.length() < 2) { + return build(); + } + + // --------------------------------------------------------------------- + // spec format: [argument_index$][flags][width][.precision] + // --------------------------------------------------------------------- + // the last character of the spec is the conversion character which has + // already been translated the appropriate byte by the append() method, + // so the last character gets chopped off before processing + final String spec = _source.substring(0, _source.length() - 1); + + // if argument index is supported, it should be followed by a '$' and be + // comprised only of digit characters, or it should be a single '<' char + final int dollarIndex = spec.indexOf('$'); + if (dollarIndex > -1) { + if (acceptsArgument(conversion)) { + if (spec.charAt(dollarIndex - 1) == '<') { + _argument_index = AIDX_PREV; + } else { + _argument_index = Integer.valueOf(spec.substring(0, dollarIndex)); + } + } else { + throw new IllegalFormatException( + "Formats that do not accept arguments cannot specify an index." + ); + } + } + if (dollarIndex == (spec.length() - 1)) { + return build(); + } + + // if precision is supported, look for the first period and assume that + // everything before is the width and everything after is the precision + final int dotIndex = spec.indexOf('.'); + if (dotIndex > -1) { + if (precisionSupported(conversion)) { + _precision = Integer.valueOf(spec.substring(dotIndex + 1)); + } else { + throw new IllegalFormatException( + "Precision is not supported for " + conversion + ); + } + } + + // Now loop over the remaining characters to get the width as well as any + // applicable flags. Note: 0 is a valid flag so must be handled carefully + final String remaining = spec.substring( + Math.max(dollarIndex, 0), dotIndex > -1 ? dotIndex : spec.length() + ); + int flagsEnd = -1; + for (int i = 0, n = remaining.length(); i < n && (flagsEnd == -1); i++) { + final char c = remaining.charAt(i); + switch (c) { + case '-': + ensureLeftJustifySupported(); + setFlagTrue(FLAG_LEFT_JUSTIFIED); + break; + case '#': + ensureNumeric(c); + setFlagTrue(FLAG_ALTERNATE_FORM); + break; + case '+': + ensureNumeric(c); + setFlagTrue(FLAG_ALWAYS_INCLUDES_SIGN); + break; + case ' ': + ensureNumeric(c); + setFlagTrue(FLAG_LEADING_SPACE_PADDED); + break; + case ',': + ensureNumeric(c); + setFlagTrue(FLAG_GROUPING_SEPARATORS); + break; + case '(': + ensureNumeric(c); + setFlagTrue(FLAG_NEGATIVES_IN_PARENS); + break; + case '0': + ensureNumeric(c); + setFlagTrue(FLAG_LEADING_ZERO_PADDED); + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + flagsEnd = i; + _width = Integer.valueOf(remaining.substring(flagsEnd)); + return build(); + } + } + throw new IllegalStateException(); + } + private final void ensureLeftJustifySupported() { + if (!widthSupported(_conversion)) { + throw new IllegalFormatException( + "Conversion must support width if specifying left justified." + ); + } + } + private final void ensureNumeric(final char c) { + if (!isNumeric(_conversion)) { + throw new IllegalFormatException( + "flag " + c + " only supported on numeric specifiers." + ); + } + } + private final void setFlagTrue(final byte flag) { + _flags |= flag; + }/* + final void setFlagFalse(final byte flag) { + _flags &= ~flag; + }*/ + } +} diff --git a/sgx-jvm/avian/classpath/avian/Function.java b/sgx-jvm/avian/classpath/avian/Function.java new file mode 100644 index 0000000000..246a3ae512 --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/Function.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +public interface Function { + public B call(A argument) throws Exception; +} diff --git a/sgx-jvm/avian/classpath/avian/IncompatibleContinuationException.java b/sgx-jvm/avian/classpath/avian/IncompatibleContinuationException.java new file mode 100644 index 0000000000..15007c0fc0 --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/IncompatibleContinuationException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +public class IncompatibleContinuationException extends Exception { + public IncompatibleContinuationException(String message) { + super(message); + } + + public IncompatibleContinuationException() { + super(); + } +} diff --git a/sgx-jvm/avian/classpath/avian/InnerClassReference.java b/sgx-jvm/avian/classpath/avian/InnerClassReference.java new file mode 100644 index 0000000000..614a4da93a --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/InnerClassReference.java @@ -0,0 +1,18 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +public class InnerClassReference { + public byte[] inner; + public byte[] outer; + public byte[] name; + public short flags; +} diff --git a/sgx-jvm/avian/classpath/avian/Iso88591.java b/sgx-jvm/avian/classpath/avian/Iso88591.java new file mode 100644 index 0000000000..dcdb7baf6a --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/Iso88591.java @@ -0,0 +1,26 @@ + +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +import java.io.ByteArrayOutputStream; + +public class Iso88591 { + + public static byte[] encode(char[] s16, int offset, int length) { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + for (int i = offset; i < offset+length; ++i) { + // ISO-88591-1/Latin-1 is the same as UTF-16 under 0x100 + buf.write(s16[i]); + } + return buf.toByteArray(); + } +} diff --git a/sgx-jvm/avian/classpath/avian/LegacyObjectInputStream.java b/sgx-jvm/avian/classpath/avian/LegacyObjectInputStream.java new file mode 100644 index 0000000000..8738048dd7 --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/LegacyObjectInputStream.java @@ -0,0 +1,240 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +import avian.VMClass; + +import java.util.HashMap; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PushbackReader; +import java.io.StreamCorruptedException; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +public class LegacyObjectInputStream extends InputStream { + private final InputStream in; + private final PushbackReader r; + + public LegacyObjectInputStream(InputStream in) { + this.in = in; + this.r = new PushbackReader(new InputStreamReader(in)); + } + + public int read() throws IOException { + return in.read(); + } + + public int read(byte[] b, int offset, int length) throws IOException { + return in.read(b, offset, length); + } + + public void close() throws IOException { + in.close(); + } + + public Object readObject() throws IOException, ClassNotFoundException { + return readObject(new HashMap()); + } + + public boolean readBoolean() throws IOException { + read('z'); + return readLongToken() != 0; + } + + public byte readByte() throws IOException { + read('b'); + return (byte) readLongToken(); + } + + public char readChar() throws IOException { + read('c'); + return (char) readLongToken(); + } + + public short readShort() throws IOException { + read('s'); + return (short) readLongToken(); + } + + public int readInt() throws IOException { + read('i'); + return (int) readLongToken(); + } + + public long readLong() throws IOException { + read('j'); + return readLongToken(); + } + + public float readFloat() throws IOException { + read('f'); + return (float) readDoubleToken(); + } + + public double readDouble() throws IOException { + read('d'); + return readDoubleToken(); + } + + public void defaultReadObject() throws IOException { + throw new UnsupportedOperationException(); + } + + private void skipSpace() throws IOException { + int c; + while ((c = r.read()) != -1 && Character.isWhitespace((char) c)); + if (c != -1) { + r.unread(c); + } + } + + private void read(char v) throws IOException { + skipSpace(); + + int c = r.read(); + if (c != v) { + if (c == -1) { + throw new EOFException(); + } else { + throw new StreamCorruptedException(); + } + } + } + + private String readStringToken() throws IOException { + skipSpace(); + + StringBuilder sb = new StringBuilder(); + int c; + while ((c = r.read()) != -1 && ! Character.isWhitespace((char) c) && c != ')') { + sb.append((char) c); + } + if (c != -1) { + r.unread(c); + } + return sb.toString(); + } + + private long readLongToken() throws IOException { + return Long.parseLong(readStringToken()); + } + + private double readDoubleToken() throws IOException { + return Double.parseDouble(readStringToken()); + } + + private Object readObject(HashMap map) + throws IOException, ClassNotFoundException + { + skipSpace(); + switch (r.read()) { + case 'a': + return deserializeArray(map); + case 'l': + return deserializeObject(map); + case 'n': + return null; + case -1: + throw new EOFException(); + default: + throw new StreamCorruptedException(); + } + } + + private Object deserialize(HashMap map) + throws IOException, ClassNotFoundException + { + skipSpace(); + switch (r.read()) { + case 'a': + return deserializeArray(map); + case 'l': + return deserializeObject(map); + case 'r': + return map.get((int) readLongToken()); + case 'n': + return null; + case 'z': + return (readLongToken() != 0); + case 'b': + return (byte) readLongToken(); + case 'c': + return (char) readLongToken(); + case 's': + return (short) readLongToken(); + case 'i': + return (int) readLongToken(); + case 'j': + return readLongToken(); + case 'f': + return (float) readDoubleToken(); + case 'd': + return readDoubleToken(); + case -1: + throw new EOFException(); + default: + throw new StreamCorruptedException(); + } + } + + private Object deserializeArray(HashMap map) + throws IOException, ClassNotFoundException + { + read('('); + int id = (int) readLongToken(); + Class c = Class.forName(readStringToken()); + int length = (int) readLongToken(); + Class t = c.getComponentType(); + Object o = Array.newInstance(t, length); + + map.put(id, o); + + for (int i = 0; i < length; ++i) { + Array.set(o, i, deserialize(map)); + } + + read(')'); + + return o; + } + + private static native Object makeInstance(VMClass c); + + private Object deserializeObject(HashMap map) + throws IOException, ClassNotFoundException + { + read('('); + int id = (int) readLongToken(); + Class c = Class.forName(readStringToken()); + Object o = makeInstance(c.vmClass); + + map.put(id, o); + + for (Field f: c.getAllFields()) { + int modifiers = f.getModifiers(); + if ((modifiers & (Modifier.TRANSIENT | Modifier.STATIC)) == 0) { + try { + f.set(o, deserialize(map)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + read(')'); + + return o; + } +} diff --git a/sgx-jvm/avian/classpath/avian/LegacyObjectOutputStream.java b/sgx-jvm/avian/classpath/avian/LegacyObjectOutputStream.java new file mode 100644 index 0000000000..a55c18230e --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/LegacyObjectOutputStream.java @@ -0,0 +1,211 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +import java.util.IdentityHashMap; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.Serializable; +import java.io.NotSerializableException; + +public class LegacyObjectOutputStream extends OutputStream { + private final PrintStream out; + + public LegacyObjectOutputStream(OutputStream out) { + this.out = new PrintStream(out); + } + + public void write(int c) throws IOException { + out.write(c); + } + + public void write(byte[] b, int offset, int length) throws IOException { + out.write(b, offset, length); + } + + public void flush() throws IOException { + out.flush(); + } + + public void close() throws IOException { + out.close(); + } + + public void writeObject(Object o) throws IOException { + writeObject(o, new IdentityHashMap(), new int[] {0}); + } + + public void writeBoolean(boolean v) { + out.print("z"); + out.print((v ? 1 : 0)); + } + + public void writeByte(byte v) { + out.print("b"); + out.print((int) v); + } + + public void writeChar(char v) { + out.print("c"); + out.print((int) v); + } + + public void writeShort(short v) { + out.print("s"); + out.print((int) v); + } + + public void writeInt(int v) { + out.print("i"); + out.print(v); + } + + public void writeLong(long v) { + out.print("j"); + out.print(v); + } + + public void writeFloat(float v) { + out.print("f"); + out.print(v); + } + + public void writeDouble(double v) { + out.print("d"); + out.print(v); + } + + public void defaultWriteObject() throws IOException { + throw new UnsupportedOperationException(); + } + + private void writeObject(Object o, IdentityHashMap map, + int[] nextId) + throws IOException + { + if (o == null) { + out.print("n"); + } else { + Integer id = map.get(o); + if (id == null) { + map.put(o, nextId[0]); + + Class c = o.getClass(); + if (c.isArray()) { + serializeArray(o, map, nextId); + } else if (Serializable.class.isAssignableFrom(c)) { + serializeObject(o, map, nextId); + } else { + throw new NotSerializableException(c.getName()); + } + } else { + out.print("r"); + out.print(id.intValue()); + } + } + } + + private void serializeArray(Object o, IdentityHashMap map, + int[] nextId) + throws IOException + { + Class c = o.getClass(); + Class t = c.getComponentType(); + int length = Array.getLength(o); + + out.print("a("); + out.print(nextId[0]++); + out.print(" "); + out.print(c.getName()); + out.print(" "); + out.print(length); + + for (int i = 0; i < length; ++i) { + out.print(" "); + Object v = Array.get(o, i); + if (t.equals(boolean.class)) { + writeBoolean((Boolean) v); + } else if (t.equals(byte.class)) { + writeByte((Byte) v); + } else if (t.equals(char.class)) { + writeChar((Character) v); + } else if (t.equals(short.class)) { + writeShort((Short) v); + } else if (t.equals(int.class)) { + writeInt((Integer) v); + } else if (t.equals(long.class)) { + writeLong((Long) v); + } else if (t.equals(float.class)) { + writeFloat((Float) v); + } else if (t.equals(double.class)) { + writeDouble((Double) v); + } else { + writeObject(v, map, nextId); + } + } + + out.print(")"); + } + + private void serializeObject(Object o, IdentityHashMap map, + int[] nextId) + throws IOException + { + Class c = o.getClass(); + + out.print("l("); + out.print(nextId[0]++); + out.print(" "); + out.print(c.getName()); + + for (Field f: c.getAllFields()) { + int modifiers = f.getModifiers(); + if ((modifiers & (Modifier.TRANSIENT | Modifier.STATIC)) == 0) { + out.print(" "); + Object v; + + try { + v = f.get(o); + } catch (Exception e) { + throw new RuntimeException(e); + } + + Class t = f.getType(); + if (t.equals(boolean.class)) { + writeBoolean((Boolean) v); + } else if (t.equals(byte.class)) { + writeByte((Byte) v); + } else if (t.equals(char.class)) { + writeChar((Character) v); + } else if (t.equals(short.class)) { + writeShort((Short) v); + } else if (t.equals(int.class)) { + writeInt((Integer) v); + } else if (t.equals(long.class)) { + writeLong((Long) v); + } else if (t.equals(float.class)) { + writeFloat((Float) v); + } else if (t.equals(double.class)) { + writeDouble((Double) v); + } else { + writeObject(v, map, nextId); + } + } + } + + out.print(")"); + } + +} diff --git a/sgx-jvm/avian/classpath/avian/Machine.java b/sgx-jvm/avian/classpath/avian/Machine.java new file mode 100644 index 0000000000..00c6ea2f15 --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/Machine.java @@ -0,0 +1,67 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +import sun.misc.Unsafe; +import java.lang.reflect.Field; + +public abstract class Machine { + + private static final Unsafe unsafe; + + static { + Unsafe u; + try { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + u = (Unsafe)f.get(null); + } catch (Exception e) { + u = null; + } + unsafe = u; + } + + public static native void dumpHeap(String outputFile); + + public static Unsafe getUnsafe() { + return unsafe; + } + + /** + * Short version: Don't use this function -- it's evil. + * + * Long version: This is kind of a poor man's, cross-platform + * version of Microsoft's Structured Exception Handling. The idea + * is that you can call a native function with the specified + * argument such that any OS signals raised (e.g. SIGSEGV, SIGBUS, + * SIGFPE, EXC_ACCESS_VIOLATION, etc.) prior to the function + * returning are converted into the appropriate Java exception + * (e.g. NullPointerException, ArithmeticException, etc.) and thrown + * from this method. This may be useful in very specific + * circumstances, e.g. to work around a bug in a library that would + * otherwise crash your app. On the other hand, you'd be much + * better off just fixing the library if possible. + * + * Caveats: The specified function should return quickly without + * blocking, since it will block critical VM features such as + * garbage collection. The implementation is equivalent to using + * setjmp/longjmp to achieve a non-local return from the signal + * handler, meaning C++ destructors and other cleanup code might not + * be run if a signal is raised. It might melt your keyboard and + * burn your fingertips. Caveat lector. + * + * @param function a function pointer of type int64_t (*)(int64_t) + * @param argument the argument to pass to the function + * @return the return value of the function + */ + public static native long tryNative(long function, long argument); + +} diff --git a/sgx-jvm/avian/classpath/avian/MethodAddendum.java b/sgx-jvm/avian/classpath/avian/MethodAddendum.java new file mode 100644 index 0000000000..11f6b9ff15 --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/MethodAddendum.java @@ -0,0 +1,17 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +public class MethodAddendum extends Addendum { + public Object exceptionTable; + public Object annotationDefault; + public Object parameterAnnotationTable; +} diff --git a/sgx-jvm/avian/classpath/avian/Pair.java b/sgx-jvm/avian/classpath/avian/Pair.java new file mode 100644 index 0000000000..dff3313542 --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/Pair.java @@ -0,0 +1,5 @@ +package avian; + +abstract class Pair { + // VM-visible fields in types.def +} diff --git a/sgx-jvm/avian/classpath/avian/PersistentSet.java b/sgx-jvm/avian/classpath/avian/PersistentSet.java new file mode 100644 index 0000000000..c257807ade --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/PersistentSet.java @@ -0,0 +1,594 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +import java.util.Comparator; + +public class PersistentSet implements Iterable { + private static final Node NullNode = new Node(null); + + static { + NullNode.left = NullNode; + NullNode.right = NullNode; + } + + private final Node root; + private final Comparator comparator; + private final int size; + + public PersistentSet() { + this(NullNode, new Comparator() { + public int compare(T a, T b) { + return ((Comparable) a).compareTo(b); + } + }, 0); + } + + public PersistentSet(Comparator comparator) { + this(NullNode, comparator, 0); + } + + private PersistentSet(Node root, Comparator comparator, int size) { + this.root = root; + this.comparator = comparator; + this.size = size; + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("{"); + for (java.util.Iterator it = iterator(); it.hasNext();) { + sb.append(it.next()); + if (it.hasNext()) { + sb.append(","); + } + } + sb.append("}"); + return sb.toString(); + } + + public Comparator comparator() { + return comparator; + } + + public PersistentSet add(T value) { + return add(value, false); + } + + public int size() { + return size; + } + + public PersistentSet add(T value, boolean replaceExisting) { + Path p = find(value); + if (! p.fresh) { + if (replaceExisting) { + return p.replaceWith(value); + } else { + return this; + } + } + + return add(p); + } + + private PersistentSet add(Path p) { + if (! p.fresh) throw new IllegalArgumentException(); + + Node new_ = p.node; + Node newRoot = p.root.root; + Cell> ancestors = p.ancestors; + + // rebalance + new_.red = true; + while (ancestors != null && ancestors.value.red) { + if (ancestors.value == ancestors.next.value.left) { + if (ancestors.next.value.right.red) { + ancestors.value.red = false; + ancestors.next.value.right = new Node(ancestors.next.value.right); + ancestors.next.value.right.red = false; + ancestors.next.value.red = true; + new_ = ancestors.next.value; + ancestors = ancestors.next.next; + } else { + if (new_ == ancestors.value.right) { + new_ = ancestors.value; + ancestors = ancestors.next; + + Node n = leftRotate(new_); + if (ancestors.value.right == new_) { + ancestors.value.right = n; + } else { + ancestors.value.left = n; + } + ancestors = new Cell(n, ancestors); + } + ancestors.value.red = false; + ancestors.next.value.red = true; + + Node n = rightRotate(ancestors.next.value); + if (ancestors.next.next == null) { + newRoot = n; + } else if (ancestors.next.next.value.right == ancestors.next.value) { + ancestors.next.next.value.right = n; + } else { + ancestors.next.next.value.left = n; + } + // done + } + } else { + if (ancestors.next.value.left.red) { + ancestors.value.red = false; + ancestors.next.value.left = new Node(ancestors.next.value.left); + ancestors.next.value.left.red = false; + ancestors.next.value.red = true; + new_ = ancestors.next.value; + ancestors = ancestors.next.next; + } else { + if (new_ == ancestors.value.left) { + new_ = ancestors.value; + ancestors = ancestors.next; + + Node n = rightRotate(new_); + if (ancestors.value.right == new_) { + ancestors.value.right = n; + } else { + ancestors.value.left = n; + } + ancestors = new Cell(n, ancestors); + } + ancestors.value.red = false; + ancestors.next.value.red = true; + + Node n = leftRotate(ancestors.next.value); + if (ancestors.next.next == null) { + newRoot = n; + } else if (ancestors.next.next.value.right == ancestors.next.value) { + ancestors.next.next.value.right = n; + } else { + ancestors.next.next.value.left = n; + } + // done + } + } + } + + newRoot.red = false; + + return new PersistentSet(newRoot, comparator, size + 1); + } + + private static Node leftRotate(Node n) { + Node child = new Node(n.right); + n.right = child.left; + child.left = n; + return child; + } + + private static Node rightRotate(Node n) { + Node child = new Node(n.left); + n.left = child.right; + child.right = n; + return child; + } + + public PersistentSet remove(T value) { + Path p = find(value); + if (! p.fresh) { + return remove(p); + } + + return this; + } + + private PersistentSet remove(Path p) { + if (size == 1) { + if (p.node != root) { + throw new IllegalArgumentException(); + } + return new PersistentSet(NullNode, comparator, 0); + } + + Node new_ = p.node; + Node newRoot = p.root.root; + Cell> ancestors = p.ancestors; + + Node dead; + if (new_.left == NullNode || new_.right == NullNode) { + dead = new_; + } else { + Cell> path = successor(new_, ancestors); + dead = path.value; + ancestors = path.next; + } + + Node child; + if (dead.left != NullNode) { + child = new Node(dead.left); + } else if (dead.right != NullNode) { + child = new Node(dead.right); + } else { + child = NullNode; + } + + if (ancestors == null) { + child.red = false; + return new PersistentSet(child, comparator, 1); + } else if (dead == ancestors.value.left) { + ancestors.value.left = child; + } else { + ancestors.value.right = child; + } + + if (dead != new_) { + new_.value = dead.value; + } + + if (! dead.red) { + // rebalance + while (ancestors != null && ! child.red) { + if (child == ancestors.value.left) { + Node sibling = ancestors.value.right + = new Node(ancestors.value.right); + if (sibling.red) { + sibling.red = false; + ancestors.value.red = true; + + Node n = leftRotate(ancestors.value); + if (ancestors.next == null) { + newRoot = n; + } else if (ancestors.next.value.right == ancestors.value) { + ancestors.next.value.right = n; + } else { + ancestors.next.value.left = n; + } + ancestors.next = new Cell(n, ancestors.next); + + sibling = ancestors.value.right = new Node(ancestors.value.right); + } + + if (! (sibling.left.red || sibling.right.red)) { + sibling.red = true; + child = ancestors.value; + ancestors = ancestors.next; + } else { + if (! sibling.right.red) { + sibling.left = new Node(sibling.left); + sibling.left.red = false; + + sibling.red = true; + sibling = ancestors.value.right = rightRotate(sibling); + } + + sibling.red = ancestors.value.red; + ancestors.value.red = false; + + sibling.right = new Node(sibling.right); + sibling.right.red = false; + + Node n = leftRotate(ancestors.value); + if (ancestors.next == null) { + newRoot = n; + } else if (ancestors.next.value.right == ancestors.value) { + ancestors.next.value.right = n; + } else { + ancestors.next.value.left = n; + } + + child = newRoot; + ancestors = null; + } + } else { + Node sibling = ancestors.value.left + = new Node(ancestors.value.left); + if (sibling.red) { + sibling.red = false; + ancestors.value.red = true; + + Node n = rightRotate(ancestors.value); + if (ancestors.next == null) { + newRoot = n; + } else if (ancestors.next.value.left == ancestors.value) { + ancestors.next.value.left = n; + } else { + ancestors.next.value.right = n; + } + ancestors.next = new Cell(n, ancestors.next); + + sibling = ancestors.value.left = new Node(ancestors.value.left); + } + + if (! (sibling.right.red || sibling.left.red)) { + sibling.red = true; + child = ancestors.value; + ancestors = ancestors.next; + } else { + if (! sibling.left.red) { + sibling.right = new Node(sibling.right); + sibling.right.red = false; + + sibling.red = true; + sibling = ancestors.value.left = leftRotate(sibling); + } + + sibling.red = ancestors.value.red; + ancestors.value.red = false; + + sibling.left = new Node(sibling.left); + sibling.left.red = false; + + Node n = rightRotate(ancestors.value); + if (ancestors.next == null) { + newRoot = n; + } else if (ancestors.next.value.left == ancestors.value) { + ancestors.next.value.left = n; + } else { + ancestors.next.value.right = n; + } + + child = newRoot; + ancestors = null; + } + } + } + + child.red = false; + } + + return new PersistentSet(newRoot, comparator, size - 1); + } + + private static Cell> minimum(Node n, + Cell> ancestors) + { + while (n.left != NullNode) { + n.left = new Node(n.left); + ancestors = new Cell(n, ancestors); + n = n.left; + } + + return new Cell(n, ancestors); + } + + private static Cell> maximum(Node n, + Cell> ancestors) + { + while (n.right != NullNode) { + n.right = new Node(n.right); + ancestors = new Cell(n, ancestors); + n = n.right; + } + + return new Cell(n, ancestors); + } + + private static Cell> successor(Node n, + Cell> ancestors) + { + if (n.right != NullNode) { + n.right = new Node(n.right); + return minimum(n.right, new Cell(n, ancestors)); + } + + while (ancestors != null && n == ancestors.value.right) { + n = ancestors.value; + ancestors = ancestors.next; + } + + return ancestors; + } + + private static Cell> predecessor(Node n, + Cell> ancestors) + { + if (n.left != NullNode) { + n.left = new Node(n.left); + return maximum(n.left, new Cell(n, ancestors)); + } + + while (ancestors != null && n == ancestors.value.left) { + n = ancestors.value; + ancestors = ancestors.next; + } + + return ancestors; + } + + public Path find(T value) { + Node newRoot = new Node(root); + Cell> ancestors = null; + + Node old = root; + Node new_ = newRoot; + while (old != NullNode) { + ancestors = new Cell(new_, ancestors); + + int difference = comparator.compare(value, old.value); + if (difference < 0) { + old = old.left; + new_ = new_.left = new Node(old); + } else if (difference > 0) { + old = old.right; + new_ = new_.right = new Node(old); + } else { + return new Path(false, new_, + new PersistentSet(newRoot, comparator, size), + ancestors.next); + } + } + + new_.value = value; + return new Path(true, new_, + new PersistentSet(newRoot, comparator, size), + ancestors); + } + + public Path first() { + if (root == NullNode) return null; + + Node newRoot = new Node(root); + Cell> ancestors = null; + + Node old = root; + Node new_ = newRoot; + while (old.left != NullNode) { + ancestors = new Cell(new_, ancestors); + + old = old.left; + new_ = new_.left = new Node(old); + } + + return new Path(true, new_, + new PersistentSet(newRoot, comparator, size), + ancestors); + } + + public Path last() { + if (root == NullNode) return null; + + Node newRoot = new Node(root); + Cell> ancestors = null; + + Node old = root; + Node new_ = newRoot; + while (old.right != NullNode) { + ancestors = new Cell(new_, ancestors); + + old = old.right; + new_ = new_.right = new Node(old); + } + + return new Path(true, new_, + new PersistentSet(newRoot, comparator, size), + ancestors); + } + + public java.util.Iterator iterator() { + return new Iterator(first()); + } + + private Path successor(Path p) { + Cell> s = successor(p.node, p.ancestors); + if (s == null) { + return null; + } else { + return new Path(false, s.value, p.root, s.next); + } + } + + private Path predecessor(Path p) { + Cell> s = predecessor(p.node, p.ancestors); + if (s == null) { + return null; + } else { + return new Path(false, s.value, p.root, s.next); + } + } + + private static class Node { + public T value; + public Node left; + public Node right; + public boolean red; + + public Node(Node basis) { + if (basis != null) { + value = basis.value; + left = basis.left; + right = basis.right; + red = basis.red; + } + } + } + + public static class Path { + private final boolean fresh; + private final Node node; + private final PersistentSet root; + private final Cell> ancestors; + + public Path(boolean fresh, Node node, PersistentSet root, + Cell> ancestors) + { + this.fresh = fresh; + this.node = node; + this.root = root; + this.ancestors = ancestors; + } + + public T value() { + return node.value; + } + + public boolean fresh() { + return fresh; + } + + public PersistentSet root() { + return root; + } + + public Path successor() { + return root.successor(this); + } + + public Path predecessor() { + return root.predecessor(this); + } + + public PersistentSet remove() { + if (fresh) throw new IllegalStateException(); + + return root.remove(this); + } + + public PersistentSet add() { + if (! fresh) throw new IllegalStateException(); + + return root.add(this); + } + + public PersistentSet replaceWith(T value) { + if (fresh) throw new IllegalStateException(); + if (root.comparator.compare(node.value, value) != 0) + throw new IllegalArgumentException(); + + node.value = value; + return root; + } + } + + public class Iterator implements java.util.Iterator { + private PersistentSet.Path path; + + private Iterator(PersistentSet.Path path) { + this.path = path; + } + + private Iterator(Iterator start) { + path = start.path; + } + + public boolean hasNext() { + return path != null; + } + + public T next() { + PersistentSet.Path p = path; + path = path.successor(); + return p.value(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + } +} diff --git a/sgx-jvm/avian/classpath/avian/Singleton.java b/sgx-jvm/avian/classpath/avian/Singleton.java new file mode 100644 index 0000000000..c866169b6e --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/Singleton.java @@ -0,0 +1,19 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +public abstract class Singleton { + public static native int getInt(Object singleton, int offset); + public static native long getLong(Object singleton, int offset); + public static native Object getObject(Object singleton, int offset); + + // Fields in types.def +} diff --git a/sgx-jvm/avian/classpath/avian/Stream.java b/sgx-jvm/avian/classpath/avian/Stream.java new file mode 100644 index 0000000000..eb2859d432 --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/Stream.java @@ -0,0 +1,80 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.io.EOFException; + +public abstract class Stream { + public static void write1(OutputStream out, int v) throws IOException { + out.write(v & 0xFF); + } + + public static int read1(InputStream in) throws IOException { + return in.read(); + } + + public static void write2(OutputStream out, int v) throws IOException { + out.write((v >>> 8) & 0xFF); + out.write((v ) & 0xFF); + } + + public static int read2(InputStream in) throws IOException { + int b1 = in.read(); + int b2 = in.read(); + if (b2 == -1) throw new EOFException(); + return ((b1 << 8) | (b2 & 0xFF)); + } + + public static void write4(OutputStream out, int v) throws IOException { + out.write((v >>> 24) & 0xFF); + out.write((v >>> 16) & 0xFF); + out.write((v >>> 8) & 0xFF); + out.write((v ) & 0xFF); + } + + public static int read4(InputStream in) throws IOException { + int b1 = in.read(); + int b2 = in.read(); + int b3 = in.read(); + int b4 = in.read(); + if (b4 == -1) throw new EOFException(); + return ((b1 << 24) | (b2 << 16) | (b3 << 8) | (b4)); + } + + public static void write8(OutputStream out, long v) throws IOException { + write4(out, (int) (v >>> 32) & 0xFFFFFFFF); + write4(out, (int) (v ) & 0xFFFFFFFF); + } + + public static long read8(InputStream in) throws IOException { + long b1 = in.read(); + long b2 = in.read(); + long b3 = in.read(); + long b4 = in.read(); + long b5 = in.read(); + long b6 = in.read(); + long b7 = in.read(); + long b8 = in.read(); + if (b8 == -1) throw new EOFException(); + return ((b1 << 56) | (b2 << 48) | (b3 << 40) | (b4 << 32) | + (b5 << 24) | (b6 << 16) | (b7 << 8) | (b8)); + } + + public static void set4(byte[] array, int offset, int v) { + array[offset ] = (byte) ((v >>> 24) & 0xFF); + array[offset + 1] = (byte) ((v >>> 16) & 0xFF); + array[offset + 2] = (byte) ((v >>> 8) & 0xFF); + array[offset + 3] = (byte) ((v ) & 0xFF); + } +} diff --git a/sgx-jvm/avian/classpath/avian/SystemClassLoader.java b/sgx-jvm/avian/classpath/avian/SystemClassLoader.java new file mode 100644 index 0000000000..5dd2ee4352 --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/SystemClassLoader.java @@ -0,0 +1,176 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +import java.net.URL; +import java.net.MalformedURLException; +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.ArrayList; +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; + + protected Class findClass(String name) throws ClassNotFoundException { + return getClass(findVMClass(name)); + } + + public static native Class getClass(VMClass vmClass); + + public static native VMClass vmClass(Class jClass); + + private native VMClass findLoadedVMClass(String name); + + protected Class reallyFindLoadedClass(String name){ + VMClass c = findLoadedVMClass(name); + return c == null ? null : getClass(c); + } + + protected Class loadClass(String name, boolean resolve) + throws ClassNotFoundException + { + Class c = findLoadedClass(name); + if (c == null) { + ClassLoader parent = getParent(); + if (parent != null) { + try { + c = parent.loadClass(name); + } catch (ClassNotFoundException ok) { } + } + + if (c == null) { + c = findClass(name); + } + } + + if (resolve) { + resolveClass(c); + } + + return c; + } + + private native String resourceURLPrefix(String name); + + protected URL findResource(String name) { + String prefix = resourceURLPrefix(name); + if (prefix != null) { + try { + return new URL(prefix + name); + } catch (MalformedURLException ignored) { } + } + return null; + } + + protected Package getPackage(String name) { + Package p = super.getPackage(name); + if (p == null) { + String source = getPackageSource(name); + if (source != null) { + // todo: load attributes from JAR manifest + definePackage(name, null, null, null, null, null, null, null); + } else { + definePackage(name, null, null, null, null, null, null, null); + } + } + + return super.getPackage(name); + } + + protected static native String getPackageSource(String name); + + // OpenJDK's java.lang.ClassLoader.getResource makes use of + // sun.misc.Launcher to load bootstrap resources, which is not + // appropriate for the Avian build, so we override it to ensure we + // get the behavior we want. This implementation is the same as + // that of Avian's java.lang.ClassLoader.getResource. + public URL getResource(String path) { + URL url = null; + ClassLoader parent = getParent(); + if (parent != null) { + url = parent.getResource(path); + } + + if (url == null) { + url = findResource(path); + } + + return url; + } + + // As above, we override this method to avoid inappropriate behavior + // in OpenJDK's java.lang.ClassLoader.getResources. + public Enumeration getResources(String name) throws IOException { + Collection urls = new ArrayList(5); + + ClassLoader parent = getParent(); + if (parent != null) { + for (Enumeration e = parent.getResources(name); + e.hasMoreElements();) + { + urls.add(e.nextElement()); + } + } + + Enumeration urls2 = findResources(name); + while (urls2.hasMoreElements()) { + urls.add(urls2.nextElement()); + } + + return Collections.enumeration(urls); + } + + private class ResourceEnumeration implements Enumeration { + private long[] finderElementPtrPtr; + private String name, urlPrefix; + + public ResourceEnumeration(String name) { + this.name = name; + finderElementPtrPtr = new long[1]; + urlPrefix = nextResourceURLPrefix(); + } + + private native String nextResourceURLPrefix(SystemClassLoader loader, + String name, long[] finderElementPtrPtr); + + private String nextResourceURLPrefix() { + return nextResourceURLPrefix(SystemClassLoader.this, name, + finderElementPtrPtr); + } + + public boolean hasMoreElements() { + return urlPrefix != null; + } + + public URL nextElement() { + if (urlPrefix == null) throw new NoSuchElementException(); + URL result; + try { + result = new URL(urlPrefix + name); + } catch (MalformedURLException ignored) { + result = null; + } + if (finderElementPtrPtr[0] == 0l) urlPrefix = null; + else urlPrefix = nextResourceURLPrefix(); + return result; + } + } + + protected Enumeration findResources(String name) { + return new ResourceEnumeration(name); + } +} diff --git a/sgx-jvm/avian/classpath/avian/Traces.java b/sgx-jvm/avian/classpath/avian/Traces.java new file mode 100644 index 0000000000..e9c106c7c9 --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/Traces.java @@ -0,0 +1,71 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; + +public class Traces { + private static final String Newline = System.getProperty("line.separator"); + + private static String traceAllThreads() { + StringBuilder buffer = new StringBuilder(); + + Thread[] threads = new Thread[Thread.activeCount()]; + + int count = Thread.enumerate(threads); + for (int i = 0; i < count; ++i) { + traceThread(threads[i], buffer); + } + + return buffer.toString(); + } + + private static String traceThread(Thread thread) { + StringBuilder buffer = new StringBuilder(); + + traceThread(thread, buffer); + + return buffer.toString(); + } + + private static void traceThread(Thread thread, StringBuilder buffer) { + buffer.append(thread).append(Newline); + for (StackTraceElement e: thread.getStackTrace()) { + buffer.append("\tat ").append(e).append(Newline); + } + } + + public static void startTraceListener(final String host, final int port) { + Thread t = new Thread(new Runnable() { + public void run() { + try { + ServerSocketChannel server = ServerSocketChannel.open(); + server.socket().bind(new InetSocketAddress(host, port)); + while (true) { + SocketChannel c = server.accept(); + try { + c.write(ByteBuffer.wrap(traceAllThreads().getBytes())); + } finally { + c.close(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + t.setDaemon(true); + t.start(); + } +} diff --git a/sgx-jvm/avian/classpath/avian/Utf8.java b/sgx-jvm/avian/classpath/avian/Utf8.java new file mode 100644 index 0000000000..2c32fec9de --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/Utf8.java @@ -0,0 +1,118 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +import java.io.ByteArrayOutputStream; + +public class Utf8 { + public static boolean test(Object data) { + if (!(data instanceof byte[])) return false; + byte[] b = (byte[])data; + for (int i = 0; i < b.length; ++i) { + if (((int)b[i] & 0x080) != 0) return true; + } + return false; + } + + public static byte[] encode(char[] s16, int offset, int length) { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + for (int i = offset; i < offset+length; ++i) { + char c = s16[i]; + if (c == '\u0000') { // null char + buf.write(0); + buf.write(0); + } else if (c < 0x080) { // 1 byte char + buf.write(c); + } else if (c < 0x0800) { // 2 byte char + buf.write(0x0c0 | (c >>> 6)); + buf.write(0x080 | (c & 0x03f)); + } else { // 3 byte char + buf.write(0x0e0 | ((c >>> 12) & 0x0f)); + buf.write(0x080 | ((c >>> 6) & 0x03f)); + buf.write(0x080 | (c & 0x03f)); + } + } + return buf.toByteArray(); + } + + public static Object decode(byte[] s8, int offset, int length) { + Object buf = new byte[length]; + boolean isMultiByte = false; + int i=offset, j=0; + while (i < offset+length) { + int x = s8[i++]; + if ((x & 0x080) == 0x0) { // 1 byte char + if (x == 0) { // 2 byte null char + if (i == offset + length) { + return null; + } + ++ i; + } + cram(buf, j++, x); + } else if ((x & 0x0e0) == 0x0c0) { // 2 byte char + if (i == offset + length) { + return null; + } + + if (!isMultiByte) { + buf = widen(buf, j, length-1); + isMultiByte = true; + } + int y = s8[i++]; + cram(buf, j++, ((x & 0x1f) << 6) | (y & 0x3f)); + } else if ((x & 0x0f0) == 0x0e0) { // 3 byte char + if (i + 1 >= offset + length) { + return null; + } + + if (!isMultiByte) { + buf = widen(buf, j, length-2); + isMultiByte = true; + } + int y = s8[i++]; int z = s8[i++]; + cram(buf, j++, ((x & 0xf) << 12) | ((y & 0x3f) << 6) | (z & 0x3f)); + } + } + + return trim(buf, j); + } + + public static char[] decode16(byte[] s8, int offset, int length) { + Object decoded = decode(s8, offset, length); + if (decoded == null) { + return null; + } else if (decoded instanceof char[]) { + return (char[])decoded; + } else { + return (char[])widen(decoded, length, length); + } + } + + private static void cram(Object data, int index, int val) { + if (data instanceof byte[]) ((byte[])data)[index] = (byte)val; + else ((char[])data)[index] = (char)val; + } + + private static Object widen(Object data, int length, int capacity) { + byte[] src = (byte[])data; + char[] result = new char[capacity]; + for (int i = 0; i < length; ++i) result[i] = (char)((int)src[i] & 0x0ff); + return result; + } + + private static Object trim(Object data, int length) { + if (data instanceof byte[]) return data; + if (((char[])data).length == length) return data; + char[] result = new char[length]; + System.arraycopy(data, 0, result, 0, length); + return result; + } +} diff --git a/sgx-jvm/avian/classpath/avian/VMClass.java b/sgx-jvm/avian/classpath/avian/VMClass.java new file mode 100644 index 0000000000..8960670775 --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/VMClass.java @@ -0,0 +1,42 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +public class VMClass { + public short flags; + public short vmFlags; + public short fixedSize; + public byte arrayElementSize; + public byte arrayDimensions; + public VMClass arrayElementClass; + public int runtimeDataIndex; + public int[] objectMask; + public byte[] name; + public byte[] sourceFile; + public VMClass super_; + public Object[] interfaceTable; + public VMMethod[] virtualTable; + public VMField[] fieldTable; + /** + * Methods declared in this class, plus any abstract virtual methods + * inherited from implemented or extended interfaces. If addendum + * is non-null and addendum.declaredMethodCount is non-negative, + * then the first addendum.declaredMethodCount methods are declared + * methods, while the rest are abstract virtual methods. If + * addendum is null or addendum.declaredMethodCount is negative, all + * are declared methods. + */ + public VMMethod[] methodTable; + public ClassAddendum addendum; + public Singleton staticTable; + public ClassLoader loader; + public byte[] source; +} diff --git a/sgx-jvm/avian/classpath/avian/VMField.java b/sgx-jvm/avian/classpath/avian/VMField.java new file mode 100644 index 0000000000..2f2715d121 --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/VMField.java @@ -0,0 +1,23 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +public class VMField { + public byte vmFlags; + public byte code; + public short flags; + public short offset; + public int nativeID; + public byte[] name; + public byte[] spec; + public FieldAddendum addendum; + public VMClass class_; +} diff --git a/sgx-jvm/avian/classpath/avian/VMMethod.java b/sgx-jvm/avian/classpath/avian/VMMethod.java new file mode 100644 index 0000000000..53af6e9c80 --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/VMMethod.java @@ -0,0 +1,31 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +public class VMMethod { + public byte vmFlags; + public byte returnCode; + public byte parameterCount; + public byte parameterFootprint; + public short flags; + public short offset; + public int nativeID; + public int runtimeDataIndex; + public byte[] name; + public byte[] spec; + public MethodAddendum addendum; + public VMClass class_; + public Code code; + + public boolean hasAnnotations() { + return addendum != null && addendum.annotationTable != null; + } +} diff --git a/sgx-jvm/avian/classpath/avian/avianvmresource/Handler.java b/sgx-jvm/avian/classpath/avian/avianvmresource/Handler.java new file mode 100644 index 0000000000..2e25a6bafc --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/avianvmresource/Handler.java @@ -0,0 +1,111 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian.avianvmresource; + +import java.net.URL; +import java.net.URLStreamHandler; +import java.net.URLConnection; +import java.io.IOException; +import java.io.FileNotFoundException; +import java.io.InputStream; + +public class Handler extends URLStreamHandler { + protected URLConnection openConnection(URL url) { + return new ResourceConnection(url); + } + + private static class ResourceConnection extends URLConnection { + public ResourceConnection(URL url) { + super(url); + } + + public int getContentLength() { + return ResourceInputStream.getContentLength(url.getFile()); + } + + public InputStream getInputStream() throws IOException { + return new ResourceInputStream(url.getFile()); + } + + public void connect() { + // ignore + } + } + + private static class ResourceInputStream extends InputStream { + private long peer; + private int position; + + public ResourceInputStream(String path) throws IOException { + peer = open(path); + if (peer == 0) { + throw new FileNotFoundException(path); + } + } + + private static native int getContentLength(String path); + + private static native long open(String path) throws IOException; + + private static native int read(long peer, int position) throws IOException; + + private static native int read(long peer, int position, + byte[] b, int offset, int length) + throws IOException; + + public static native void close(long peer) throws IOException; + + public static native int available(long peer, int position); + + public int available() { + return available(peer, position); + } + + public int read() throws IOException { + if (peer != 0) { + int c = read(peer, position); + if (c >= 0) { + ++ position; + } + return c; + } else { + throw new IOException(); + } + } + + public int read(byte[] b, int offset, int length) throws IOException { + if (peer != 0) { + if (b == null) { + throw new NullPointerException(); + } + + if (offset < 0 || offset + length > b.length) { + throw new ArrayIndexOutOfBoundsException(); + } + + int c = read(peer, position, b, offset, length); + if (c >= 0) { + position += c; + } + return c; + } else { + throw new IOException(); + } + } + + public void close() throws IOException { + if (peer != 0) { + close(peer); + peer = 0; + } + } + } +} diff --git a/sgx-jvm/avian/classpath/avian/file/Handler.java b/sgx-jvm/avian/classpath/avian/file/Handler.java new file mode 100644 index 0000000000..2ac476ff65 --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/file/Handler.java @@ -0,0 +1,44 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian.file; + +import java.net.URL; +import java.net.URLStreamHandler; +import java.net.URLConnection; +import java.io.IOException; +import java.io.FileNotFoundException; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; + +public class Handler extends URLStreamHandler { + protected URLConnection openConnection(URL url) { + return new FileURLConnection(url); + } + + private static class FileURLConnection extends URLConnection { + public FileURLConnection(URL url) { + super(url); + } + + public int getContentLength() { + return (int) new File(url.getFile()).length(); + } + + public InputStream getInputStream() throws IOException { + return new FileInputStream(new File(url.getFile())); + } + + public void connect() { + // ignore + } + } +} diff --git a/sgx-jvm/avian/classpath/avian/http/Handler.java b/sgx-jvm/avian/classpath/avian/http/Handler.java new file mode 100644 index 0000000000..28f36d4194 --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/http/Handler.java @@ -0,0 +1,120 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian.http; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.Socket; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; +import java.util.HashMap; +import java.util.Map; + +public class Handler extends URLStreamHandler +{ + public URLConnection openConnection(URL url) throws IOException + { + return new HttpURLConnection(url); + } + + class HttpURLConnection extends URLConnection + { + Socket socket; + private BufferedWriter writer; + private InputStream bin; + private Map header = new HashMap(); + private int status; + + protected HttpURLConnection(URL url) + { + super(url); + } + + @Override + public void connect() throws IOException + { + if(socket == null) + { + URLConnection con = null; + String host = url.getHost(); + int port =url.getPort(); + if(port < 0) port = 80; + socket = new Socket(host, port); + OutputStream out = socket.getOutputStream(); + writer = new BufferedWriter(new OutputStreamWriter(out)); + writer.write("GET " + url.getPath() + " HTTP/1.1"); + writer.write("\r\nHost: " + host); + writer.write("\r\n\r\n"); + writer.flush(); + bin = new BufferedInputStream(socket.getInputStream()); + readHeader(); +// System.out.println("Status: " + status); +// System.out.println("Headers: " + header); + } + } + + private void readHeader() throws IOException + { + byte[] buf = new byte[8192]; + int b = 0; + int index = 0; + while(b >= 0) + { + if(index >= 4 && buf[index-4] == '\r' && buf[index-3] == '\n' && buf[index-2] == '\r' && buf[index-1] == '\n') + { + break; + } + b = bin.read(); + buf[index] = (byte) b; + index++; + if(index >= buf.length) + { + throw new IOException("Header exceeded maximum size of 8k."); + } + } + BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, index))); + String line = reader.readLine(); + int x = line.indexOf(' '); + status = Integer.parseInt(line.substring(x + 1 , line.indexOf(' ', x+1))); + while(line != null) + { + int i = line.indexOf(':'); + if(i > 0) + { + header.put(line.substring(0, i), line.substring(i + 1) .trim()); + } + line = reader.readLine(); + } + reader.close(); + } + + @Override + public InputStream getInputStream() throws IOException + { + connect(); + return bin; + } + + @Override + public OutputStream getOutputStream() throws IOException + { + throw new UnsupportedOperationException("Can' write to HTTP Connection"); + } + } +} diff --git a/sgx-jvm/avian/classpath/avian/jar/Handler.java b/sgx-jvm/avian/classpath/avian/jar/Handler.java new file mode 100644 index 0000000000..b4a24de288 --- /dev/null +++ b/sgx-jvm/avian/classpath/avian/jar/Handler.java @@ -0,0 +1,84 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian.jar; + +import java.net.URL; +import java.net.MalformedURLException; +import java.net.URLStreamHandler; +import java.net.JarURLConnection; +import java.net.URLConnection; +import java.io.IOException; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.jar.JarFile; +import java.util.jar.JarEntry; + +public class Handler extends URLStreamHandler { + protected URLConnection openConnection(URL url) { + return new MyJarURLConnection(url); + } + + protected void parseURL(URL url, String s, int start, int end) + throws MalformedURLException + { + // skip "jar:" + s = s.toString().substring(4); + int index = s.indexOf("!/"); + if (index < 0) { + throw new MalformedURLException(); + } + + URL file = new URL(s.substring(0, index)); + if (! "file".equals(file.getProtocol())) { + throw new RuntimeException + ("protocol " + file.getProtocol() + " not yet supported"); + } + + url.set("jar", null, -1, s, null); + } + + private static class MyJarURLConnection extends JarURLConnection { + private final JarFile file; + private final JarEntry entry; + + public MyJarURLConnection(URL url) { + super(url); + + String s = url.getFile(); + int index = s.indexOf("!/"); + + try { + this.file = new JarFile(new URL(s.substring(0, index)).getFile()); + this.entry = this.file.getJarEntry(s.substring(index + 2)); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public JarFile getJarFile() throws IOException { + return file; + } + + public int getContentLength() { + return (int)entry.getSize(); + } + + public InputStream getInputStream() throws IOException { + return file.getInputStream(entry); + } + + public void connect() { + // ignore + } + } +} diff --git a/sgx-jvm/avian/classpath/dalvik/system/BaseDexClassLoader.java b/sgx-jvm/avian/classpath/dalvik/system/BaseDexClassLoader.java new file mode 100644 index 0000000000..27049cea21 --- /dev/null +++ b/sgx-jvm/avian/classpath/dalvik/system/BaseDexClassLoader.java @@ -0,0 +1,5 @@ +package dalvik.system; + +public class BaseDexClassLoader extends ClassLoader { + public native String getLdLibraryPath(); +} diff --git a/sgx-jvm/avian/classpath/java-io.cpp b/sgx-jvm/avian/classpath/java-io.cpp new file mode 100644 index 0000000000..2ca5ee04d1 --- /dev/null +++ b/sgx-jvm/avian/classpath/java-io.cpp @@ -0,0 +1,1015 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include +#include +#include +#include +#include +#include + +#include "jni.h" +#include "jni-util.h" + +#ifdef PLATFORM_WINDOWS + +#define UNICODE + +#include +#include +#include +#include + +#define ACCESS _waccess +#define CLOSE _close +#define READ _read +#define WRITE _write +#define STAT _wstat +#define STRUCT_STAT struct _stat +#define MKDIR(path, mode) _wmkdir(path) +#define CHMOD(path, mode) _wchmod(path, mode) +#define REMOVE _wremove +#define RENAME _wrename +#define OPEN_MASK O_BINARY + +#define CHECK_X_OK R_OK + +#ifdef _MSC_VER +#define S_ISREG(x) ((x)&_S_IFREG) +#define S_ISDIR(x) ((x)&_S_IFDIR) +#define S_IRUSR _S_IREAD +#define S_IWUSR _S_IWRITE +#define W_OK 2 +#define R_OK 4 +#else +#define OPEN _wopen +#endif + +#define GET_CHARS GetStringChars +#define RELEASE_CHARS(path, chars) \ + ReleaseStringChars(path, reinterpret_cast(chars)) + +typedef wchar_t char_t; + +#if defined(WINAPI_FAMILY) +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + +#include "avian-interop.h" +#define SKIP_OPERATOR_NEW + +#endif +#endif + +#else // not PLATFORM_WINDOWS + +#include +#include +#include "sys/mman.h" + +#define ACCESS access +#define OPEN open +#define CLOSE close +#define READ read +#define WRITE write +#define STAT stat +#define STRUCT_STAT struct stat +#define MKDIR mkdir +#define CHMOD chmod +#define REMOVE remove +#define RENAME rename +#define OPEN_MASK 0 + +#define CHECK_X_OK X_OK + +#define GET_CHARS GetStringUTFChars +#define RELEASE_CHARS ReleaseStringUTFChars + +typedef char char_t; + +#endif // not PLATFORM_WINDOWS + +#ifndef WINAPI_FAMILY +#ifndef WINAPI_PARTITION_DESKTOP +#define WINAPI_PARTITION_DESKTOP 1 +#endif + +#ifndef WINAPI_FAMILY_PARTITION +#define WINAPI_FAMILY_PARTITION(x) (x) +#endif +#endif // WINAPI_FAMILY + +#if !defined(SKIP_OPERATOR_NEW) +inline void* operator new(size_t, void* p) throw() +{ + return p; +} +#endif + +typedef const char_t* string_t; + +namespace { + +#ifdef _MSC_VER +inline int OPEN(string_t path, int mask, int mode) +{ + int fd; + if (_wsopen_s(&fd, path, mask, _SH_DENYNO, mode) == 0) { + return fd; + } else { + return -1; + } +} +#endif + +inline bool exists(string_t path) +{ +#ifdef PLATFORM_WINDOWS + return GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES; +#else + STRUCT_STAT s; + return STAT(path, &s) == 0; +#endif +} + +inline int doOpen(JNIEnv* e, string_t path, int mask) +{ + int fd = OPEN(path, mask | OPEN_MASK, S_IRUSR | S_IWUSR); + if (fd == -1) { + if (errno == ENOENT) { + throwNewErrno(e, "java/io/FileNotFoundException"); + } else { + throwNewErrno(e, "java/io/IOException"); + } + } + return fd; +} + +inline void doClose(JNIEnv* e, jint fd) +{ + int r = CLOSE(fd); + if (r == -1) { + throwNewErrno(e, "java/io/IOException"); + } +} + +inline int doRead(JNIEnv* e, jint fd, jbyte* data, jint length) +{ + int r = READ(fd, data, length); + if (r > 0) { + return r; + } else if (r == 0) { + return -1; + } else { + throwNewErrno(e, "java/io/IOException"); + return 0; + } +} + +inline void doWrite(JNIEnv* e, jint fd, const jbyte* data, jint length) +{ + int r = WRITE(fd, data, length); + if (r != length) { + throwNewErrno(e, "java/io/IOException"); + } +} + +#ifdef PLATFORM_WINDOWS + +class Directory { + public: + Directory() : handle(0), findNext(false) + { + } + + virtual string_t next() + { + if (handle and handle != INVALID_HANDLE_VALUE) { + if (findNext) { + if (FindNextFileW(handle, &data)) { + return data.cFileName; + } + } else { + findNext = true; + return data.cFileName; + } + } + return 0; + } + + virtual void dispose() + { + if (handle and handle != INVALID_HANDLE_VALUE) { + FindClose(handle); + } + free(this); + } + + HANDLE handle; + WIN32_FIND_DATAW data; + bool findNext; +}; + +#else // not PLATFORM_WINDOWS + +#endif // not PLATFORM_WINDOWS + +} // namespace + +static inline string_t getChars(JNIEnv* e, jstring path) +{ + return reinterpret_cast(e->GET_CHARS(path, 0)); +} + +static inline void releaseChars(JNIEnv* e, jstring path, string_t chars) +{ + e->RELEASE_CHARS(path, chars); +} + +#ifndef SGX + +extern "C" JNIEXPORT jstring JNICALL + Java_java_io_File_toCanonicalPath(JNIEnv* /*e*/, jclass, jstring path) +{ + // todo + return path; +} + +extern "C" JNIEXPORT jstring JNICALL + Java_java_io_File_toAbsolutePath(JNIEnv* e UNUSED, jclass, jstring path) +{ +#ifdef PLATFORM_WINDOWS +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + string_t chars = getChars(e, path); + if (chars) { + const unsigned BufferSize = MAX_PATH; + char_t buffer[BufferSize]; + DWORD success = GetFullPathNameW(chars, BufferSize, buffer, 0); + releaseChars(e, path, chars); + + if (success) { + return e->NewString(reinterpret_cast(buffer), + wcslen(buffer)); + } + } + + return path; +#else + string_t chars = getChars(e, path); + if (chars) { + std::wstring partialPath = chars; + releaseChars(e, path, chars); + + std::wstring fullPath = AvianInterop::GetFullPath(partialPath); + + return e->NewString(reinterpret_cast(fullPath.c_str()), + fullPath.length()); + } + return path; +#endif +#else + jstring result = path; + string_t chars = getChars(e, path); + if (chars) { + if (chars[0] != '/') { + char* cwd = getcwd(NULL, 0); + if (cwd) { + unsigned size = strlen(cwd) + strlen(chars) + 2; + RUNTIME_ARRAY(char, buffer, size); + snprintf(RUNTIME_ARRAY_BODY(buffer), size, "%s/%s", cwd, chars); + result = e->NewStringUTF(RUNTIME_ARRAY_BODY(buffer)); + free(cwd); + } + } + releaseChars(e, path, chars); + } + return result; +#endif +} + +extern "C" JNIEXPORT jlong JNICALL + Java_java_io_File_length(JNIEnv* e, jclass, jstring path) +{ +#ifdef PLATFORM_WINDOWS + // Option: without opening file + // http://msdn.microsoft.com/en-us/library/windows/desktop/aa364946(v=vs.85).aspx + string_t chars = getChars(e, path); + if (chars) { + LARGE_INTEGER fileSize; +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + HANDLE file = CreateFileW( + chars, FILE_READ_DATA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); +#else + HANDLE file = CreateFile2( + chars, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr); +#endif + releaseChars(e, path, chars); + if (file == INVALID_HANDLE_VALUE) + return 0; +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + if (!GetFileSizeEx(file, &fileSize)) { + CloseHandle(file); + return 0; + } +#else + FILE_STANDARD_INFO info; + if (!GetFileInformationByHandleEx( + file, FileStandardInfo, &info, sizeof(info))) { + CloseHandle(file); + return 0; + } + fileSize = info.EndOfFile; +#endif + + CloseHandle(file); + return static_cast(fileSize.QuadPart); + } +#else + + string_t chars = getChars(e, path); + if (chars) { + STRUCT_STAT s; + int r = STAT(chars, &s); + releaseChars(e, path, chars); + if (r == 0) { + return s.st_size; + } + } + +#endif + + return 0; +} + +extern "C" JNIEXPORT void JNICALL + Java_java_io_File_mkdir(JNIEnv* e, jclass, jstring path) +{ + string_t chars = getChars(e, path); + if (chars) { + if (not exists(chars)) { + int r = ::MKDIR(chars, 0777); + if (r != 0) { + throwNewErrno(e, "java/io/IOException"); + } + } + releaseChars(e, path, chars); + } +} + +extern "C" JNIEXPORT jboolean JNICALL + Java_java_io_File_createNewFile(JNIEnv* e, jclass, jstring path) +{ + bool result = false; + string_t chars = getChars(e, path); + if (chars) { + if (not exists(chars)) { + int fd = OPEN(chars, O_CREAT | O_WRONLY | O_EXCL, 0666); + if (fd == -1) { + if (errno != EEXIST) { + throwNewErrno(e, "java/io/IOException"); + } + } else { + result = true; + doClose(e, fd); + } + } + releaseChars(e, path, chars); + } + return result; +} + +extern "C" JNIEXPORT void JNICALL +Java_java_io_File_delete(JNIEnv* e, jclass, jstring path) +{ + string_t chars = getChars(e, path); + int r; + if (chars) { +#ifdef PLATFORM_WINDOWS + if (GetFileAttributes(chars) & FILE_ATTRIBUTE_DIRECTORY) { + r = !RemoveDirectory(chars); + } else { + r = REMOVE(chars); + } +#else + r = REMOVE(chars); +#endif + if (r != 0) { + throwNewErrno(e, "java/io/IOException"); + } + releaseChars(e, path, chars); + } +} + +extern "C" JNIEXPORT jboolean JNICALL + Java_java_io_File_canRead(JNIEnv* e, jclass, jstring path) +{ + string_t chars = getChars(e, path); + if (chars) { + int r = ACCESS(chars, R_OK); + releaseChars(e, path, chars); + return (r == 0); + } + return false; +} + +extern "C" JNIEXPORT jboolean JNICALL + Java_java_io_File_canWrite(JNIEnv* e, jclass, jstring path) +{ + string_t chars = getChars(e, path); + if (chars) { + int r = ACCESS(chars, W_OK); + releaseChars(e, path, chars); + return (r == 0); + } + return false; +} + +extern "C" JNIEXPORT jboolean JNICALL + Java_java_io_File_canExecute(JNIEnv* e, jclass, jstring path) +{ + string_t chars = getChars(e, path); + if (chars) { + int r = ACCESS(chars, CHECK_X_OK); + releaseChars(e, path, chars); + return (r == 0); + } + return false; +} + +#ifndef PLATFORM_WINDOWS +extern "C" JNIEXPORT jboolean JNICALL + Java_java_io_File_setExecutable(JNIEnv* e, + jclass, + jstring path, + jboolean executable, + jboolean ownerOnly) +{ + string_t chars = getChars(e, path); + if (chars) { + jboolean v; + int mask; + if (ownerOnly) { + mask = S_IXUSR; + } else { + mask = S_IXUSR | S_IXGRP | S_IXOTH; + } + + STRUCT_STAT s; + int r = STAT(chars, &s); + if (r == 0) { + int mode = s.st_mode; + if (executable) { + mode |= mask; + } else { + mode &= ~mask; + } + if (CHMOD(chars, mode) != 0) { + v = false; + } else { + v = true; + } + } else { + v = false; + } + releaseChars(e, path, chars); + return v; + } + return false; +} + +#else // ifndef PLATFORM_WINDOWS + +extern "C" JNIEXPORT jboolean JNICALL + Java_java_io_File_setExecutable(JNIEnv*, + jclass, + jstring, + jboolean executable, + jboolean) +{ + return executable; +} + +#endif + +extern "C" JNIEXPORT jboolean JNICALL + Java_java_io_File_rename(JNIEnv* e, jclass, jstring old, jstring new_) +{ + string_t oldChars = getChars(e, old); + string_t newChars = getChars(e, new_); + if (oldChars) { + bool v; + if (newChars) { + v = RENAME(oldChars, newChars) == 0; + + releaseChars(e, new_, newChars); + } else { + v = false; + } + releaseChars(e, old, oldChars); + return v; + } else { + return false; + } +} + +extern "C" JNIEXPORT jboolean JNICALL + Java_java_io_File_isDirectory(JNIEnv* e, jclass, jstring path) +{ + string_t chars = getChars(e, path); + if (chars) { + STRUCT_STAT s; + int r = STAT(chars, &s); + bool v = (r == 0 and S_ISDIR(s.st_mode)); + releaseChars(e, path, chars); + return v; + } else { + return false; + } +} + +extern "C" JNIEXPORT jboolean JNICALL + Java_java_io_File_isFile(JNIEnv* e, jclass, jstring path) +{ + string_t chars = getChars(e, path); + if (chars) { + STRUCT_STAT s; + int r = STAT(chars, &s); + bool v = (r == 0 and S_ISREG(s.st_mode)); + releaseChars(e, path, chars); + return v; + } else { + return false; + } +} + +extern "C" JNIEXPORT jboolean JNICALL + Java_java_io_File_exists(JNIEnv* e, jclass, jstring path) +{ + string_t chars = getChars(e, path); + if (chars) { + bool v = exists(chars); + releaseChars(e, path, chars); + return v; + } else { + return false; + } +} + +extern "C" JNIEXPORT jlong JNICALL + Java_java_io_File_lastModified(JNIEnv* e, jclass, jstring path) +{ + string_t chars = getChars(e, path); + if (chars) { +#ifdef PLATFORM_WINDOWS +// Option: without opening file +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa364946(v=vs.85).aspx +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + HANDLE hFile = CreateFileW( + chars, FILE_READ_DATA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); +#else + HANDLE hFile = CreateFile2( + chars, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr); +#endif + releaseChars(e, path, chars); + if (hFile == INVALID_HANDLE_VALUE) + return 0; + LARGE_INTEGER fileDate, filetimeToUnixEpochAdjustment; + filetimeToUnixEpochAdjustment.QuadPart = 11644473600000L * 10000L; +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + FILETIME fileLastWriteTime; + if (!GetFileTime(hFile, 0, 0, &fileLastWriteTime)) { + CloseHandle(hFile); + return 0; + } + fileDate.HighPart = fileLastWriteTime.dwHighDateTime; + fileDate.LowPart = fileLastWriteTime.dwLowDateTime; +#else + FILE_BASIC_INFO fileInfo; + if (!GetFileInformationByHandleEx( + hFile, FileBasicInfo, &fileInfo, sizeof(fileInfo))) { + CloseHandle(hFile); + return 0; + } + fileDate = fileInfo.ChangeTime; +#endif + CloseHandle(hFile); + fileDate.QuadPart -= filetimeToUnixEpochAdjustment.QuadPart; + return fileDate.QuadPart / 10000000L; +#else + struct stat fileStat; + int res = stat(chars, &fileStat); + releaseChars(e, path, chars); + + if (res == -1) { + return 0; + } +#ifdef __APPLE__ +#define MTIME st_mtimespec +#else +#define MTIME st_mtim +#endif + return (static_cast(fileStat.MTIME.tv_sec) * 1000) + + (static_cast(fileStat.MTIME.tv_nsec) / (1000 * 1000)); +#endif + } + + return 0; +} + +#ifdef PLATFORM_WINDOWS + +extern "C" JNIEXPORT jlong JNICALL + Java_java_io_File_openDir(JNIEnv* e, jclass, jstring path) +{ + string_t chars = getChars(e, path); + if (chars) { + unsigned length = wcslen(chars); + unsigned size = length * sizeof(char_t); + + RUNTIME_ARRAY(char_t, buffer, length + 3); + memcpy(RUNTIME_ARRAY_BODY(buffer), chars, size); + memcpy(RUNTIME_ARRAY_BODY(buffer) + length, L"\\*", 6); + + releaseChars(e, path, chars); + + Directory* d = new (malloc(sizeof(Directory))) Directory; +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + d->handle = FindFirstFileW(RUNTIME_ARRAY_BODY(buffer), &(d->data)); +#else + d->handle = FindFirstFileExW(RUNTIME_ARRAY_BODY(buffer), + FindExInfoStandard, + &(d->data), + FindExSearchNameMatch, + NULL, + 0); +#endif + if (d->handle == INVALID_HANDLE_VALUE) { + d->dispose(); + d = 0; + } + + return reinterpret_cast(d); + } else { + return 0; + } +} + +extern "C" JNIEXPORT jstring JNICALL + Java_java_io_File_readDir(JNIEnv* e, jclass, jlong handle) +{ + Directory* d = reinterpret_cast(handle); + + while (true) { + string_t s = d->next(); + if (s) { + if (wcscmp(s, L".") == 0 || wcscmp(s, L"..") == 0) { + // skip . or .. and try again + } else { + return e->NewString(reinterpret_cast(s), wcslen(s)); + } + } else { + return 0; + } + } +} + +extern "C" JNIEXPORT void JNICALL + Java_java_io_File_closeDir(JNIEnv*, jclass, jlong handle) +{ + reinterpret_cast(handle)->dispose(); +} + +#else // not PLATFORM_WINDOWS + +extern "C" JNIEXPORT jlong JNICALL + Java_java_io_File_openDir(JNIEnv* e, jclass, jstring path) +{ + string_t chars = getChars(e, path); + if (chars) { + jlong handle = reinterpret_cast(opendir(chars)); + releaseChars(e, path, chars); + return handle; + } else { + return 0; + } +} + +extern "C" JNIEXPORT jstring JNICALL + Java_java_io_File_readDir(JNIEnv* e, jclass, jlong handle) +{ + struct dirent* directoryEntry; + + if (handle != 0) { + while (true) { + directoryEntry = readdir(reinterpret_cast(handle)); + if (directoryEntry == NULL) { + return NULL; + } else if (strcmp(directoryEntry->d_name, ".") == 0 + || strcmp(directoryEntry->d_name, "..") == 0) { + // skip . or .. and try again + } else { + return e->NewStringUTF(directoryEntry->d_name); + } + } + } + return NULL; +} + +extern "C" JNIEXPORT void JNICALL + Java_java_io_File_closeDir(JNIEnv*, jclass, jlong handle) +{ + if (handle != 0) { + closedir(reinterpret_cast(handle)); + } +} + +#endif // not PLATFORM_WINDOWS + +extern "C" JNIEXPORT jint JNICALL + Java_java_io_FileInputStream_open(JNIEnv* e, jclass, jstring path) +{ + string_t chars = getChars(e, path); + if (chars) { + int fd = doOpen(e, chars, O_RDONLY); + releaseChars(e, path, chars); + return fd; + } else { + return -1; + } +} + +extern "C" JNIEXPORT jint JNICALL + Java_java_io_FileInputStream_read__I(JNIEnv* e, jclass, jint fd) +{ + jbyte data; + int r = doRead(e, fd, &data, 1); + if (r <= 0) { + return -1; + } else { + return data & 0xff; + } +} + +extern "C" JNIEXPORT jint JNICALL + Java_java_io_FileInputStream_read__I_3BII(JNIEnv* e, + jclass, + jint fd, + jbyteArray b, + jint offset, + jint length) +{ + jbyte* data = static_cast(malloc(length)); + if (data == 0) { + throwNew(e, "java/lang/OutOfMemoryError", 0); + return 0; + } + + int r = doRead(e, fd, data, length); + + e->SetByteArrayRegion(b, offset, length, data); + + free(data); + + return r; +} + +extern "C" JNIEXPORT void JNICALL + Java_java_io_FileInputStream_close(JNIEnv* e, jclass, jint fd) +{ + doClose(e, fd); +} + +extern "C" JNIEXPORT jint JNICALL + Java_java_io_FileOutputStream_open(JNIEnv* e, + jclass, + jstring path, + jboolean append) +{ + string_t chars = getChars(e, path); + if (chars) { + int fd = doOpen(e, + chars, + append ? (O_WRONLY | O_CREAT | O_APPEND) + : (O_WRONLY | O_CREAT | O_TRUNC)); + releaseChars(e, path, chars); + return fd; + } else { + return -1; + } +} + +#endif // !SGX + +extern "C" JNIEXPORT void JNICALL + Java_java_io_FileOutputStream_write__II(JNIEnv* e, jclass, jint fd, jint c) +{ + jbyte data = c; + doWrite(e, fd, &data, 1); +} + +extern "C" JNIEXPORT void JNICALL + Java_java_io_FileOutputStream_write__I_3BII(JNIEnv* e, + jclass, + jint fd, + jbyteArray b, + jint offset, + jint length) +{ + jbyte* data = static_cast(malloc(length)); + + if (data == 0) { + throwNew(e, "java/lang/OutOfMemoryError", 0); + return; + } + + e->GetByteArrayRegion(b, offset, length, data); + if (not e->ExceptionCheck()) { + doWrite(e, fd, data, length); + } + + free(data); +} + +extern "C" JNIEXPORT void JNICALL + Java_java_io_FileOutputStream_close(JNIEnv* e, jclass, jint fd) +{ + doClose(e, fd); +} + +#ifndef SGX + +extern "C" JNIEXPORT void JNICALL + Java_java_io_RandomAccessFile_open(JNIEnv* e, + jclass, + jstring path, + jboolean allowWrite, + jlongArray result) +{ + string_t chars = getChars(e, path); + if (chars) { + jlong peer = 0; + jlong length = 0; + int flags = (allowWrite ? O_RDWR | O_CREAT : O_RDONLY) | OPEN_MASK; +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#if defined(PLATFORM_WINDOWS) + int fd = ::_wopen(chars, flags); +#else + int fd = ::open((const char*)chars, flags, 0666); +#endif + releaseChars(e, path, chars); + if (fd == -1) { + throwNewErrno(e, "java/io/IOException"); + return; + } + struct ::stat fileStats; + if (::fstat(fd, &fileStats) == -1) { + ::close(fd); + throwNewErrno(e, "java/io/IOException"); + return; + } + peer = fd; + length = fileStats.st_size; +#else + HANDLE hFile = CreateFile2( + chars, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr); + if (hFile == INVALID_HANDLE_VALUE) { + throwNewErrno(e, "java/io/IOException"); + return; + } + + FILE_STANDARD_INFO info; + if (!GetFileInformationByHandleEx( + hFile, FileStandardInfo, &info, sizeof(info))) { + CloseHandle(hFile); + throwNewErrno(e, "java/io/IOException"); + return; + } + + peer = (jlong)hFile; + length = info.EndOfFile.QuadPart; +#endif + + e->SetLongArrayRegion(result, 0, 1, &peer); + e->SetLongArrayRegion(result, 1, 1, &length); + } +} + +extern "C" JNIEXPORT jint JNICALL + Java_java_io_RandomAccessFile_readBytes(JNIEnv* e, + jclass, + jlong peer, + jlong position, + jbyteArray buffer, + int offset, + int length) +{ +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + int fd = (int)peer; + if (::lseek(fd, position, SEEK_SET) == -1) { + throwNewErrno(e, "java/io/IOException"); + return -1; + } + + uint8_t* dst + = reinterpret_cast(e->GetPrimitiveArrayCritical(buffer, 0)); + + int64_t bytesRead = ::read(fd, dst + offset, length); + e->ReleasePrimitiveArrayCritical(buffer, dst, 0); + + if (bytesRead == -1) { + throwNewErrno(e, "java/io/IOException"); + return -1; + } +#else + HANDLE hFile = (HANDLE)peer; + LARGE_INTEGER lPos; + lPos.QuadPart = position; + if (!SetFilePointerEx(hFile, lPos, nullptr, FILE_BEGIN)) { + throwNewErrno(e, "java/io/IOException"); + return -1; + } + + uint8_t* dst + = reinterpret_cast(e->GetPrimitiveArrayCritical(buffer, 0)); + + DWORD bytesRead = 0; + if (!ReadFile(hFile, dst + offset, length, &bytesRead, nullptr)) { + e->ReleasePrimitiveArrayCritical(buffer, dst, 0); + throwNewErrno(e, "java/io/IOException"); + return -1; + } + e->ReleasePrimitiveArrayCritical(buffer, dst, 0); +#endif + + return (jint)bytesRead; +} + +extern "C" JNIEXPORT jint JNICALL + Java_java_io_RandomAccessFile_writeBytes(JNIEnv* e, + jclass, + jlong peer, + jlong position, + jbyteArray buffer, + int offset, + int length) +{ +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + int fd = (int)peer; + if (::lseek(fd, position, SEEK_SET) == -1) { + throwNewErrno(e, "java/io/IOException"); + return -1; + } + + uint8_t* dst + = reinterpret_cast(e->GetPrimitiveArrayCritical(buffer, 0)); + + int64_t bytesWritten = ::write(fd, dst + offset, length); + e->ReleasePrimitiveArrayCritical(buffer, dst, 0); + + if (bytesWritten == -1) { + throwNewErrno(e, "java/io/IOException"); + return -1; + } +#else + HANDLE hFile = (HANDLE)peer; + LARGE_INTEGER lPos; + lPos.QuadPart = position; + if (!SetFilePointerEx(hFile, lPos, nullptr, FILE_BEGIN)) { + throwNewErrno(e, "java/io/IOException"); + return -1; + } + + uint8_t* dst + = reinterpret_cast(e->GetPrimitiveArrayCritical(buffer, 0)); + + DWORD bytesWritten = 0; + if (!WriteFile(hFile, dst + offset, length, &bytesWritten, nullptr)) { + e->ReleasePrimitiveArrayCritical(buffer, dst, 0); + throwNewErrno(e, "java/io/IOException"); + return -1; + } + e->ReleasePrimitiveArrayCritical(buffer, dst, 0); +#endif + + return (jint)bytesWritten; +} + +extern "C" JNIEXPORT void JNICALL + Java_java_io_RandomAccessFile_close(JNIEnv* /* e*/, jclass, jlong peer) +{ +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + int fd = (int)peer; + ::close(fd); +#else + HANDLE hFile = (HANDLE)peer; + CloseHandle(hFile); +#endif +} + +#endif // !SGX \ No newline at end of file diff --git a/sgx-jvm/avian/classpath/java-lang.cpp b/sgx-jvm/avian/classpath/java-lang.cpp new file mode 100644 index 0000000000..75f89088cd --- /dev/null +++ b/sgx-jvm/avian/classpath/java-lang.cpp @@ -0,0 +1,1253 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "stdlib.h" +#include "time.h" +#include "string.h" +#include "stdio.h" +#include "jni.h" +#include "jni-util.h" +#include "errno.h" +#include "fcntl.h" +#include "ctype.h" + +// Make sure M_* constants (in particular M_E) are exposed in math.h. +// This was a problem on the default mingw install on ubuntu precise +#undef __STRICT_ANSI__ +#include "math.h" + +#ifdef PLATFORM_WINDOWS + +#include "windows.h" +#include "winbase.h" +#include "io.h" +#include "tchar.h" +#include "float.h" +#include "sys/types.h" +#include "sys/timeb.h" +#define SO_PREFIX "" +#define SO_SUFFIX ".dll" + +#ifdef _MSC_VER +#define snprintf sprintf_s +#define isnan _isnan +#define isfinite _finite +#define strtof strtod +#endif + +#else // not PLATFORM_WINDOWS + +#define SO_PREFIX "lib" +#ifdef __APPLE__ +#define SO_SUFFIX ".dylib" +#include +#if !TARGET_IPHONE_SIMULATOR && !TARGET_OS_IPHONE +#include +#endif +#else +#define SO_SUFFIX ".so" +#endif +#include "unistd.h" +#include "limits.h" +#include "signal.h" +#include "sys/time.h" +#include "sys/types.h" +#ifndef __ANDROID__ +#include "sys/sysctl.h" +#endif +#include "sys/utsname.h" +#include "sys/wait.h" + +#endif // not PLATFORM_WINDOWS + +#ifndef WINAPI_FAMILY +#ifndef WINAPI_PARTITION_DESKTOP +#define WINAPI_PARTITION_DESKTOP 1 +#endif + +#ifndef WINAPI_FAMILY_PARTITION +#define WINAPI_FAMILY_PARTITION(x) (x) +#endif +#else +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + +#include "avian-interop.h" + +#endif +#endif // WINAPI_FAMILY + +#ifndef M_E +// in new C++-11 standard math.h doesn't have M_E, at least on MinGW, so define it manually +#define M_E 2.7182818284590452354 +#endif // M_E + +namespace { + +void add(JNIEnv* e, jobjectArray array, unsigned index, const char* format, ...) +{ + int size = 256; + while (true) { + va_list a; + va_start(a, format); + RUNTIME_ARRAY(char, buffer, size); + int r = vsnprintf(RUNTIME_ARRAY_BODY(buffer), size - 1, format, a); + va_end(a); + if (r >= 0 and r < size - 1) { + e->SetObjectArrayElement( + array, index++, e->NewStringUTF(RUNTIME_ARRAY_BODY(buffer))); + return; + } + + size *= 2; + } +} + +#ifdef PLATFORM_WINDOWS + +void add(JNIEnv* e, + jobjectArray array, + unsigned index, + const WCHAR* format, + ...) +{ + int size = 256; + while (true) { + va_list a; + va_start(a, format); + RUNTIME_ARRAY(WCHAR, buffer, size); + int r = _vsnwprintf(RUNTIME_ARRAY_BODY(buffer), size - 1, format, a); + va_end(a); + if (r >= 0 and r < size - 1) { + e->SetObjectArrayElement( + array, + index++, + e->NewString(reinterpret_cast(RUNTIME_ARRAY_BODY(buffer)), + r)); + return; + } + + size *= 2; + } +} + +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +char* getErrorStr(DWORD err) +{ + LPSTR errorStr = 0; + if (!FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, + 0, + err, + LANG_SYSTEM_DEFAULT, + (LPSTR)&errorStr, + 0, + 0)) { + char* errStr = (char*)malloc(9 * sizeof(char)); + snprintf(errStr, 9, "%d", (int)err); + return errStr; + } + char* errStr = strdup(errorStr); + LocalFree(errorStr); + return errStr; +} +#else +char* getErrorStr(DWORD err) +{ + LPSTR errorStr = (LPSTR)malloc(4096); // NOTE: something constant + if (!FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + 0, + err, + LANG_SYSTEM_DEFAULT, + errorStr, + 0, + 0)) { + free(errorStr); + + char* errStr = (char*)malloc(9 * sizeof(char)); + snprintf(errStr, 9, "%d", (int)err); + return errStr; + } + char* errStr = strdup(errorStr); + free(errorStr); + return errStr; +} +#endif + +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +void makePipe(JNIEnv* e, HANDLE p[2]) +{ + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(sa); + sa.bInheritHandle = 1; + sa.lpSecurityDescriptor = 0; + + BOOL success = CreatePipe(p, p + 1, &sa, 0); + if (not success) { + throwNew(e, "java/io/IOException", getErrorStr(GetLastError())); + } +} +#endif + +int descriptor(JNIEnv* e, HANDLE h) +{ + int fd = _open_osfhandle(reinterpret_cast(h), 0); + if (fd == -1) { + throwNewErrno(e, "java/io/IOException"); + } + return fd; +} +#else +void makePipe(JNIEnv* e, int p[2]) +{ + if (pipe(p) != 0) { + throwNewErrno(e, "java/io/IOException"); + } +} + +void safeClose(int& fd) +{ + if (fd != -1) + close(fd); + fd = -1; +} + +void close(int p[2]) +{ + ::close(p[0]); + ::close(p[1]); +} + +void clean(JNIEnv* e, jobjectArray command, char** p) +{ + int i = 0; + for (char** x = p; *x; ++x, ++i) { + jstring element = (jstring)e->GetObjectArrayElement(command, i); + e->ReleaseStringUTFChars(element, *x); + } + free(p); +} +#endif +} + +class Locale { // represents an ISO two-char language/country pair + static const unsigned FIELDLEN = 2; + static const unsigned FIELDSIZE = FIELDLEN + 1; + + static const char* DEFAULT_LANGUAGE; + static const char* DEFAULT_REGION; + + char language[FIELDSIZE]; + char region[FIELDSIZE]; + + bool isLanguage(const char* language) + { + if (!language) + return false; + unsigned len = strlen(language); + if (len != FIELDLEN) + return false; + const char* p = language - 1; + while (islower(*++p)) + ; + if (*p != '\0') + return false; + return true; + } + + bool isRegion(const char* region) + { + if (!region) + return false; + unsigned len = strlen(region); + if (len != FIELDLEN) + return false; + const char* p = region - 1; + while (isupper(*++p)) + ; + if (*p != '\0') + return false; + return true; + } + + public: + Locale(const char* language = "") + { + Locale l(language, ""); + *this = l; + } + + Locale(const char* language, const char* region) + { + language = isLanguage(language) ? language : DEFAULT_LANGUAGE; + region = isRegion(region) ? region : DEFAULT_REGION; + memcpy(this->language, language, FIELDSIZE); + memcpy(this->region, region, FIELDSIZE); + } + + Locale& operator=(const Locale& l) + { + memcpy(language, l.language, FIELDSIZE); + memcpy(region, l.region, FIELDSIZE); + return *this; + } + + const char* getLanguage() + { + return reinterpret_cast(language); + } + const char* getRegion() + { + return reinterpret_cast(region); + } +}; +const char* Locale::DEFAULT_LANGUAGE = "en"; +const char* Locale::DEFAULT_REGION = ""; + +#ifdef PLATFORM_WINDOWS + +void appendN(char** dest, char ch, size_t length) +{ + for (size_t i = 0; i < length; i++) { + *((*dest)++) = ch; + } +} + +bool needsEscape(const char* src, size_t length) +{ + const char* end = src + length; + for (const char* ptr = src; ptr < end; ptr++) { + switch (*ptr) { + case ' ': + case '\t': + case '\n': + case '\v': + case '"': + return true; + } + } + + return false; +} + +void copyAndEscape(char** dest, const char* src, size_t length) +{ + char* destp = *dest; + const char* end = src + length; + + if (length != 0 && !needsEscape(src, length)) { + for (const char* ptr = src; ptr < end; ptr++) { + *(destp++) = *ptr; + } + } else { + *(destp++) = '"'; + + for (const char* ptr = src;; ptr++) { + unsigned numBackslashes = 0; + + while (ptr < end && *ptr == '\\') { + ptr++; + numBackslashes++; + } + + if (ptr == end) { + appendN(&destp, '\\', 2 * numBackslashes); + break; + } else if (*ptr == '"') { + appendN(&destp, '\\', 2 * numBackslashes + 1); + *(destp++) = *ptr; + } else { + appendN(&destp, '\\', numBackslashes); + *(destp++) = *ptr; + } + } + + *(destp++) = '"'; + } + + *dest = destp; +} + +extern "C" JNIEXPORT void JNICALL + Java_java_lang_Runtime_exec(JNIEnv* e, + jclass, + jobjectArray command, + jlongArray process) +{ +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + int size = 0; + for (int i = 0; i < e->GetArrayLength(command); ++i) { + jstring element = (jstring)e->GetObjectArrayElement(command, i); + if (element) { + // worst case, assuming every character is '"', and we escape all of them + size += 2 * e->GetStringUTFLength(element) + 3; + } else { + throwNew(e, + "java/lang/NullPointerException", + strdup("null string array element")); + } + } + + RUNTIME_ARRAY(char, line, size); + char* linep = RUNTIME_ARRAY_BODY(line); + for (int i = 0; i < e->GetArrayLength(command); ++i) { + if (i) + *(linep++) = _T(' '); + jstring element = (jstring)e->GetObjectArrayElement(command, i); + const char* s = e->GetStringUTFChars(element, 0); + + copyAndEscape(&linep, s, e->GetStringUTFLength(element)); + + e->ReleaseStringUTFChars(element, s); + } + *(linep++) = _T('\0'); + + HANDLE in[] = {0, 0}; + HANDLE out[] = {0, 0}; + HANDLE err[] = {0, 0}; + + makePipe(e, in); + SetHandleInformation(in[0], HANDLE_FLAG_INHERIT, 0); + jlong inDescriptor = static_cast(descriptor(e, in[0])); + if (e->ExceptionCheck()) + return; + e->SetLongArrayRegion(process, 2, 1, &inDescriptor); + makePipe(e, out); + SetHandleInformation(out[1], HANDLE_FLAG_INHERIT, 0); + jlong outDescriptor = static_cast(descriptor(e, out[1])); + if (e->ExceptionCheck()) + return; + e->SetLongArrayRegion(process, 3, 1, &outDescriptor); + makePipe(e, err); + SetHandleInformation(err[0], HANDLE_FLAG_INHERIT, 0); + jlong errDescriptor = static_cast(descriptor(e, err[0])); + if (e->ExceptionCheck()) + return; + e->SetLongArrayRegion(process, 4, 1, &errDescriptor); + + PROCESS_INFORMATION pi; + ZeroMemory(&pi, sizeof(pi)); + + STARTUPINFO si; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdOutput = in[1]; + si.hStdInput = out[0]; + si.hStdError = err[1]; + + BOOL success = CreateProcess(0, + (LPSTR)RUNTIME_ARRAY_BODY(line), + 0, + 0, + 1, + CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT, + 0, + 0, + &si, + &pi); + + CloseHandle(in[1]); + CloseHandle(out[0]); + CloseHandle(err[1]); + + if (not success) { + throwNew(e, "java/io/IOException", getErrorStr(GetLastError())); + return; + } + + jlong pid = reinterpret_cast(pi.hProcess); + e->SetLongArrayRegion(process, 0, 1, &pid); + jlong tid = reinterpret_cast(pi.hThread); + e->SetLongArrayRegion(process, 1, 1, &tid); +#else + throwNew(e, "java/io/Exception", strdup("Not supported on WinRT/WinPhone8")); +#endif +} + +extern "C" JNIEXPORT jint JNICALL + Java_java_lang_Runtime_waitFor(JNIEnv* e, jclass, jlong pid, jlong tid) +{ +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + DWORD exitCode; + WaitForSingleObject(reinterpret_cast(pid), INFINITE); + BOOL success = GetExitCodeProcess(reinterpret_cast(pid), &exitCode); + if (not success) { + throwNew(e, "java/lang/Exception", getErrorStr(GetLastError())); + } + + CloseHandle(reinterpret_cast(pid)); + CloseHandle(reinterpret_cast(tid)); + + return exitCode; +#else + throwNew(e, "java/io/Exception", strdup("Not supported on WinRT/WinPhone8")); + return -1; +#endif +} + +extern "C" JNIEXPORT void JNICALL + Java_java_lang_Runtime_kill(JNIEnv* e UNUSED, jclass, jlong pid) +{ +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + TerminateProcess(reinterpret_cast(pid), 1); +#else + throwNew(e, "java/io/Exception", strdup("Not supported on WinRT/WinPhone8")); +#endif +} + +Locale getLocale() +{ +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + const char* lang = ""; + const char* reg = ""; + unsigned langid = GetUserDefaultUILanguage(); + unsigned prilang = langid & 0x3ff; + unsigned sublang = langid >> 10; + + switch (prilang) { + case 0x004: { + lang = "zh"; + switch (sublang) { + case 0x01: + reg = "CN"; + break; + case 0x02: + reg = "TW"; + break; + case 0x03: + reg = "HK"; + break; + case 0x04: + reg = "SG"; + break; + } + } break; + case 0x006: + lang = "da"; + reg = "DK"; + break; + case 0x007: + lang = "de"; + reg = "DE"; + break; + case 0x009: { + lang = "en"; + switch (sublang) { + case 0x01: + reg = "US"; + break; + case 0x02: + reg = "GB"; + break; + case 0x03: + reg = "AU"; + break; + case 0x04: + reg = "CA"; + break; + case 0x05: + reg = "NZ"; + break; + case 0x06: + reg = "IE"; + break; + case 0x07: + reg = "ZA"; + break; + case 0x10: + reg = "IN"; + break; + } + } break; + case 0x00a: { + lang = "es"; + switch (sublang) { + case 0x01: + case 0x03: + reg = "ES"; + break; + case 0x02: + reg = "MX"; + break; + } + } break; + case 0x00c: { + lang = "fr"; + switch (sublang) { + case 0x01: + reg = "FR"; + break; + case 0x02: + reg = "BE"; + break; + case 0x03: + reg = "CA"; + break; + } + } break; + case 0x010: + lang = "it"; + reg = "IT"; + break; + case 0x011: + lang = "ja"; + reg = "JP"; + break; + case 0x012: + lang = "ko"; + reg = "KR"; + break; + case 0x013: { + lang = "nl"; + switch (sublang) { + case 0x01: + reg = "NL"; + break; + case 0x02: + reg = "BE"; + break; + } + } break; + case 0x014: + lang = "no"; + reg = "NO"; + break; + case 0x015: + lang = "pl"; + reg = "PL"; + break; + case 0x016: { + lang = "pt"; + switch (sublang) { + case 0x01: + reg = "BR"; + break; + case 0x02: + reg = "PT"; + break; + } + } break; + case 0x018: + lang = "ro"; + reg = "RO"; + break; + case 0x019: + lang = "ru"; + reg = "RU"; + break; + case 0x01d: + lang = "sv"; + reg = "SE"; + break; + default: + lang = "en"; + } + + return Locale(lang, reg); +#else + std::wstring culture = AvianInterop::GetCurrentUICulture(); + char* cultureName + = strdup(std::string(culture.begin(), culture.end()).c_str()); + char* delimiter = strchr(cultureName, '-'); + if (!delimiter) { + free(cultureName); + return Locale("en", "US"); + } + const char* lang = cultureName; + const char* reg = delimiter + 1; + *delimiter = 0; + Locale locale(lang, reg); + free(cultureName); + return locale; +#endif +} +#else +extern "C" JNIEXPORT void JNICALL + Java_java_lang_Runtime_exec(JNIEnv* e, + jclass, + jobjectArray command, + jlongArray process) +{ + char** argv = static_cast( + malloc((e->GetArrayLength(command) + 1) * sizeof(char*))); + int i; + for (i = 0; i < e->GetArrayLength(command); i++) { + jstring element = (jstring)e->GetObjectArrayElement(command, i); + char* s = const_cast(e->GetStringUTFChars(element, 0)); + argv[i] = s; + } + argv[i] = 0; + + int in[] = {-1, -1}; + int out[] = {-1, -1}; + int err[] = {-1, -1}; + int msg[] = {-1, -1}; + + makePipe(e, in); + if (e->ExceptionCheck()) + return; + jlong inDescriptor = static_cast(in[0]); + e->SetLongArrayRegion(process, 2, 1, &inDescriptor); + makePipe(e, out); + if (e->ExceptionCheck()) + return; + jlong outDescriptor = static_cast(out[1]); + e->SetLongArrayRegion(process, 3, 1, &outDescriptor); + makePipe(e, err); + if (e->ExceptionCheck()) + return; + jlong errDescriptor = static_cast(err[0]); + e->SetLongArrayRegion(process, 4, 1, &errDescriptor); + makePipe(e, msg); + if (e->ExceptionCheck()) + return; + if (fcntl(msg[1], F_SETFD, FD_CLOEXEC) != 0) { + throwNewErrno(e, "java/io/IOException"); + return; + } + +#ifdef __QNX__ + // fork(2) doesn't work in multithreaded QNX programs. See + // http://www.qnx.com/developers/docs/6.4.1/neutrino/getting_started/s1_procs.html + pid_t pid = vfork(); +#else + // We might be able to just use vfork on all UNIX-style systems, but + // the manual makes it sound dangerous due to the shared + // parent/child address space, so we use fork if we can. + pid_t pid = fork(); +#endif + switch (pid) { + case -1: // error + throwNewErrno(e, "java/io/IOException"); + return; + case 0: { // child + // Setup stdin, stdout and stderr + dup2(in[1], 1); + close(in); + dup2(out[0], 0); + close(out); + dup2(err[1], 2); + close(err); + close(msg[0]); + + execvp(argv[0], argv); + + // Error if here + int val = errno; + ssize_t rv UNUSED = write(msg[1], &val, sizeof(val)); + exit(127); + } break; + + default: { // parent + jlong JNIPid = static_cast(pid); + e->SetLongArrayRegion(process, 0, 1, &JNIPid); + + safeClose(in[1]); + safeClose(out[0]); + safeClose(err[1]); + safeClose(msg[1]); + + int val; + int r = read(msg[0], &val, sizeof(val)); + if (r == -1) { + throwNewErrno(e, "java/io/IOException"); + return; + } else if (r) { + errno = val; + throwNewErrno(e, "java/io/IOException"); + return; + } + } break; + } + + safeClose(msg[0]); + clean(e, command, argv); + + fcntl(in[0], F_SETFD, FD_CLOEXEC); + fcntl(out[1], F_SETFD, FD_CLOEXEC); + fcntl(err[0], F_SETFD, FD_CLOEXEC); +} + +extern "C" JNIEXPORT jint JNICALL + Java_java_lang_Runtime_waitFor(JNIEnv*, jclass, jlong pid, jlong) +{ + bool finished = false; + int status; + int exitCode; + while (!finished) { + waitpid(pid, &status, 0); + if (WIFEXITED(status)) { + finished = true; + exitCode = WEXITSTATUS(status); + } else if (WIFSIGNALED(status)) { + finished = true; + exitCode = -1; + } + } + + return exitCode; +} + +extern "C" JNIEXPORT void JNICALL + Java_java_lang_Runtime_kill(JNIEnv*, jclass, jlong pid) +{ + kill((pid_t)pid, SIGTERM); +} + +Locale getLocale() +{ + Locale fallback; + + const char* LANG = getenv("LANG"); + if (!LANG || strcmp(LANG, "C") == 0) + return fallback; + + int len = strlen(LANG); + char buf[len + 1]; // + 1 for the '\0' char + memcpy(buf, LANG, len + 1); + + char* tracer = buf; + const char* reg; + + while (*tracer && *tracer != '_') + ++tracer; + if (!*tracer) + return fallback; + *tracer = '\0'; + reg = ++tracer; + + while (*tracer && *tracer != '.') + ++tracer; + if (tracer == reg) + return fallback; + *tracer = '\0'; + + Locale locale(buf, reg); + return locale; +} +#endif + +extern "C" JNIEXPORT jobjectArray JNICALL + Java_java_lang_System_getNativeProperties(JNIEnv* e, jclass) +{ + jobjectArray array + = e->NewObjectArray(32, e->FindClass("java/lang/String"), 0); + + unsigned index = 0; + +#ifdef ARCH_x86_32 + e->SetObjectArrayElement(array, index++, e->NewStringUTF("os.arch=x86")); + +#elif defined ARCH_x86_64 + e->SetObjectArrayElement(array, index++, e->NewStringUTF("os.arch=x86_64")); + +#elif defined ARCH_arm + e->SetObjectArrayElement(array, index++, e->NewStringUTF("os.arch=arm")); + +#elif defined ARCH_arm64 + e->SetObjectArrayElement(array, index++, e->NewStringUTF("os.arch=arm64")); + +#else +#error "unknown architecture" + +#endif + +#ifdef PLATFORM_WINDOWS + e->SetObjectArrayElement( + array, index++, e->NewStringUTF("line.separator=\r\n")); + + e->SetObjectArrayElement( + array, index++, e->NewStringUTF("file.separator=\\")); + + e->SetObjectArrayElement(array, index++, e->NewStringUTF("path.separator=;")); + +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + e->SetObjectArrayElement(array, index++, e->NewStringUTF("os.name=Windows")); + +#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE) + e->SetObjectArrayElement( + array, index++, e->NewStringUTF("os.name=Windows Phone")); + +#else + e->SetObjectArrayElement( + array, index++, e->NewStringUTF("os.name=Windows RT")); + +#endif + +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + { + OSVERSIONINFO OSversion; + OSversion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + ::GetVersionEx(&OSversion); + + add(e, + array, + index++, + "os.version=%i.%i", + static_cast(OSversion.dwMajorVersion), + static_cast(OSversion.dwMinorVersion)); + } + +#else + // Currently there is no alternative on WinRT/WP8 + e->SetObjectArrayElement(array, index++, e->NewStringUTF("os.version=8.0")); + +#endif + +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + { + WCHAR buffer[MAX_PATH]; + GetTempPathW(MAX_PATH, buffer); + add(e, array, index++, L"java.io.tmpdir=%ls", buffer); + } + +#else + add(e, + array, + index++, + L"java.io.tmpdir=%ls", + AvianInterop::GetTemporaryFolder().c_str()); + +#endif + +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + { + WCHAR buffer[MAX_PATH]; + GetCurrentDirectoryW(MAX_PATH, buffer); + add(e, array, index++, L"user.dir=%ls", buffer); + } + +#else + add(e, + array, + index++, + L"user.dir=%ls", + AvianInterop::GetInstalledLocation().c_str()); + +#endif + +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#ifdef _MSC_VER + { + WCHAR buffer[MAX_PATH]; + size_t needed; + if (_wgetenv_s(&needed, buffer, MAX_PATH, L"USERPROFILE") == 0) { + add(e, array, index++, L"user.home=%ls", buffer); + } + } + +#else + add(e, array, index++, L"user.home=%ls", _wgetenv(L"USERPROFILE")); + +#endif +#else + add(e, + array, + index++, + L"user.home=%ls", + AvianInterop::GetDocumentsLibraryLocation().c_str()); + +#endif + +#else // not Windows + e->SetObjectArrayElement( + array, index++, e->NewStringUTF("line.separator=\n")); + + e->SetObjectArrayElement(array, index++, e->NewStringUTF("file.separator=/")); + + e->SetObjectArrayElement(array, index++, e->NewStringUTF("path.separator=:")); + +#ifdef __APPLE__ + e->SetObjectArrayElement(array, index++, e->NewStringUTF("os.name=Mac OS X")); + +#elif defined __FreeBSD__ + e->SetObjectArrayElement(array, index++, e->NewStringUTF("os.name=FreeBSD")); + +#else + e->SetObjectArrayElement(array, index++, e->NewStringUTF("os.name=Linux")); + +#endif + { + struct utsname system_id; + uname(&system_id); + add(e, array, index++, "os.version=%s", system_id.release); + } + + e->SetObjectArrayElement( + array, index++, e->NewStringUTF("java.io.tmpdir=/tmp")); + + { + char buffer[PATH_MAX]; + add(e, array, index++, "user.dir=%s", getcwd(buffer, PATH_MAX)); + } + + add(e, array, index++, "user.home=%s", getenv("HOME")); + +#endif // not Windows + + { + Locale locale = getLocale(); + add(e, array, index++, "user.language=%s", locale.getLanguage()); + add(e, array, index++, "user.region=%s", locale.getRegion()); + } + + return array; +} + +// System.getEnvironment() implementation +// TODO: For Win32, replace usage of deprecated _environ and add Unicode +// support (neither of which is likely to be of great importance). +#ifdef AVIAN_IOS +namespace { +const char* environ[] = {0}; +} +#elif defined __APPLE__ +#include +#define environ (*_NSGetEnviron()) +#elif defined(WINAPI_FAMILY) \ + && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +// WinRT/WP8 does not provide alternative for environment variables +char* environ[] = {0}; +#else +extern char** environ; +#endif +extern "C" JNIEXPORT jobjectArray JNICALL + Java_java_lang_System_getEnvironment(JNIEnv* env, jclass) +{ + int length; + for (length = 0; environ[length] != 0; ++length) + ; + + jobjectArray stringArray = env->NewObjectArray( + length, env->FindClass("java/lang/String"), env->NewStringUTF("")); + + for (int i = 0; i < length; i++) { + jobject varString = env->NewStringUTF(environ[i]); + env->SetObjectArrayElement(stringArray, i, varString); + } + + return stringArray; +} + +extern "C" JNIEXPORT jlong JNICALL + Java_java_lang_System_currentTimeMillis(JNIEnv*, jclass) +{ +#ifdef PLATFORM_WINDOWS + // We used to use _ftime here, but that only gives us 1-second + // resolution on Windows 7. _ftime_s might work better, but MinGW + // doesn't have it as of this writing. So we use this mess instead: + FILETIME time; + GetSystemTimeAsFileTime(&time); + return (((static_cast(time.dwHighDateTime) << 32) | time.dwLowDateTime) + / 10000) - 11644473600000LL; +#else + timeval tv = {0, 0}; + gettimeofday(&tv, 0); + return (static_cast(tv.tv_sec) * 1000) + + (static_cast(tv.tv_usec) / 1000); +#endif +} + +extern "C" JNIEXPORT jstring JNICALL + Java_java_lang_System_doMapLibraryName(JNIEnv* e, jclass, jstring name) +{ + jstring r = 0; + const char* chars = e->GetStringUTFChars(name, 0); + if (chars) { + unsigned nameLength = strlen(chars); + unsigned size = sizeof(SO_PREFIX) + nameLength + sizeof(SO_SUFFIX); + RUNTIME_ARRAY(char, buffer, size); + snprintf(RUNTIME_ARRAY_BODY(buffer), size, SO_PREFIX "%s" SO_SUFFIX, chars); + r = e->NewStringUTF(RUNTIME_ARRAY_BODY(buffer)); + + e->ReleaseStringUTFChars(name, chars); + } + return r; +} + +extern "C" JNIEXPORT jboolean JNICALL + Java_java_lang_Double_isInfinite(JNIEnv*, jclass, jdouble val) +{ + return !isfinite(val); +} + +extern "C" JNIEXPORT jboolean JNICALL + Java_java_lang_Double_isNaN(JNIEnv*, jclass, jdouble val) +{ + return isnan(val); +} + +extern "C" JNIEXPORT jdouble JNICALL + Java_java_lang_Double_doubleFromString(JNIEnv* e, + jclass, + jstring s, + jintArray numDoublesRead) +{ + const char* chars = e->GetStringUTFChars(s, 0); + double d = 0.0; + jint numRead = 0; + + if (chars) { + char* lastRead; + d = strtod(chars, &lastRead); + if ((lastRead != chars) && ((chars + strlen(chars)) == lastRead)) { + numRead = 1; + } + e->ReleaseStringUTFChars(s, chars); + } + e->SetIntArrayRegion(numDoublesRead, 0, 1, &numRead); + return d; +} + +extern "C" JNIEXPORT jboolean JNICALL + Java_java_lang_Float_isInfinite(JNIEnv*, jclass, jfloat val) +{ + return !isfinite(val); +} + +extern "C" JNIEXPORT jboolean JNICALL + Java_java_lang_Float_isNaN(JNIEnv*, jclass, jfloat val) +{ + return isnan(val); +} + +extern "C" JNIEXPORT jfloat JNICALL + Java_java_lang_Float_floatFromString(JNIEnv* e, + jclass, + jstring s, + jintArray numFloatsRead) +{ + const char* chars = e->GetStringUTFChars(s, 0); + float f = 0.0; + jint numRead = 0; + + if (chars) { + char* lastRead; + f = strtof(chars, &lastRead); + if ((lastRead != chars) && ((chars + strlen(chars)) == lastRead)) { + numRead = 1; + } + e->ReleaseStringUTFChars(s, chars); + } + e->SetIntArrayRegion(numFloatsRead, 0, 1, &numRead); + return f; +} + +extern "C" JNIEXPORT jdouble JNICALL + Java_java_lang_Math_sin(JNIEnv*, jclass, jdouble val) +{ + return sin(val); +} + +extern "C" JNIEXPORT jdouble JNICALL + Java_java_lang_Math_cos(JNIEnv*, jclass, jdouble val) +{ + return cos(val); +} + +extern "C" JNIEXPORT jdouble JNICALL + Java_java_lang_Math_tan(JNIEnv*, jclass, jdouble val) +{ + return tan(val); +} + +extern "C" JNIEXPORT jdouble JNICALL + Java_java_lang_Math_asin(JNIEnv*, jclass, jdouble val) +{ + return asin(val); +} + +extern "C" JNIEXPORT jdouble JNICALL + Java_java_lang_Math_acos(JNIEnv*, jclass, jdouble val) +{ + return acos(val); +} + +extern "C" JNIEXPORT jdouble JNICALL + Java_java_lang_Math_atan(JNIEnv*, jclass, jdouble val) +{ + return atan(val); +} + +extern "C" JNIEXPORT jdouble JNICALL + Java_java_lang_Math_atan2(JNIEnv*, jclass, jdouble y, jdouble x) +{ + return atan2(y, x); +} + +extern "C" JNIEXPORT jdouble JNICALL + Java_java_lang_Math_sinh(JNIEnv*, jclass, jdouble val) +{ + return sinh(val); +} + +extern "C" JNIEXPORT jdouble JNICALL + Java_java_lang_Math_cosh(JNIEnv*, jclass, jdouble val) +{ + return cosh(val); +} + +extern "C" JNIEXPORT jdouble JNICALL + Java_java_lang_Math_tanh(JNIEnv*, jclass, jdouble val) +{ + return tanh(val); +} + +extern "C" JNIEXPORT jdouble JNICALL + Java_java_lang_Math_sqrt(JNIEnv*, jclass, jdouble val) +{ + return sqrt(val); +} + +extern "C" JNIEXPORT jdouble JNICALL + Java_java_lang_Math_pow(JNIEnv*, jclass, jdouble val, jdouble exp) +{ + return pow(val, exp); +} + +extern "C" JNIEXPORT jdouble JNICALL + Java_java_lang_Math_log(JNIEnv*, jclass, jdouble val) +{ + return log(val); +} + +extern "C" JNIEXPORT jdouble JNICALL + Java_java_lang_Math_floor(JNIEnv*, jclass, jdouble val) +{ + return floor(val); +} + +extern "C" JNIEXPORT jdouble JNICALL + Java_java_lang_Math_ceil(JNIEnv*, jclass, jdouble val) +{ + return ceil(val); +} + +extern "C" JNIEXPORT jdouble JNICALL + Java_java_lang_Math_exp(JNIEnv*, jclass, jdouble exp) +{ + return pow(M_E, exp); +} + +extern "C" JNIEXPORT jint JNICALL + Java_java_lang_Double_fillBufferWithDouble(JNIEnv* e, + jclass, + jdouble val, + jbyteArray buffer, + jint bufferSize) +{ + jboolean isCopy; + jbyte* buf = e->GetByteArrayElements(buffer, &isCopy); + jint count = snprintf(reinterpret_cast(buf), bufferSize, "%g", val); + e->ReleaseByteArrayElements(buffer, buf, 0); + return count; +} diff --git a/sgx-jvm/avian/classpath/java-net.cpp b/sgx-jvm/avian/classpath/java-net.cpp new file mode 100644 index 0000000000..cafb02238a --- /dev/null +++ b/sgx-jvm/avian/classpath/java-net.cpp @@ -0,0 +1,145 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef SGX + +#include "jni.h" +#include "avian/machine.h" +#include "sockets.h" +#include "jni-util.h" + +using namespace avian::classpath::sockets; + +extern "C" JNIEXPORT void JNICALL Java_java_net_Socket_init(JNIEnv* e, jclass) +{ + init(e); +} + +extern "C" JNIEXPORT SOCKET JNICALL + Java_java_net_Socket_create(JNIEnv* e, jclass) +{ + return create(e); +} + +extern "C" JNIEXPORT void JNICALL Java_java_net_Socket_connect(JNIEnv* e, + jclass, + jlong sock, + jlong addr, + jshort port) +{ + connect(e, static_cast(sock), addr, port); +} + +extern "C" JNIEXPORT void JNICALL Java_java_net_Socket_bind(JNIEnv* e, + jclass, + jlong sock, + jlong addr, + jshort port) +{ + bind(e, static_cast(sock), addr, port); +} + +extern "C" JNIEXPORT void JNICALL + Java_java_net_Socket_abort(JNIEnv* e, jclass, jlong sock) +{ + abort(e, static_cast(sock)); +} + +extern "C" JNIEXPORT void JNICALL + Java_java_net_Socket_close(JNIEnv* e, jclass, jlong sock) +{ + close(e, static_cast(sock)); +} + +extern "C" JNIEXPORT void JNICALL + Java_java_net_Socket_closeOutput(JNIEnv* e, jclass, jlong sock) +{ + close_output(e, static_cast(sock)); +} + +extern "C" JNIEXPORT void JNICALL + Java_java_net_Socket_closeInput(JNIEnv* e, jclass, jlong sock) +{ + close_input(e, static_cast(sock)); +} + +extern "C" JNIEXPORT void JNICALL + Avian_java_net_Socket_send(vm::Thread* t, vm::object, uintptr_t* arguments) +{ /* SOCKET s, object buffer_obj, int start_pos, int count */ + SOCKET& s = *(reinterpret_cast(&arguments[0])); + vm::GcByteArray* buffer_obj = vm::cast( + t, reinterpret_cast(arguments[2])); + int32_t& start_pos = *(reinterpret_cast(&arguments[3])); + int32_t& count = *(reinterpret_cast(&arguments[4])); + char* buffer = reinterpret_cast(&buffer_obj->body()[start_pos]); + avian::classpath::sockets::send((JNIEnv*)t, s, buffer, count); +} + +extern "C" JNIEXPORT int64_t JNICALL + Avian_java_net_Socket_recv(vm::Thread* t, vm::object, uintptr_t* arguments) +{ /* SOCKET s, object buffer_obj, int start_pos, int count */ + SOCKET& s = *(reinterpret_cast(&arguments[0])); + vm::GcByteArray* buffer_obj = vm::cast( + t, reinterpret_cast(arguments[2])); + int32_t& start_pos = *(reinterpret_cast(&arguments[3])); + int32_t& count = *(reinterpret_cast(&arguments[4])); + char* buffer = reinterpret_cast(&buffer_obj->body()[start_pos]); + return avian::classpath::sockets::recv((JNIEnv*)t, s, buffer, count); +} + +extern "C" JNIEXPORT jint JNICALL + Java_java_net_InetAddress_ipv4AddressForName(JNIEnv* e, + jclass, + jstring name) +{ + if(!name) { + throwNew(e, "java/lang/NullPointerException", 0); + return 0; + } + + const char* chars = e->GetStringUTFChars(name, 0); + if (chars) { +#ifdef PLATFORM_WINDOWS + hostent* host = gethostbyname(chars); + e->ReleaseStringUTFChars(name, chars); + if (host) { + return ntohl(reinterpret_cast(host->h_addr_list[0])->s_addr); + } else { + throwNew(e, "java/net/UnknownHostException", 0); + return 0; + } +#else + addrinfo hints; + memset(&hints, 0, sizeof(addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + addrinfo* result; + int r = getaddrinfo(chars, 0, &hints, &result); + e->ReleaseStringUTFChars(name, chars); + + if (r != 0) { + throwNew(e, "java/net/UnknownHostException", 0); + return 0; + } else { + int address = ntohl( + reinterpret_cast(result->ai_addr)->sin_addr.s_addr); + + freeaddrinfo(result); + return address; + } +#endif + } else { + throwNew(e, "java/lang/OutOfMemoryError", 0); + return 0; + } +} + +#endif // !SGX \ No newline at end of file diff --git a/sgx-jvm/avian/classpath/java-nio.cpp b/sgx-jvm/avian/classpath/java-nio.cpp new file mode 100644 index 0000000000..e04adc2d70 --- /dev/null +++ b/sgx-jvm/avian/classpath/java-nio.cpp @@ -0,0 +1,1102 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef SGX + +#include +#include +#include +#include + +#include "jni.h" +#include "jni-util.h" + +#ifdef PLATFORM_WINDOWS +#include +#include +#include +#ifdef _MSC_VER +#define snprintf sprintf_s +#else +#include +#endif +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#define java_nio_channels_SelectionKey_OP_READ 1L +#define java_nio_channels_SelectionKey_OP_WRITE 4L +#define java_nio_channels_SelectionKey_OP_CONNECT 8L +#define java_nio_channels_SelectionKey_OP_ACCEPT 16L + +#ifdef PLATFORM_WINDOWS +typedef int socklen_t; +#endif + +inline void* operator new(size_t, void* p) throw() +{ + return p; +} + +namespace { + +inline jbyteArray charsToArray(JNIEnv* e, const char* s) +{ + unsigned length = strlen(s); + jbyteArray a = e->NewByteArray(length + 1); + e->SetByteArrayRegion(a, 0, length + 1, reinterpret_cast(s)); + return a; +} + +inline void doClose(int socket) +{ +#ifdef PLATFORM_WINDOWS + closesocket(socket); +#else + close(socket); +#endif +} + +inline jbyteArray errorString(JNIEnv* e, int n) +{ +#ifdef _MSC_VER + const unsigned size = 128; + char buffer[size]; + strerror_s(buffer, size, n); + return charsToArray(e, buffer); +#else + return charsToArray(e, strerror(n)); +#endif +} + +inline jbyteArray socketErrorString(JNIEnv* e, int n) +{ +#ifdef PLATFORM_WINDOWS + const unsigned size = 64; + char buffer[size]; + snprintf(buffer, size, "wsa code: %d", n); + return charsToArray(e, buffer); +#else + return errorString(e, n); +#endif +} + +inline jbyteArray errorString(JNIEnv* e) +{ +#ifdef PLATFORM_WINDOWS + const unsigned size = 64; + char buffer[size]; + snprintf(buffer, size, "wsa code: %d", WSAGetLastError()); + return charsToArray(e, buffer); +#else + return errorString(e, errno); +#endif +} + +void throwIOException(JNIEnv* e, const char* s) +{ + throwNew(e, "java/io/IOException", s); +} + +void throwIOException(JNIEnv* e, jbyteArray a) +{ + size_t length = e->GetArrayLength(a); + uint8_t* buf = static_cast(allocate(e, length)); + if (buf) { + e->GetByteArrayRegion(a, 0, length, reinterpret_cast(buf)); + throwIOException(e, reinterpret_cast(buf)); + free(buf); + } else { + return; + } +} + +void throwIOException(JNIEnv* e) +{ + throwIOException(e, errorString(e)); +} + +void throwSocketException(JNIEnv* e, const char* s) +{ + throwNew(e, "java/net/SocketException", s); +} + +void throwSocketException(JNIEnv* e, jbyteArray a) +{ + size_t length = e->GetArrayLength(a); + uint8_t* buf = static_cast(allocate(e, length)); + if (buf) { + e->GetByteArrayRegion(a, 0, length, reinterpret_cast(buf)); + throwSocketException(e, reinterpret_cast(buf)); + free(buf); + } else { + return; + } +} + +void throwSocketException(JNIEnv* e) +{ + throwSocketException(e, errorString(e)); +} + +void init(sockaddr_in* address, jint host, jint port) +{ + memset(address, 0, sizeof(sockaddr_in)); + address->sin_family = AF_INET; + address->sin_port = htons(port); + address->sin_addr.s_addr = htonl(host); +} + +inline bool einProgress(int error) +{ +#ifdef PLATFORM_WINDOWS + return error == WSAEINPROGRESS or error == WSAEWOULDBLOCK; +#else + return error == EINPROGRESS; +#endif +} + +inline bool einProgress() +{ +#ifdef PLATFORM_WINDOWS + return WSAGetLastError() == WSAEINPROGRESS + or WSAGetLastError() == WSAEWOULDBLOCK; +#else + return errno == EINPROGRESS; +#endif +} + +inline bool eagain() +{ +#ifdef PLATFORM_WINDOWS + return WSAGetLastError() == WSAEINPROGRESS + or WSAGetLastError() == WSAEWOULDBLOCK; +#else + return errno == EAGAIN; +#endif +} + +bool setBlocking(JNIEnv* e, int d, bool blocking) +{ +#ifdef PLATFORM_WINDOWS + u_long a = (blocking ? 0 : 1); + int r = ioctlsocket(d, FIONBIO, &a); + if (r != 0) { + throwIOException(e); + return false; + } +#else + int r = fcntl(d, + F_SETFL, + (blocking ? (fcntl(d, F_GETFL) & (~O_NONBLOCK)) + : (fcntl(d, F_GETFL) | O_NONBLOCK))); + if (r < 0) { + throwIOException(e); + return false; + } +#endif + return true; +} + +bool setTcpNoDelay(JNIEnv* e, int d, bool on) +{ + int flag = on; + int r = setsockopt( + d, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&flag), sizeof(int)); + if (r < 0) { + throwSocketException(e); + return false; + } + return true; +} + +void doBind(JNIEnv* e, int s, sockaddr_in* address) +{ + { + int opt = 1; + int r = ::setsockopt(s, + SOL_SOCKET, + SO_REUSEADDR, + reinterpret_cast(&opt), + sizeof(int)); + if (r != 0) { + throwIOException(e); + return; + } + } + +#ifdef SO_NOSIGPIPE + { + int opt = 1; + int r = ::setsockopt(s, + SOL_SOCKET, + SO_NOSIGPIPE, + reinterpret_cast(&opt), + sizeof(int)); + if (r != 0) { + throwIOException(e); + return; + } + } +#endif + + { + int r + = ::bind(s, reinterpret_cast(address), sizeof(sockaddr_in)); + if (r != 0) { + throwIOException(e); + return; + } + } +} + +void doListen(JNIEnv* e, int s) +{ + int r = ::listen(s, 100); + if (r != 0) { + throwIOException(e); + } +} + +void doFinishConnect(JNIEnv* e, int socket) +{ + int error; + socklen_t size = sizeof(int); + int r = getsockopt( + socket, SOL_SOCKET, SO_ERROR, reinterpret_cast(&error), &size); + + if (r != 0 or size != sizeof(int)) { + throwIOException(e); + } else if (error and not einProgress(error)) { + throwIOException(e, socketErrorString(e, error)); + } +} + +bool doConnect(JNIEnv* e, int s, sockaddr_in* address) +{ + int r + = ::connect(s, reinterpret_cast(address), sizeof(sockaddr_in)); + if (r == 0) { + return true; + } else if (not einProgress()) { + throwIOException(e); + return false; + } else { + return false; + } +} + +int doAccept(JNIEnv* e, int s) +{ + sockaddr address; + socklen_t length = sizeof(address); + int r = ::accept(s, &address, &length); + if (r >= 0) { + return r; + } else if (errno != EINTR) { + throwIOException(e); + } + return -1; +} + +int doRead(int fd, void* buffer, size_t count) +{ +#ifdef PLATFORM_WINDOWS + return recv(fd, static_cast(buffer), count, 0); +#else + return read(fd, buffer, count); +#endif +} + +int doRecv(int fd, void* buffer, size_t count, int32_t* host, int32_t* port) +{ + sockaddr address; + socklen_t length = sizeof(address); + int r = recvfrom(fd, static_cast(buffer), count, 0, &address, &length); + + if (r > 0) { + sockaddr_in a; + memcpy(&a, &address, length); + *host = ntohl(a.sin_addr.s_addr); + *port = ntohs(a.sin_port); + } else { + *host = 0; + *port = 0; + } + + return r; +} + +int doWrite(int fd, const void* buffer, size_t count) +{ +#ifdef PLATFORM_WINDOWS + return send(fd, static_cast(buffer), count, 0); +#else + return write(fd, buffer, count); +#endif +} + +int doSend(int fd, sockaddr_in* address, const void* buffer, size_t count) +{ + return sendto(fd, + static_cast(buffer), + count, + 0, + reinterpret_cast(address), + sizeof(sockaddr_in)); +} + +int makeSocket(JNIEnv* e, int type = SOCK_STREAM, int protocol = IPPROTO_TCP) +{ + int s = ::socket(AF_INET, type, protocol); + if (s < 0) { + throwIOException(e); + return s; + } + + return s; +} + +} // namespace + +extern "C" JNIEXPORT jint JNICALL + Java_java_nio_channels_ServerSocketChannel_natDoAccept(JNIEnv* e, + jclass, + jint socket) +{ + return ::doAccept(e, socket); +} + +extern "C" JNIEXPORT void JNICALL + Java_java_nio_channels_ServerSocketChannel_natDoListen(JNIEnv* e, + jclass, + jint socket, + jint host, + jint port) +{ + sockaddr_in address; + init(&address, host, port); + + ::doBind(e, socket, &address); + if (e->ExceptionCheck()) + return; + + ::doListen(e, socket); +} + +extern "C" JNIEXPORT void JNICALL + Java_java_nio_channels_SocketChannel_bind(JNIEnv* e, + jclass, + jint socket, + jint host, + jint port) +{ + sockaddr_in address; + init(&address, host, port); + + ::doBind(e, socket, &address); +} + +extern "C" JNIEXPORT void JNICALL + Java_java_nio_channels_DatagramChannel_bind(JNIEnv* e, + jclass c, + jint socket, + jint host, + jint port) +{ + Java_java_nio_channels_SocketChannel_bind(e, c, socket, host, port); +} + +extern "C" JNIEXPORT void JNICALL + Java_java_nio_channels_SocketChannel_configureBlocking(JNIEnv* e, + jclass, + jint socket, + jboolean blocking) +{ + setBlocking(e, socket, blocking); +} + +extern "C" JNIEXPORT void JNICALL + Java_java_nio_channels_DatagramChannel_configureBlocking(JNIEnv* e, + jclass c, + jint socket, + jboolean blocking) +{ + return Java_java_nio_channels_SocketChannel_configureBlocking( + e, c, socket, blocking); +} + +extern "C" JNIEXPORT void JNICALL + Java_java_nio_channels_SocketChannel_natSetTcpNoDelay(JNIEnv* e, + jclass, + jint socket, + jboolean on) +{ + setTcpNoDelay(e, socket, on); +} + +extern "C" JNIEXPORT jboolean JNICALL + Java_java_nio_channels_SocketChannel_natDoConnect(JNIEnv* e, + jclass, + jint socket, + jint host, + jint port) +{ + sockaddr_in address; + init(&address, host, port); + + return ::doConnect(e, socket, &address); +} + +extern "C" JNIEXPORT jint JNICALL + Java_java_nio_channels_SocketChannel_makeSocket(JNIEnv* e, jclass) +{ + return makeSocket(e); +} + +extern "C" JNIEXPORT jint JNICALL + Java_java_nio_channels_DatagramChannel_makeSocket(JNIEnv* e, jclass) +{ + return makeSocket(e, SOCK_DGRAM, IPPROTO_UDP); +} + +extern "C" JNIEXPORT jboolean JNICALL + Java_java_nio_channels_DatagramChannel_connect(JNIEnv* e, + jclass, + jint socket, + jint host, + jint port) +{ + sockaddr_in address; + init(&address, host, port); + + return ::doConnect(e, socket, &address); +} + +extern "C" JNIEXPORT void JNICALL + Java_java_nio_channels_SocketChannel_natFinishConnect(JNIEnv* e, + jclass, + jint socket) +{ + doFinishConnect(e, socket); +} + +extern "C" JNIEXPORT jint JNICALL + Java_java_nio_channels_SocketChannel_natRead(JNIEnv* e, + jclass, + jint socket, + jbyteArray buffer, + jint offset, + jint length, + jboolean blocking) +{ + int r; + if (blocking) { + uint8_t* buf = static_cast(allocate(e, length)); + if (buf) { + r = ::doRead(socket, buf, length); + if (r > 0) { + e->SetByteArrayRegion(buffer, offset, r, reinterpret_cast(buf)); + } + free(buf); + } else { + return 0; + } + } else { + jboolean isCopy; + uint8_t* buf + = static_cast(e->GetPrimitiveArrayCritical(buffer, &isCopy)); + + r = ::doRead(socket, buf + offset, length); + + e->ReleasePrimitiveArrayCritical(buffer, buf, 0); + } + + if (r < 0) { + if (eagain()) { + return 0; + } else { + throwIOException(e); + } + } else if (r == 0) { + return -1; + } + return r; +} + +extern "C" JNIEXPORT jint JNICALL + Java_java_nio_channels_DatagramChannel_receive(JNIEnv* e, + jclass, + jint socket, + jbyteArray buffer, + jint offset, + jint length, + jboolean blocking, + jintArray address) +{ + int r; + int32_t host; + int32_t port; + if (blocking) { + uint8_t* buf = static_cast(allocate(e, length)); + if (buf) { + r = ::doRecv(socket, buf, length, &host, &port); + if (r > 0) { + e->SetByteArrayRegion(buffer, offset, r, reinterpret_cast(buf)); + } + free(buf); + } else { + return 0; + } + } else { + jboolean isCopy; + uint8_t* buf + = static_cast(e->GetPrimitiveArrayCritical(buffer, &isCopy)); + + r = ::doRecv(socket, buf + offset, length, &host, &port); + + e->ReleasePrimitiveArrayCritical(buffer, buf, 0); + } + + if (r < 0) { + if (eagain()) { + return 0; + } else { + throwIOException(e); + } + } else if (r == 0) { + return -1; + } else { + jint jhost = host; + e->SetIntArrayRegion(address, 0, 1, &jhost); + jint jport = port; + e->SetIntArrayRegion(address, 1, 1, &jport); + } + + return r; +} + +extern "C" JNIEXPORT jint JNICALL + Java_java_nio_channels_SocketChannel_natWrite(JNIEnv* e, + jclass, + jint socket, + jbyteArray buffer, + jint offset, + jint length, + jboolean blocking) +{ + int r; + if (blocking) { + uint8_t* buf = static_cast(allocate(e, length)); + if (buf) { + e->GetByteArrayRegion( + buffer, offset, length, reinterpret_cast(buf)); + r = ::doWrite(socket, buf, length); + free(buf); + } else { + return 0; + } + } else { + jboolean isCopy; + uint8_t* buf + = static_cast(e->GetPrimitiveArrayCritical(buffer, &isCopy)); + + r = ::doWrite(socket, buf + offset, length); + + e->ReleasePrimitiveArrayCritical(buffer, buf, 0); + } + + if (r < 0) { + if (eagain()) { + return 0; + } else { + throwIOException(e); + } + } + return r; +} + +extern "C" JNIEXPORT jint JNICALL + Java_java_nio_channels_DatagramChannel_write(JNIEnv* e, + jclass c, + jint socket, + jbyteArray buffer, + jint offset, + jint length, + jboolean blocking) +{ + return Java_java_nio_channels_SocketChannel_natWrite( + e, c, socket, buffer, offset, length, blocking); +} + +extern "C" JNIEXPORT jint JNICALL + Java_java_nio_channels_DatagramChannel_send(JNIEnv* e, + jclass, + jint socket, + jint host, + jint port, + jbyteArray buffer, + jint offset, + jint length, + jboolean blocking) +{ + sockaddr_in address; + init(&address, host, port); + + int r; + if (blocking) { + uint8_t* buf = static_cast(allocate(e, length)); + if (buf) { + e->GetByteArrayRegion( + buffer, offset, length, reinterpret_cast(buf)); + r = ::doSend(socket, &address, buf, length); + free(buf); + } else { + return 0; + } + } else { + jboolean isCopy; + uint8_t* buf + = static_cast(e->GetPrimitiveArrayCritical(buffer, &isCopy)); + + r = ::doSend(socket, &address, buf + offset, length); + + e->ReleasePrimitiveArrayCritical(buffer, buf, 0); + } + + if (r < 0) { + if (eagain()) { + return 0; + } else { + throwIOException(e); + } + } + return r; +} + +extern "C" JNIEXPORT void JNICALL + Java_java_nio_channels_SocketChannel_natThrowWriteError(JNIEnv* e, + jclass, + jint socket) +{ + int error; + socklen_t size = sizeof(int); + int r = getsockopt( + socket, SOL_SOCKET, SO_ERROR, reinterpret_cast(&error), &size); + if (r != 0 or size != sizeof(int)) { + throwIOException(e); + } else if (error != 0) { + throwIOException(e, socketErrorString(e, error)); + } +} + +extern "C" JNIEXPORT void JNICALL + Java_java_nio_channels_SocketChannel_natCloseSocket(JNIEnv*, + jclass, + jint socket) +{ + doClose(socket); +} + +extern "C" JNIEXPORT void JNICALL + Java_java_nio_channels_DatagramChannel_close(JNIEnv*, jclass, jint socket) +{ + doClose(socket); +} + +namespace { + +class Pipe { + public: +#ifdef PLATFORM_WINDOWS + // The Windows socket API only accepts socket file descriptors, not + // pipe descriptors or others. Thus, to implement + // Selector.wakeup(), we make a socket connection via the loopback + // interface and use it as a pipe. + Pipe(JNIEnv* e) : connected_(false), listener_(-1), reader_(-1), writer_(-1) + { + sockaddr_in address; + address.sin_family = AF_INET; + address.sin_port = 0; + address.sin_addr.s_addr = inet_addr("127.0.0.1"); // INADDR_LOOPBACK; + + listener_ = makeSocket(e); + if (e->ExceptionCheck()) + return; + + setBlocking(e, listener_, false); + + ::doBind(e, listener_, &address); + if (e->ExceptionCheck()) + return; + + ::doListen(e, listener_); + if (e->ExceptionCheck()) + return; + + socklen_t length = sizeof(sockaddr_in); + int r = getsockname( + listener_, reinterpret_cast(&address), &length); + if (r) { + throwIOException(e); + return; + } + + writer_ = makeSocket(e); + if (e->ExceptionCheck()) + return; + + setBlocking(e, writer_, true); + connected_ = ::doConnect(e, writer_, &address); + } + + void dispose() + { + if (listener_ >= 0) + ::doClose(listener_); + if (reader_ >= 0) + ::doClose(reader_); + if (writer_ >= 0) + ::doClose(writer_); + } + + bool connected() + { + return connected_; + } + + void setConnected(bool v) + { + connected_ = v; + } + + int listener() + { + return listener_; + } + + void setListener(int v) + { + listener_ = v; + } + + int reader() + { + return reader_; + } + + void setReader(int v) + { + reader_ = v; + } + + int writer() + { + return writer_; + } + + private: + bool connected_; + int listener_; + int reader_; + int writer_; +#else + Pipe(JNIEnv* e) + { + if (::pipe(pipe) != 0) { + throwIOException(e); + return; + } + + if (setBlocking(e, pipe[0], false)) { + setBlocking(e, pipe[1], false); + } + + open_ = true; + } + + void dispose() + { + ::doClose(pipe[0]); + ::doClose(pipe[1]); + open_ = false; + } + + bool connected() + { + return open_; + } + + int reader() + { + return pipe[0]; + } + + int writer() + { + return pipe[1]; + } + + private: + int pipe[2]; + bool open_; +#endif +}; + +struct SelectorState { + fd_set read; + fd_set write; + fd_set except; + Pipe control; + SelectorState(JNIEnv* e) : control(e) + { + } +}; + +} // namespace + +extern "C" JNIEXPORT jlong JNICALL + Java_java_nio_channels_SocketSelector_natInit(JNIEnv* e, jclass) +{ + void* mem = malloc(sizeof(SelectorState)); + if (mem) { + SelectorState* s = new (mem) SelectorState(e); + if (e->ExceptionCheck()) + return 0; + + if (s) { + FD_ZERO(&(s->read)); + FD_ZERO(&(s->write)); + FD_ZERO(&(s->except)); + return reinterpret_cast(s); + } + } + throwNew(e, "java/lang/OutOfMemoryError", 0); + return 0; +} + +extern "C" JNIEXPORT void JNICALL + Java_java_nio_channels_SocketSelector_natWakeup(JNIEnv* e, + jclass, + jlong state) +{ + SelectorState* s = reinterpret_cast(state); + if (s->control.connected()) { + const char c = 1; + int r = ::doWrite(s->control.writer(), &c, 1); + if (r != 1) { + throwIOException(e); + } + } +} + +extern "C" JNIEXPORT void JNICALL + Java_java_nio_channels_SocketSelector_natClose(JNIEnv*, jclass, jlong state) +{ + SelectorState* s = reinterpret_cast(state); + s->control.dispose(); + free(s); +} + +extern "C" JNIEXPORT void JNICALL + Java_java_nio_channels_SocketSelector_natSelectClearAll(JNIEnv*, + jclass, + jint socket, + jlong state) +{ + SelectorState* s = reinterpret_cast(state); + FD_CLR(static_cast(socket), &(s->read)); + FD_CLR(static_cast(socket), &(s->write)); + FD_CLR(static_cast(socket), &(s->except)); +} + +extern "C" JNIEXPORT jint JNICALL + Java_java_nio_channels_SocketSelector_natSelectUpdateInterestSet( + JNIEnv*, + jclass, + jint socket, + jint interest, + jlong state, + jint max) +{ + SelectorState* s = reinterpret_cast(state); + if (interest & (java_nio_channels_SelectionKey_OP_READ + | java_nio_channels_SelectionKey_OP_ACCEPT)) { + FD_SET(static_cast(socket), &(s->read)); + if (max < socket) + max = socket; + } else { + FD_CLR(static_cast(socket), &(s->read)); + } + + if (interest & (java_nio_channels_SelectionKey_OP_WRITE + | java_nio_channels_SelectionKey_OP_CONNECT)) { + FD_SET(static_cast(socket), &(s->write)); + FD_SET(static_cast(socket), &(s->except)); + if (max < socket) + max = socket; + } else { + FD_CLR(static_cast(socket), &(s->write)); + } + return max; +} + +extern "C" JNIEXPORT jint JNICALL + Java_java_nio_channels_SocketSelector_natDoSocketSelect(JNIEnv* e, + jclass, + jlong state, + jint max, + jlong interval) +{ + SelectorState* s = reinterpret_cast(state); + if (s->control.reader() >= 0) { + int socket = s->control.reader(); + FD_SET(static_cast(socket), &(s->read)); + if (max < socket) + max = socket; + } + +#ifdef PLATFORM_WINDOWS + if (s->control.listener() >= 0) { + int socket = s->control.listener(); + FD_SET(static_cast(socket), &(s->read)); + if (max < socket) + max = socket; + } + + if (not s->control.connected()) { + int socket = s->control.writer(); + FD_SET(static_cast(socket), &(s->write)); + FD_SET(static_cast(socket), &(s->except)); + if (max < socket) + max = socket; + } +#endif + + timeval time; + if (interval > 0) { + time.tv_sec = interval / 1000; + time.tv_usec = (interval % 1000) * 1000; + } else if (interval < 0) { + time.tv_sec = 0; + time.tv_usec = 0; + } else { + time.tv_sec = 24 * 60 * 60 * 1000; + time.tv_usec = 0; + } + int r = ::select(max + 1, &(s->read), &(s->write), &(s->except), &time); + + if (r < 0) { + if (errno != EINTR) { + throwIOException(e); + return 0; + } + } + +#ifdef PLATFORM_WINDOWS + if (FD_ISSET(s->control.writer(), &(s->write)) + or FD_ISSET(s->control.writer(), &(s->except))) { + int socket = s->control.writer(); + FD_CLR(static_cast(socket), &(s->write)); + FD_CLR(static_cast(socket), &(s->except)); + + int error; + socklen_t size = sizeof(int); + int r = getsockopt( + socket, SOL_SOCKET, SO_ERROR, reinterpret_cast(&error), &size); + if (r != 0 or size != sizeof(int)) { + throwIOException(e); + } else if (error != 0) { + throwIOException(e, socketErrorString(e, error)); + } + s->control.setConnected(true); + } + + if (s->control.listener() >= 0 + and FD_ISSET(s->control.listener(), &(s->read))) { + FD_CLR(static_cast(s->control.listener()), &(s->read)); + + s->control.setReader(::doAccept(e, s->control.listener())); + s->control.setListener(-1); + } +#endif + + if (s->control.reader() >= 0 and FD_ISSET(s->control.reader(), &(s->read))) { + FD_CLR(static_cast(s->control.reader()), &(s->read)); + + char c; + int r = 1; + while (r == 1) { + r = ::doRead(s->control.reader(), &c, 1); + } + if (r < 0 and not eagain()) { + throwIOException(e); + } + } + + return r; +} + +extern "C" JNIEXPORT jint JNICALL + Java_java_nio_channels_SocketSelector_natUpdateReadySet(JNIEnv*, + jclass, + jint socket, + jint interest, + jlong state) +{ + SelectorState* s = reinterpret_cast(state); + jint ready = 0; + + if (FD_ISSET(socket, &(s->read))) { + if (interest & java_nio_channels_SelectionKey_OP_READ) { + ready |= java_nio_channels_SelectionKey_OP_READ; + } + + if (interest & java_nio_channels_SelectionKey_OP_ACCEPT) { + ready |= java_nio_channels_SelectionKey_OP_ACCEPT; + } + } + + if (FD_ISSET(socket, &(s->write)) or FD_ISSET(socket, &(s->except))) { + if (interest & java_nio_channels_SelectionKey_OP_WRITE) { + ready |= java_nio_channels_SelectionKey_OP_WRITE; + } + + if (interest & java_nio_channels_SelectionKey_OP_CONNECT) { + ready |= java_nio_channels_SelectionKey_OP_CONNECT; + } + } + + return ready; +} + +extern "C" JNIEXPORT jboolean JNICALL + Java_java_nio_ByteOrder_isNativeBigEndian(JNIEnv*, jclass) +{ + union { + uint32_t i; + char c[4]; + } u = {0x01020304}; + + if (u.c[0] == 1) + return JNI_TRUE; + return JNI_FALSE; +} + +#endif // !SGX \ No newline at end of file diff --git a/sgx-jvm/avian/classpath/java-util-zip.cpp b/sgx-jvm/avian/classpath/java-util-zip.cpp new file mode 100644 index 0000000000..0839d181e0 --- /dev/null +++ b/sgx-jvm/avian/classpath/java-util-zip.cpp @@ -0,0 +1,172 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "stdlib.h" +#include "string.h" +#include "avian/zlib-custom.h" + +#include "jni.h" +#include "jni-util.h" + +extern "C" JNIEXPORT jlong JNICALL + Java_java_util_zip_Inflater_make(JNIEnv* e, jclass, jboolean nowrap) +{ + z_stream* s = static_cast(malloc(sizeof(z_stream))); + if (s == 0) { + throwNew(e, "java/lang/OutOfMemoryError", 0); + return 0; + } + + memset(s, 0, sizeof(z_stream)); + + int r = inflateInit2(s, (nowrap ? -15 : 15)); + if (r != Z_OK) { + free(s); + throwNew(e, "java/lang/RuntimeException", zError(r)); + return 0; + } + + return reinterpret_cast(s); +} + +extern "C" JNIEXPORT void JNICALL + Java_java_util_zip_Inflater_dispose(JNIEnv*, jclass, jlong peer) +{ + z_stream* s = reinterpret_cast(peer); + inflateEnd(s); + free(s); +} + +extern "C" JNIEXPORT void JNICALL + Java_java_util_zip_Inflater_inflate(JNIEnv* e, + jclass, + jlong peer, + jbyteArray input, + jint inputOffset, + jint inputLength, + jbyteArray output, + jint outputOffset, + jint outputLength, + jintArray results) +{ + z_stream* s = reinterpret_cast(peer); + + jbyte* in = static_cast(malloc(inputLength)); + if (in == 0) { + throwNew(e, "java/lang/OutOfMemoryError", 0); + return; + } + + jbyte* out = static_cast(malloc(outputLength)); + if (out == 0) { + free(in); + throwNew(e, "java/lang/OutOfMemoryError", 0); + return; + } + + e->GetByteArrayRegion(input, inputOffset, inputLength, in); + + s->next_in = reinterpret_cast(in); + s->avail_in = inputLength; + s->next_out = reinterpret_cast(out); + s->avail_out = outputLength; + + int r = inflate(s, Z_SYNC_FLUSH); + jint resultArray[3] = {r, + static_cast(inputLength - s->avail_in), + static_cast(outputLength - s->avail_out)}; + + free(in); + + e->SetByteArrayRegion(output, outputOffset, resultArray[2], out); + free(out); + + e->SetIntArrayRegion(results, 0, 3, resultArray); +} + +extern "C" JNIEXPORT jlong JNICALL + Java_java_util_zip_Deflater_make(JNIEnv* e, + jclass, + jboolean nowrap, + jint level) +{ + z_stream* s = static_cast(malloc(sizeof(z_stream))); + if (s == 0) { + throwNew(e, "java/lang/OutOfMemoryError", 0); + return 0; + } + + memset(s, 0, sizeof(z_stream)); + + int r = deflateInit2(s, level, (nowrap ? -15 : 15)); + if (r != Z_OK) { + free(s); + throwNew(e, "java/lang/RuntimeException", zError(r)); + return 0; + } + + return reinterpret_cast(s); +} + +extern "C" JNIEXPORT void JNICALL + Java_java_util_zip_Deflater_dispose(JNIEnv*, jclass, jlong peer) +{ + z_stream* s = reinterpret_cast(peer); + deflateEnd(s); + free(s); +} + +extern "C" JNIEXPORT void JNICALL + Java_java_util_zip_Deflater_deflate(JNIEnv* e, + jclass, + jlong peer, + jbyteArray input, + jint inputOffset, + jint inputLength, + jbyteArray output, + jint outputOffset, + jint outputLength, + jboolean finish, + jintArray results) +{ + z_stream* s = reinterpret_cast(peer); + + jbyte* in = static_cast(malloc(inputLength)); + if (in == 0) { + throwNew(e, "java/lang/OutOfMemoryError", 0); + return; + } + + jbyte* out = static_cast(malloc(outputLength)); + if (out == 0) { + free(in); + throwNew(e, "java/lang/OutOfMemoryError", 0); + return; + } + + e->GetByteArrayRegion(input, inputOffset, inputLength, in); + + s->next_in = reinterpret_cast(in); + s->avail_in = inputLength; + s->next_out = reinterpret_cast(out); + s->avail_out = outputLength; + + int r = deflate(s, finish ? Z_FINISH : Z_NO_FLUSH); + jint resultArray[3] = {r, + static_cast(inputLength - s->avail_in), + static_cast(outputLength - s->avail_out)}; + + free(in); + + e->SetByteArrayRegion(output, outputOffset, resultArray[2], out); + free(out); + + e->SetIntArrayRegion(results, 0, 3, resultArray); +} diff --git a/sgx-jvm/avian/classpath/java-util.cpp b/sgx-jvm/avian/classpath/java-util.cpp new file mode 100644 index 0000000000..bb36df86f8 --- /dev/null +++ b/sgx-jvm/avian/classpath/java-util.cpp @@ -0,0 +1,58 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "time.h" +#include "jni.h" +#include "jni-util.h" + +namespace { + +#if (!defined PLATFORM_WINDOWS) || (defined _MSC_VER) + +void removeNewline(char* s) +{ + for (; s; ++s) { + if (*s == '\n') { + *s = 0; + break; + } + } +} + +#endif + +} // namespace + +extern "C" JNIEXPORT jstring JNICALL + Java_java_util_Date_toString(JNIEnv* e, jclass c UNUSED, jlong when) +{ + const unsigned BufferSize UNUSED = 27; + + time_t time = when / 1000; + +#ifdef PLATFORM_WINDOWS + e->MonitorEnter(c); +#ifdef _MSC_VER + char buffer[BufferSize]; + ctime_s(buffer, BufferSize, &time); + removeNewline(buffer); +#else + char* buffer = ctime(&time); +#endif + jstring r = e->NewStringUTF(buffer); + e->MonitorExit(c); + return r; +#else + char buffer[BufferSize]; + ctime_r(&time, buffer); + removeNewline(buffer); + return e->NewStringUTF(buffer); +#endif +} diff --git a/sgx-jvm/avian/classpath/java/io/BufferedInputStream.java b/sgx-jvm/avian/classpath/java/io/BufferedInputStream.java new file mode 100644 index 0000000000..25a7441304 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/BufferedInputStream.java @@ -0,0 +1,88 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ +package java.io; + +import java.io.IOException; +import java.io.InputStream; + +public class BufferedInputStream extends InputStream { + private final InputStream in; + private final byte[] buffer; + private int position; + private int limit; + + public BufferedInputStream(InputStream in, int size) { + this.in = in; + this.buffer = new byte[size]; + } + + public BufferedInputStream(InputStream in) { + this(in, 4096); + } + + private int fill() throws IOException { + position = 0; + limit = in.read(buffer); + + return limit; + } + + public int read() throws IOException { + if (position >= limit && fill() == -1) { + return -1; + } + + return buffer[position++] & 0xFF; + } + + public int read(byte[] b, int offset, int length) throws IOException { + int count = 0; + if (position >= limit && fill() == -1) { + return -1; + } + if (position < limit) { + int remaining = limit - position; + if (remaining > length) { + remaining = length; + } + + System.arraycopy(buffer, position, b, offset, remaining); + + count += remaining; + position += remaining; + offset += remaining; + length -= remaining; + } + while (length > 0 && in.available() > 0) + { + int c = in.read(b, offset, length); + if (c == -1) { + if (count == 0) { + count = -1; + } + break; + } else { + offset += c; + count += c; + length -= c; + } + } + return count; + } + + public int available() throws IOException { + return in.available() + (limit - position); + } + + public void close() throws IOException { + in.close(); + } +} + diff --git a/sgx-jvm/avian/classpath/java/io/BufferedOutputStream.java b/sgx-jvm/avian/classpath/java/io/BufferedOutputStream.java new file mode 100644 index 0000000000..165ebc2cca --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/BufferedOutputStream.java @@ -0,0 +1,61 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class BufferedOutputStream extends OutputStream { + private final OutputStream out; + private final byte[] buffer; + private int position; + + public BufferedOutputStream(OutputStream out, int size) { + this.out = out; + this.buffer = new byte[size]; + } + + public BufferedOutputStream(OutputStream out) { + this(out, 4096); + } + + private void drain() throws IOException { + if (position > 0) { + out.write(buffer, 0, position); + position = 0; + } + } + + public void write(int c) throws IOException { + if (position >= buffer.length) { + drain(); + } + + buffer[position++] = (byte) (c & 0xFF); + } + + public void write(byte[] b, int offset, int length) throws IOException { + if (length > buffer.length - position) { + drain(); + out.write(b, offset, length); + } else { + System.arraycopy(b, offset, buffer, position, length); + position += length; + } + } + + public void flush() throws IOException { + drain(); + out.flush(); + } + + public void close() throws IOException { + flush(); + out.close(); + } +} diff --git a/sgx-jvm/avian/classpath/java/io/BufferedReader.java b/sgx-jvm/avian/classpath/java/io/BufferedReader.java new file mode 100644 index 0000000000..8d5a69b884 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/BufferedReader.java @@ -0,0 +1,101 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class BufferedReader extends Reader { + private final Reader in; + private final char[] buffer; + private int position; + private int limit; + + public BufferedReader(Reader in, int bufferSize) { + this.in = in; + this.buffer = new char[bufferSize]; + } + + public BufferedReader(Reader in) { + this(in, 32); + } + + private void fill() throws IOException { + position = 0; + limit = in.read(buffer); + } + + public String readLine() throws IOException { + StringBuilder sb = new StringBuilder(); + while (true) { + if (position >= limit) { + fill(); + } + + if (position >= limit) { + return sb.length() == 0 ? null : sb.toString(); + } + + for (int i = position; i < limit; ++i) { + if(buffer[i] == '\r') { + sb.append(buffer, position, i - position); + position = i + 1; + if(i+1 < limit && buffer[i+1] == '\n') { + position = i + 2; + } + return sb.toString(); + } else if (buffer[i] == '\n') { + sb.append(buffer, position, i - position); + position = i + 1; + return sb.toString(); + } + } + sb.append(buffer, position, limit-position); + position = limit; + } + } + + public int read(char[] b, int offset, int length) throws IOException { + int count = 0; + + if (position >= limit && length < buffer.length) { + fill(); + } + + if (position < limit) { + int remaining = limit - position; + if (remaining > length) { + remaining = length; + } + + System.arraycopy(buffer, position, b, offset, remaining); + + count += remaining; + position += remaining; + offset += remaining; + length -= remaining; + } + + if (length > 0) { + int c = in.read(b, offset, length); + if (c == -1) { + if (count == 0) { + count = -1; + } + } else { + count += c; + } + } + + return count; + } + + public void close() throws IOException { + in.close(); + } +} diff --git a/sgx-jvm/avian/classpath/java/io/BufferedWriter.java b/sgx-jvm/avian/classpath/java/io/BufferedWriter.java new file mode 100644 index 0000000000..e1c4e71b18 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/BufferedWriter.java @@ -0,0 +1,53 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class BufferedWriter extends Writer { + private final Writer out; + private final char[] buffer; + private int position; + + public BufferedWriter(Writer out, int size) { + this.out = out; + this.buffer = new char[size]; + } + + public BufferedWriter(Writer out) { + this(out, 4096); + } + + private void drain() throws IOException { + if (position > 0) { + out.write(buffer, 0, position); + position = 0; + } + } + + public void write(char[] b, int offset, int length) throws IOException { + if (length > buffer.length - position) { + drain(); + out.write(b, offset, length); + } else { + System.arraycopy(b, offset, buffer, position, length); + position += length; + } + } + + public void flush() throws IOException { + drain(); + out.flush(); + } + + public void close() throws IOException { + flush(); + out.close(); + } +} diff --git a/sgx-jvm/avian/classpath/java/io/ByteArrayInputStream.java b/sgx-jvm/avian/classpath/java/io/ByteArrayInputStream.java new file mode 100644 index 0000000000..e5373b6a62 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/ByteArrayInputStream.java @@ -0,0 +1,55 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class ByteArrayInputStream extends InputStream { + private final byte[] array; + private int position; + private final int limit; + + public ByteArrayInputStream(byte[] array, int offset, int length) { + this.array = array; + position = offset; + this.limit = offset + length; + } + + public ByteArrayInputStream(byte[] array) { + this(array, 0, array.length); + } + + public int read() { + if (position < limit) { + return array[position++] & 0xff; + } else { + return -1; + } + } + + public int read(byte[] buffer, int offset, int bufferLength) { + if (bufferLength == 0) { + return 0; + } + if (position >= limit) { + return -1; + } + int remaining = limit-position; + if (remaining < bufferLength) { + bufferLength = remaining; + } + System.arraycopy(array, position, buffer, offset, bufferLength); + position += bufferLength; + return bufferLength; + } + + public int available() { + return limit - position; + } +} diff --git a/sgx-jvm/avian/classpath/java/io/ByteArrayOutputStream.java b/sgx-jvm/avian/classpath/java/io/ByteArrayOutputStream.java new file mode 100644 index 0000000000..96b0cd584e --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/ByteArrayOutputStream.java @@ -0,0 +1,148 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class ByteArrayOutputStream extends OutputStream { + private static final int BufferSize = 32; + + private Cell firstCell; + private Cell curCell; + private int length; + private byte[] buffer; + private int position; + + public ByteArrayOutputStream(int capacity) { } + + public ByteArrayOutputStream() { + this(0); + } + + public void reset() { + firstCell = null; + curCell = null; + length = 0; + buffer = null; + position = 0; + } + + public int size() { + return length; + } + + public void write(int c) { + if (buffer == null) { + buffer = new byte[BufferSize]; + } else if (position >= buffer.length) { + flushBuffer(); + buffer = new byte[BufferSize]; + } + + buffer[position++] = (byte) (c & 0xFF); + ++ length; + } + + private byte[] copy(byte[] b, int offset, int length) { + byte[] array = new byte[length]; + System.arraycopy(b, offset, array, 0, length); + return array; + } + + public void write(byte[] b, int offset, int length) { + if (b == null) { + throw new NullPointerException(); + } + + if (offset < 0 || offset + length > b.length) { + throw new ArrayIndexOutOfBoundsException(); + } + + if (length == 0) return; + + if (buffer != null && length <= buffer.length - position) { + System.arraycopy(b, offset, buffer, position, length); + position += length; + } else { + flushBuffer(); + chainCell( new Cell(copy(b, offset, length), 0, length) ); + } + + this.length += length; + } + + private void chainCell(Cell cell){ + if (curCell == null){ + firstCell = cell; + curCell = cell; + }else{ + curCell.next = cell; + curCell = cell; + } + } + + private void flushBuffer() { + if (position > 0) { + byte[] b = buffer; + int p = position; + buffer = null; + position = 0; + + chainCell( new Cell(b, 0, p) ); + } + } + + public byte[] toByteArray() { + flushBuffer(); + + byte[] array = new byte[length]; + int pos = 0; + for (Cell c = firstCell; c != null; c = c.next) { + System.arraycopy(c.array, c.offset, array, pos, c.length); + pos += c.length; + } + return array; + } + + public synchronized void writeTo(OutputStream out) throws IOException { + if (length==0) return; + + if (out == null){ + throw new NullPointerException(); + } + + flushBuffer(); + + for (Cell c = firstCell; c != null; c = c.next) { + out.write(c.array, c.offset, c.length); + } + } + + @Override + public String toString() { + return new String(toByteArray()); + } + + public String toString(String encoding) throws UnsupportedEncodingException { + return new String(toByteArray(), encoding); + } + + private static class Cell { + public byte[] array; + public int offset; + public int length; + public Cell next = null; + + public Cell(byte[] array, int offset, int length) { + this.array = array; + this.offset = offset; + this.length = length; + } + } +} diff --git a/sgx-jvm/avian/classpath/java/io/Closeable.java b/sgx-jvm/avian/classpath/java/io/Closeable.java new file mode 100644 index 0000000000..5a04d7db63 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/Closeable.java @@ -0,0 +1,16 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public interface Closeable extends AutoCloseable { + void close() + throws IOException; +} diff --git a/sgx-jvm/avian/classpath/java/io/DataInput.java b/sgx-jvm/avian/classpath/java/io/DataInput.java new file mode 100644 index 0000000000..4c9c25cf54 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/DataInput.java @@ -0,0 +1,29 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public interface DataInput { + boolean readBoolean() throws IOException; + byte readByte() throws IOException; + char readChar() throws IOException; + double readDouble() throws IOException; + float readFloat() throws IOException; + void readFully(byte[] b) throws IOException; + void readFully(byte[] b, int off, int len) throws IOException; + int readInt() throws IOException; + String readLine() throws IOException; + long readLong() throws IOException; + short readShort() throws IOException; + int readUnsignedByte() throws IOException; + int readUnsignedShort() throws IOException; + String readUTF() throws IOException; + int skipBytes(int n) throws IOException; +} diff --git a/sgx-jvm/avian/classpath/java/io/DataInputStream.java b/sgx-jvm/avian/classpath/java/io/DataInputStream.java new file mode 100644 index 0000000000..a80d93c5bc --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/DataInputStream.java @@ -0,0 +1,132 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class DataInputStream extends InputStream implements DataInput { + private InputStream in; + + public DataInputStream(InputStream in) { + this.in = in; + } + + public void close() throws IOException { + in.close(); + } + + public int read(byte[] buffer) throws IOException { + return in.read(buffer); + } + + public int read(byte[] buffer, int offset, int length) throws IOException { + return in.read(buffer, offset, length); + } + + public void readFully(byte[] b) throws IOException { + readFully(b, 0, b.length); + } + + public void readFully(byte[] b, int offset, int length) throws IOException { + while (length > 0) { + int count = read(b, offset, length); + if (count < 0) { + throw new EOFException("Reached EOF " + length + " bytes too early"); + } + offset += count; + length -= count; + } + } + + public int read() throws IOException { + return in.read(); + } + + private int read0() throws IOException { + int b = in.read(); + if (b < 0) { + throw new EOFException(); + } + return b; + } + + public boolean readBoolean() throws IOException { + return read0() != 0; + } + + public byte readByte() throws IOException { + return (byte)read0(); + } + + public short readShort() throws IOException { + return (short)((read0() << 8) | read0()); + } + + public int readInt() throws IOException { + return ((read0() << 24) | (read0() << 16) | (read0() << 8) | read0()); + } + + public float readFloat() throws IOException { + return Float.floatToIntBits(readInt()); + } + + public double readDouble() throws IOException { + return Double.doubleToLongBits(readLong()); + } + + public long readLong() throws IOException { + return ((readInt() & 0xffffffffl) << 32) | (readInt() & 0xffffffffl); + } + + public char readChar() throws IOException { + return (char)readShort(); + } + + public int readUnsignedByte() throws IOException { + return readByte() & 0xff; + } + + public int readUnsignedShort() throws IOException { + return readShort() & 0xffff; + } + + public String readUTF() throws IOException { + int length = readUnsignedShort(); + byte[] bytes = new byte[length]; + readFully(bytes); + return new String(bytes, "UTF-8"); + } + + @Deprecated + public String readLine() throws IOException { + int c = read(); + if (c < 0) { + return null; + } else if (c == '\n') { + return ""; + } + StringBuilder builder = new StringBuilder(); + for (;;) { + builder.append((char)c); + c = read(); + if (c < 0 || c == '\n') { + return builder.toString(); + } + } + } + + public int skipBytes(int n) throws IOException { + for (int count = 0; count < n; ++count) { + if (read() < 0) {; + return count; + } + } + return n; + } +} diff --git a/sgx-jvm/avian/classpath/java/io/DataOutput.java b/sgx-jvm/avian/classpath/java/io/DataOutput.java new file mode 100644 index 0000000000..9365e6cd73 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/DataOutput.java @@ -0,0 +1,28 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public interface DataOutput { + void write(byte[] b) throws IOException; + void write(byte[] b, int off, int len) throws IOException; + void write(int b) throws IOException; + void writeBoolean(boolean v) throws IOException; + void writeByte(int v) throws IOException; + void writeBytes(String s) throws IOException; + void writeChar(int v) throws IOException; + void writeChars(String s) throws IOException; + void writeDouble(double v) throws IOException; + void writeFloat(float v) throws IOException; + void writeInt(int v) throws IOException; + void writeLong(long v) throws IOException; + void writeShort(int v) throws IOException; + void writeUTF(String s) throws IOException; +} diff --git a/sgx-jvm/avian/classpath/java/io/DataOutputStream.java b/sgx-jvm/avian/classpath/java/io/DataOutputStream.java new file mode 100644 index 0000000000..0bec001a5f --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/DataOutputStream.java @@ -0,0 +1,99 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class DataOutputStream extends OutputStream implements DataOutput { + private OutputStream out; + + public DataOutputStream(OutputStream out) { + this.out = out; + } + + public void close() throws IOException { + out.close(); + } + + public void flush() throws IOException { + out.flush(); + } + + public void write(byte[] buffer) throws IOException { + out.write(buffer); + } + + public void write(byte[] buffer, int offset, int length) throws IOException { + out.write(buffer, offset, length); + } + + public void write(int b) throws IOException { + out.write(b); + } + + public void writeBoolean(boolean b) throws IOException { + writeByte(b ? 1 : 0); + } + + public void writeByte(int b) throws IOException { + out.write(b); + } + + public void writeShort(int s) throws IOException { + write((byte)(s >> 8)); + write((byte)s); + } + + public void writeInt(int i) throws IOException { + write((byte)(i >> 24)); + write((byte)(i >> 16)); + write((byte)(i >> 8)); + write((byte)i); + } + + public void writeFloat(float f) throws IOException { + writeInt(Float.floatToIntBits(f)); + } + + public void writeDouble(double d) throws IOException { + writeLong(Double.doubleToLongBits(d)); + } + + public void writeLong(long l) throws IOException { + write((byte)(l >> 56)); + write((byte)(l >> 48)); + write((byte)(l >> 40)); + write((byte)(l >> 32)); + write((byte)(l >> 24)); + write((byte)(l >> 16)); + write((byte)(l >> 8)); + write((byte)l); + } + + public void writeChar(int ch) throws IOException { + write((byte)(ch >> 8)); + write((byte)ch); + } + + public void writeChars(String s) throws IOException { + for (char ch : s.toCharArray()) { + writeChar(ch & 0xffff); + } + } + + public void writeBytes(String s) throws IOException { + out.write(s.getBytes()); + } + + public void writeUTF(String s) throws IOException { + byte[] bytes = s.getBytes("UTF-8"); + writeShort((short)bytes.length); + out.write(bytes); + } +} diff --git a/sgx-jvm/avian/classpath/java/io/EOFException.java b/sgx-jvm/avian/classpath/java/io/EOFException.java new file mode 100644 index 0000000000..78a9e995a2 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/EOFException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class EOFException extends IOException { + public EOFException(String message) { + super(message); + } + + public EOFException() { + this(null); + } +} diff --git a/sgx-jvm/avian/classpath/java/io/Externalizable.java b/sgx-jvm/avian/classpath/java/io/Externalizable.java new file mode 100644 index 0000000000..48351db912 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/Externalizable.java @@ -0,0 +1,16 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public interface Externalizable { + public void readExternal(ObjectInput in); + public void writeExternal(ObjectOutput out); +} diff --git a/sgx-jvm/avian/classpath/java/io/File.java b/sgx-jvm/avian/classpath/java/io/File.java new file mode 100644 index 0000000000..732f2d050f --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/File.java @@ -0,0 +1,335 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class File implements Serializable { + private static final String FileSeparator + = System.getProperty("file.separator"); + + private static final boolean IsWindows + = System.getProperty("os.name").equals("Windows"); + + public static final String separator = FileSeparator; + + public static final char separatorChar = FileSeparator.charAt(0); + + private static final String PathSeparator + = System.getProperty("path.separator"); + + public static final String pathSeparator = PathSeparator; + + public static final char pathSeparatorChar = PathSeparator.charAt(0); + + // static { + // System.loadLibrary("natives"); + // } + + private final String path; + + public File(String path) { + if (path == null) throw new NullPointerException(); + this.path = normalize(path); + } + + public File(String parent, String child) { + this(parent + FileSeparator + child); + } + + public File(File parent, String child) { + this(parent.getPath() + FileSeparator + child); + } + + public static File createTempFile(String prefix, String suffix) + throws IOException + { + return createTempFile(prefix, suffix, null); + } + + public static File createTempFile(String prefix, String suffix, + File directory) + throws IOException + { + if(directory == null) { + directory = new File(System.getProperty("java.io.tmpdir")); + } + if(suffix == null) { + suffix = ".tmp"; + } + File ret; + long state = System.currentTimeMillis(); + + do { + ret = generateFile(directory, prefix, state, suffix); + state *= 7; + state += 3; + } while(ret == null); + ret.createNewFile(); + return ret; + } + + private static File generateFile(File directory, String prefix, long state, String suffix) { + File ret = new File(directory, prefix + state + suffix); + if(ret.exists()) { + return null; + } else { + return ret; + } + } + + private static String stripSeparators(String p) { + while (p.endsWith(FileSeparator)) { + p = p.substring(0, p.length() - 1); + } + return p; + } + + private static String normalize(String path) { + if(IsWindows + && path.length() > 2 + && path.charAt(0) == '/' + && path.charAt(2) == ':') + { + // remove a leading slash on Windows + path = path.substring(1); + } + return stripSeparators + ("\\".equals(FileSeparator) ? path.replace('/', '\\') : path); + } + + public static native boolean rename(String old, String new_); + + public boolean renameTo(File newName) { + return rename(path, newName.path); + } + + private static native boolean isDirectory(String path); + + public boolean isDirectory() { + return isDirectory(path); + } + + private static native boolean isFile(String path); + + public boolean isFile() { + return isFile(path); + } + + public boolean isAbsolute() { + return path.equals(toAbsolutePath(path)); + } + + private static native boolean canRead(String path); + + public boolean canRead() { + return canRead(path); + } + + private static native boolean canWrite(String path); + + public boolean canWrite() { + return canWrite(path); + } + + private static native boolean canExecute(String path); + + public boolean canExecute() { + return canExecute(path); + } + + private static native boolean setExecutable(String path, boolean executable, boolean ownerOnly); + + public boolean setExecutable(boolean executable) { + return setExecutable(executable, true); + } + + public boolean setExecutable(boolean executable, boolean ownerOnly) { + return setExecutable(path, executable, ownerOnly); + } + + public String getName() { + int index = path.lastIndexOf(FileSeparator); + if (index >= 0) { + return path.substring(index + 1); + } else { + return path; + } + } + + public String toString() { + return getPath(); + } + + public String getPath() { + return path; + } + + public String getParent() { + int index = path.lastIndexOf(FileSeparator); + if (index >= 0) { + return normalize(path.substring(0, index)); + } else { + return null; + } + } + + public File getParentFile() { + String s = getParent(); + return (s == null ? null : new File(s)); + } + + private static native String toCanonicalPath(String path); + + public String getCanonicalPath() { + return toCanonicalPath(path); + } + + public File getCanonicalFile() { + return new File(getCanonicalPath()); + } + + private static native String toAbsolutePath(String path); + + public String getAbsolutePath() { + return toAbsolutePath(path); + } + + public File getAbsoluteFile() { + return new File(getAbsolutePath()); + } + + private static native long length(String path); + + public long length() { + return length(path); + } + + private static native boolean exists(String path); + + public boolean exists() { + return exists(path); + } + + private static native void mkdir(String path) throws IOException; + + public boolean mkdir() { + try { + mkdir(path); + return true; + } catch (IOException e) { + return false; + } + } + + private static native boolean createNewFile(String path) throws IOException; + + public boolean createNewFile() throws IOException { + return createNewFile(path); + } + + public static native void delete(String path) throws IOException; + + public boolean delete() { + try { + delete(path); + return true; + } catch (IOException e) { + return false; + } + } + + public boolean mkdirs() { + File parent = getParentFile(); + if (parent != null) { + if (!parent.exists()) { + if (!parent.mkdirs()) { + return false; + } + } + } + return mkdir(); + } + + public File[] listFiles() { + return listFiles(null); + } + + public File[] listFiles(FilenameFilter filter) { + String[] list = list(filter); + if (list != null) { + File[] result = new File[list.length]; + for (int i = 0; i < list.length; ++i) { + result[i] = new File(this, list[i]); + } + return result; + } else { + return null; + } + } + + public String[] list() { + return list(null); + } + + public String[] list(FilenameFilter filter) { + long handle = 0; + try { + handle = openDir(path); + if (handle != 0) { + Pair list = null; + int count = 0; + for (String s = readDir(handle); s != null; s = readDir(handle)) { + if (filter == null || filter.accept(this, s)) { + list = new Pair(s, list); + ++ count; + } + } + + String[] result = new String[count]; + for (int i = count - 1; i >= 0; --i) { + result[i] = list.value; + list = list.next; + } + + return result; + } else { + return null; + } + } finally { + if (handle != 0) { + closeDir(handle); + } + } + } + + public long lastModified() { + return lastModified(path); + } + private static native long openDir(String path); + + private static native long lastModified(String path); + + private static native String readDir(long handle); + + private static native void closeDir(long handle); + + + + private static class Pair { + public final String value; + public final Pair next; + + public Pair(String value, Pair next) { + this.value = value; + this.next = next; + } + } + +} diff --git a/sgx-jvm/avian/classpath/java/io/FileDescriptor.java b/sgx-jvm/avian/classpath/java/io/FileDescriptor.java new file mode 100644 index 0000000000..0c7d5f7201 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/FileDescriptor.java @@ -0,0 +1,27 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class FileDescriptor { + public static final FileDescriptor in = new FileDescriptor(0); + public static final FileDescriptor out = new FileDescriptor(1); + public static final FileDescriptor err = new FileDescriptor(2); + + final int value; + + public FileDescriptor(int value) { + this.value = value; + } + + public FileDescriptor() { + this(-1); + } +} diff --git a/sgx-jvm/avian/classpath/java/io/FileInputStream.java b/sgx-jvm/avian/classpath/java/io/FileInputStream.java new file mode 100644 index 0000000000..e2e7fa6e48 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/FileInputStream.java @@ -0,0 +1,77 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class FileInputStream extends InputStream { + // static { + // System.loadLibrary("natives"); + // } + + private int fd; + private int remaining; + + public FileInputStream(FileDescriptor fd) { + this.fd = fd.value; + } + + public FileInputStream(String path) throws IOException { + fd = open(path); + remaining = (int) new File(path).length(); + } + + public FileInputStream(File file) throws IOException { + this(file.getPath()); + } + + public int available() throws IOException { + return remaining; + } + + private static native int open(String path) throws IOException; + + private static native int read(int fd) throws IOException; + + private static native int read(int fd, byte[] b, int offset, int length) + throws IOException; + + public static native void close(int fd) throws IOException; + + public int read() throws IOException { + int c = read(fd); + if (c >= 0 && remaining > 0) { + -- remaining; + } + return c; + } + + public int read(byte[] b, int offset, int length) throws IOException { + if (b == null) { + throw new NullPointerException(); + } + + if (offset < 0 || offset + length > b.length) { + throw new ArrayIndexOutOfBoundsException(); + } + + int c = read(fd, b, offset, length); + if (c > 0 && remaining > 0) { + remaining -= c; + } + return c; + } + + public void close() throws IOException { + if (fd != -1) { + close(fd); + fd = -1; + } + } +} diff --git a/sgx-jvm/avian/classpath/java/io/FileNotFoundException.java b/sgx-jvm/avian/classpath/java/io/FileNotFoundException.java new file mode 100644 index 0000000000..114595f470 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/FileNotFoundException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class FileNotFoundException extends IOException { + public FileNotFoundException(String message) { + super(message); + } + + public FileNotFoundException() { + this(null); + } +} diff --git a/sgx-jvm/avian/classpath/java/io/FileOutputStream.java b/sgx-jvm/avian/classpath/java/io/FileOutputStream.java new file mode 100644 index 0000000000..8732004e72 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/FileOutputStream.java @@ -0,0 +1,68 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class FileOutputStream extends OutputStream { + // static { + // System.loadLibrary("natives"); + // } + + private int fd; + + public FileOutputStream(FileDescriptor fd) { + this.fd = fd.value; + } + + public FileOutputStream(String path) throws IOException { + this(path, false); + } + + public FileOutputStream(String path, boolean append) throws IOException { + fd = open(path, append); + } + + + public FileOutputStream(File file) throws IOException { + this(file.getPath()); + } + + private static native int open(String path, boolean append) throws IOException; + + private static native void write(int fd, int c) throws IOException; + + private static native void write(int fd, byte[] b, int offset, int length) + throws IOException; + + private static native void close(int fd) throws IOException; + + public void write(int c) throws IOException { + write(fd, c); + } + + public void write(byte[] b, int offset, int length) throws IOException { + if (b == null) { + throw new NullPointerException(); + } + + if (offset < 0 || offset + length > b.length) { + throw new ArrayIndexOutOfBoundsException(); + } + + write(fd, b, offset, length); + } + + public void close() throws IOException { + if (fd != -1) { + close(fd); + fd = -1; + } + } +} diff --git a/sgx-jvm/avian/classpath/java/io/FileReader.java b/sgx-jvm/avian/classpath/java/io/FileReader.java new file mode 100644 index 0000000000..fa484720ae --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/FileReader.java @@ -0,0 +1,39 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class FileReader extends Reader { + private final Reader in; + + public FileReader(FileInputStream in) { + this.in = new InputStreamReader(in); + } + + public FileReader(FileDescriptor fd) { + this(new FileInputStream(fd)); + } + + public FileReader(String path) throws IOException { + this(new FileInputStream(path)); + } + + public FileReader(File file) throws IOException { + this(new FileInputStream(file)); + } + + public int read(char[] b, int offset, int length) throws IOException { + return in.read(b, offset, length); + } + + public void close() throws IOException { + in.close(); + } +} diff --git a/sgx-jvm/avian/classpath/java/io/FileWriter.java b/sgx-jvm/avian/classpath/java/io/FileWriter.java new file mode 100644 index 0000000000..ca384b0af4 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/FileWriter.java @@ -0,0 +1,43 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class FileWriter extends Writer { + private final Writer out; + + private FileWriter(FileOutputStream out) { + this.out = new OutputStreamWriter(out); + } + + public FileWriter(FileDescriptor fd) { + this(new FileOutputStream(fd)); + } + + public FileWriter(String path) throws IOException { + this(new FileOutputStream(path)); + } + + public FileWriter(File file) throws IOException { + this(new FileOutputStream(file)); + } + + public void write(char[] b, int offset, int length) throws IOException { + out.write(b, offset, length); + } + + public void flush() throws IOException { + out.flush(); + } + + public void close() throws IOException { + out.close(); + } +} diff --git a/sgx-jvm/avian/classpath/java/io/FilenameFilter.java b/sgx-jvm/avian/classpath/java/io/FilenameFilter.java new file mode 100644 index 0000000000..8556e0ed8e --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/FilenameFilter.java @@ -0,0 +1,17 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public interface FilenameFilter { + + boolean accept(File dir, String name); + +} diff --git a/sgx-jvm/avian/classpath/java/io/FilterInputStream.java b/sgx-jvm/avian/classpath/java/io/FilterInputStream.java new file mode 100644 index 0000000000..76119ebb24 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/FilterInputStream.java @@ -0,0 +1,35 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class FilterInputStream extends InputStream { + protected InputStream in; + + public FilterInputStream(InputStream in) { + this.in = in; + } + + public void close() throws IOException { + in.close(); + } + + public int read(byte[] b) throws IOException { + return in.read(b); + } + + public int read(byte[] b, int off, int len) throws IOException { + return in.read(b, off, len); + } + + public int read() throws IOException { + return in.read(); + } +} diff --git a/sgx-jvm/avian/classpath/java/io/FilterOutputStream.java b/sgx-jvm/avian/classpath/java/io/FilterOutputStream.java new file mode 100644 index 0000000000..ebab3b7cb2 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/FilterOutputStream.java @@ -0,0 +1,40 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class FilterOutputStream extends OutputStream { + protected OutputStream out; + + public FilterOutputStream(OutputStream out) { + this.out = out; + } + + public void close() throws IOException { + out.close(); + } + + public void flush() throws IOException { + out.flush(); + } + + public void write(byte[] b) throws IOException { + out.write(b); + } + + public void write(byte[] b, int off, int len) throws IOException { + out.write(b, off, len); + } + + public void write(int b) throws IOException { + out.write(b); + } + +} diff --git a/sgx-jvm/avian/classpath/java/io/FilterReader.java b/sgx-jvm/avian/classpath/java/io/FilterReader.java new file mode 100644 index 0000000000..13c8af3f98 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/FilterReader.java @@ -0,0 +1,51 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public abstract class FilterReader extends Reader { + protected Reader in; + + protected FilterReader(Reader in) { + this.in = in; + } + + public int read() throws IOException { + return in.read(); + } + + public int read(char[] buffer, int offset, int length) throws IOException { + return in.read(buffer, offset, length); + } + + public boolean ready() throws IOException { + throw new UnsupportedOperationException(); + } + + public long skip(long n) throws IOException { + throw new UnsupportedOperationException(); + } + + public void close() throws IOException { + in.close(); + } + + public boolean markSupported() { + return in.markSupported(); + } + + public void mark(int readAheadLimit) throws IOException { + in.mark(readAheadLimit); + } + + public void reset() throws IOException { + in.reset(); + } +} diff --git a/sgx-jvm/avian/classpath/java/io/Flushable.java b/sgx-jvm/avian/classpath/java/io/Flushable.java new file mode 100644 index 0000000000..a3dd445c15 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/Flushable.java @@ -0,0 +1,16 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public interface Flushable { + void flush() + throws IOException; +} diff --git a/sgx-jvm/avian/classpath/java/io/IOException.java b/sgx-jvm/avian/classpath/java/io/IOException.java new file mode 100644 index 0000000000..6dc9d189f1 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/IOException.java @@ -0,0 +1,29 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class IOException extends Exception { + public IOException(String message, Throwable cause) { + super(message, cause); + } + + public IOException(String message) { + this(message, null); + } + + public IOException(Throwable cause) { + this(null, cause); + } + + public IOException() { + this(null, null); + } +} diff --git a/sgx-jvm/avian/classpath/java/io/InputStream.java b/sgx-jvm/avian/classpath/java/io/InputStream.java new file mode 100644 index 0000000000..8bac129287 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/InputStream.java @@ -0,0 +1,67 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public abstract class InputStream implements Closeable { + public abstract int read() throws IOException; + + public int read(byte[] buffer) throws IOException { + return read(buffer, 0, buffer.length); + } + + public int read(byte[] buffer, int offset, int length) throws IOException { + for (int i = 0; i < length; ++i) { + int c = read(); + if (c == -1) { + if (i == 0) { + return -1; + } else { + return i; + } + } else { + buffer[offset + i] = (byte) (c & 0xFF); + } + } + return length; + } + + public long skip(long count) throws IOException { + final long Max = 8 * 1024; + int size = (int) (count < Max ? count : Max); + byte[] buffer = new byte[size]; + long remaining = count; + int c; + while ((c = read(buffer, 0, (int) (size < remaining ? size : remaining))) + >= 0 + && remaining > 0) { + remaining -= c; + } + return count - remaining; + } + + public int available() throws IOException { + return 0; + } + + public void mark(int limit) { + // ignore + } + + public void reset() throws IOException { + throw new IOException("mark/reset not supported"); + } + + public boolean markSupported() { + return false; + } + + public void close() throws IOException { } +} diff --git a/sgx-jvm/avian/classpath/java/io/InputStreamReader.java b/sgx-jvm/avian/classpath/java/io/InputStreamReader.java new file mode 100644 index 0000000000..c7805a6f92 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/InputStreamReader.java @@ -0,0 +1,92 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +import avian.Utf8; + +public class InputStreamReader extends Reader { + private static final int MultibytePadding = 4; + + private final InputStream in; + + public InputStreamReader(InputStream in) { + this.in = in; + } + + public InputStreamReader(InputStream in, String encoding) + throws UnsupportedEncodingException + { + this(in); + + if (! encoding.equals("UTF-8")) { + throw new UnsupportedEncodingException(encoding); + } + } + + public int read(char[] b, int offset, int length) throws IOException { + if (length == 0) { + return 0; + } + + byte[] buffer = new byte[length + MultibytePadding]; + int bufferLength = length; + int bufferOffset = 0; + while (true) { + int c = in.read(buffer, bufferOffset, bufferLength); + + if (c <= 0) { + if (bufferOffset > 0) { + // if we've reached the end of the stream while trying to + // read a multibyte character, we still need to return any + // competely-decoded characters, plus \ufffd to indicate an + // unknown character + c = 1; + while (bufferOffset > 0) { + char[] buffer16 = Utf8.decode16(buffer, 0, bufferOffset); + + if (buffer16 != null) { + System.arraycopy(buffer16, 0, b, offset, buffer16.length); + + c = buffer16.length + 1; + break; + } else { + -- bufferOffset; + } + } + + b[offset + c - 1] = '\ufffd'; + } + + return c; + } + + bufferOffset += c; + + char[] buffer16 = Utf8.decode16(buffer, 0, bufferOffset); + + if (buffer16 != null) { + bufferOffset = 0; + + System.arraycopy(buffer16, 0, b, offset, buffer16.length); + + return buffer16.length; + } else { + // the buffer ended in an incomplete multibyte character, so + // we try to read a another byte at a time until it's complete + bufferLength = 1; + } + } + } + + public void close() throws IOException { + in.close(); + } +} diff --git a/sgx-jvm/avian/classpath/java/io/LineNumberReader.java b/sgx-jvm/avian/classpath/java/io/LineNumberReader.java new file mode 100644 index 0000000000..f8860f4987 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/LineNumberReader.java @@ -0,0 +1,41 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class LineNumberReader extends BufferedReader { + private int line; + + public LineNumberReader(Reader in, int bufferSize) { + super(in, bufferSize); + } + + public LineNumberReader(Reader in) { + super(in); + } + + public int getLineNumber() { + return line; + } + + public void setLineNumber(int v) { + line = v; + } + + public int read(char[] b, int offset, int length) throws IOException { + int c = super.read(b, offset, length); + for (int i = 0; i < c; ++i) { + if (b[i] == '\n') { + ++ line; + } + } + return c; + } +} diff --git a/sgx-jvm/avian/classpath/java/io/NotSerializableException.java b/sgx-jvm/avian/classpath/java/io/NotSerializableException.java new file mode 100644 index 0000000000..d34397817a --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/NotSerializableException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class NotSerializableException extends ObjectStreamException { + public NotSerializableException(String message) { + super(message); + } + + public NotSerializableException() { + this(null); + } +} diff --git a/sgx-jvm/avian/classpath/java/io/ObjectInput.java b/sgx-jvm/avian/classpath/java/io/ObjectInput.java new file mode 100644 index 0000000000..640a4c16f6 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/ObjectInput.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public interface ObjectInput { + public int available(); + public void close(); + public void read(); + public void read(byte[] b); + public void read(byte[] b, int off, int len); + public Object readObject(); + public long skip(long n); +} diff --git a/sgx-jvm/avian/classpath/java/io/ObjectInputStream.java b/sgx-jvm/avian/classpath/java/io/ObjectInputStream.java new file mode 100644 index 0000000000..66677141de --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/ObjectInputStream.java @@ -0,0 +1,443 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +import static java.io.ObjectOutputStream.STREAM_MAGIC; +import static java.io.ObjectOutputStream.STREAM_VERSION; +import static java.io.ObjectOutputStream.TC_NULL; +import static java.io.ObjectOutputStream.TC_REFERENCE; +import static java.io.ObjectOutputStream.TC_CLASSDESC; +import static java.io.ObjectOutputStream.TC_OBJECT; +import static java.io.ObjectOutputStream.TC_STRING; +import static java.io.ObjectOutputStream.TC_ARRAY; +import static java.io.ObjectOutputStream.TC_CLASS; +import static java.io.ObjectOutputStream.TC_BLOCKDATA; +import static java.io.ObjectOutputStream.TC_ENDBLOCKDATA; +import static java.io.ObjectOutputStream.TC_RESET; +import static java.io.ObjectOutputStream.TC_BLOCKDATALONG; +import static java.io.ObjectOutputStream.TC_EXCEPTION; +import static java.io.ObjectOutputStream.TC_LONGSTRING; +import static java.io.ObjectOutputStream.TC_PROXYCLASSDESC; +import static java.io.ObjectOutputStream.TC_ENUM; +import static java.io.ObjectOutputStream.SC_WRITE_METHOD; +import static java.io.ObjectOutputStream.SC_BLOCK_DATA; +import static java.io.ObjectOutputStream.SC_SERIALIZABLE; +import static java.io.ObjectOutputStream.SC_EXTERNALIZABLE; +import static java.io.ObjectOutputStream.SC_ENUM; +import static java.io.ObjectOutputStream.getReadOrWriteMethod; + +import avian.VMClass; + +import java.util.ArrayList; +import java.util.HashMap; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +public class ObjectInputStream extends InputStream implements DataInput { + private final static int HANDLE_OFFSET = 0x7e0000; + + private final InputStream in; + private final ArrayList references; + + public ObjectInputStream(InputStream in) throws IOException { + this.in = in; + short signature = (short)rawShort(); + if (signature != STREAM_MAGIC) { + throw new IOException("Unrecognized signature: 0x" + + Integer.toHexString(signature)); + } + int version = rawShort(); + if (version != STREAM_VERSION) { + throw new IOException("Unsupported version: " + version); + } + references = new ArrayList(); + } + + public int read() throws IOException { + return in.read(); + } + + private int rawByte() throws IOException { + int c = read(); + if (c < 0) { + throw new EOFException(); + } + return c; + } + + private int rawShort() throws IOException { + return (rawByte() << 8) | rawByte(); + } + + private int rawInt() throws IOException { + return (rawShort() << 16) | rawShort(); + } + + private long rawLong() throws IOException { + return ((rawInt() & 0xffffffffl) << 32) | rawInt(); + } + + private String rawString() throws IOException { + int length = rawShort(); + byte[] array = new byte[length]; + readFully(array); + return new String(array); + } + + public int read(byte[] b, int offset, int length) throws IOException { + return in.read(b, offset, length); + } + + public void readFully(byte[] b) throws IOException { + readFully(b, 0, b.length); + } + + public void readFully(byte[] b, int offset, int length) throws IOException { + while (length > 0) { + int count = read(b, offset, length); + if (count < 0) { + throw new EOFException("Reached EOF " + length + " bytes too early"); + } + offset += count; + length -= count; + } + } + + public String readLine() throws IOException { + int c = read(); + if (c < 0) { + return null; + } else if (c == '\n') { + return ""; + } + StringBuilder builder = new StringBuilder(); + for (;;) { + builder.append((char)c); + c = read(); + if (c < 0 || c == '\n') { + return builder.toString(); + } + } + } + + public void close() throws IOException { + in.close(); + } + + private int remainingBlockData; + + private int rawBlockDataByte() throws IOException { + while (remainingBlockData <= 0) { + int b = rawByte(); + if (b == TC_BLOCKDATA) { + remainingBlockData = rawByte(); + } else { + throw new UnsupportedOperationException("Unknown token: 0x" + + Integer.toHexString(b)); + } + } + --remainingBlockData; + return rawByte(); + } + + private int rawBlockDataShort() throws IOException { + return (rawBlockDataByte() << 8) | rawBlockDataByte(); + } + + private int rawBlockDataInt() throws IOException { + return (rawBlockDataShort() << 16) | rawBlockDataShort(); + } + + private long rawBlockDataLong() throws IOException { + return ((rawBlockDataInt() & 0xffffffffl) << 32) | rawBlockDataInt(); + } + + public boolean readBoolean() throws IOException { + return rawBlockDataByte() != 0; + } + + public byte readByte() throws IOException { + return (byte)rawBlockDataByte(); + } + + public char readChar() throws IOException { + return (char)rawBlockDataShort(); + } + + public short readShort() throws IOException { + return (short)rawBlockDataShort(); + } + + public int readInt() throws IOException { + return rawBlockDataInt(); + } + + public long readLong() throws IOException { + return rawBlockDataLong(); + } + + public float readFloat() throws IOException { + return Float.intBitsToFloat(rawBlockDataInt()); + } + + public double readDouble() throws IOException { + return Double.longBitsToDouble(rawBlockDataLong()); + } + + public int readUnsignedByte() throws IOException { + return rawBlockDataByte(); + } + + public int readUnsignedShort() throws IOException { + return rawBlockDataShort(); + } + + public String readUTF() throws IOException { + int length = rawBlockDataShort(); + if (remainingBlockData < length) { + throw new IOException("Short block data: " + + remainingBlockData + " < " + length); + } + byte[] bytes = new byte[length]; + readFully(bytes); + remainingBlockData -= length; + return new String(bytes, "UTF-8"); + } + + public int skipBytes(int count) throws IOException { + int i = 0; + while (i < count) { + if (read() < 0) { + return i; + } + ++i; + } + return count; + } + + private static Class charToPrimitiveType(int c) { + if (c == 'B') { + return Byte.TYPE; + } else if (c == 'C') { + return Character.TYPE; + } else if (c == 'D') { + return Double.TYPE; + } else if (c == 'F') { + return Float.TYPE; + } else if (c == 'I') { + return Integer.TYPE; + } else if (c == 'J') { + return Long.TYPE; + } else if (c == 'S') { + return Short.TYPE; + } else if (c == 'Z') { + return Boolean.TYPE; + } + throw new RuntimeException("Unhandled char: " + (char)c); + } + + private void expectToken(int token) throws IOException { + int c = rawByte(); + if (c != token) { + throw new UnsupportedOperationException("Unexpected token: 0x" + + Integer.toHexString(c)); + } + } + + private void field(Field field, Object o) + throws IOException, IllegalArgumentException, IllegalAccessException, + ClassNotFoundException + { + Class type = field.getType(); + if (!type.isPrimitive()) { + field.set(o, readObject()); + } else { + if (type == Byte.TYPE) { + field.setByte(o, (byte)rawByte()); + } else if (type == Character.TYPE) { + field.setChar(o, (char)rawShort()); + } else if (type == Double.TYPE) { + field.setDouble(o, Double.longBitsToDouble(rawLong())); + } else if (type == Float.TYPE) { + field.setFloat(o, Float.intBitsToFloat(rawInt())); + } else if (type == Integer.TYPE) { + field.setInt(o, rawInt()); + } else if (type == Long.TYPE) { + field.setLong(o, rawLong()); + } else if (type == Short.TYPE) { + field.setShort(o, (short)rawShort()); + } else if (type == Boolean.TYPE) { + field.setBoolean(o, rawByte() != 0); + } else { + throw new IOException("Unhandled type: " + type); + } + } + } + + public Object readObject() throws IOException, ClassNotFoundException { + int c = rawByte(); + if (c == TC_NULL) { + return null; + } + if (c == TC_STRING) { + int length = rawShort(); + byte[] bytes = new byte[length]; + readFully(bytes); + String s = new String(bytes, "UTF-8"); + references.add(s); + return s; + } + if (c == TC_REFERENCE) { + int handle = rawInt(); + return references.get(handle - HANDLE_OFFSET); + } + if (c != TC_OBJECT) { + throw new IOException("Unexpected token: 0x" + + Integer.toHexString(c)); + } + + // class desc + c = rawByte(); + ClassDesc classDesc; + if (c == TC_REFERENCE) { + int handle = rawInt() - HANDLE_OFFSET; + classDesc = (ClassDesc)references.get(handle); + } else if (c == TC_CLASSDESC) { + classDesc = classDesc(); + } else { + throw new UnsupportedOperationException("Unexpected token: 0x" + + Integer.toHexString(c)); + } + + try { + Object o = makeInstance(classDesc.clazz.vmClass); + references.add(o); + + do { + Object o1 = classDesc.clazz.cast(o); + boolean customized = (classDesc.flags & SC_WRITE_METHOD) != 0; + Method readMethod = customized ? + getReadOrWriteMethod(o, "readObject") : null; + if (readMethod == null) { + if (customized) { + throw new IOException("Could not find required readObject method " + + "in " + classDesc.clazz); + } + defaultReadObject(o, classDesc.fields); + } else { + current = o1; + currentFields = classDesc.fields; + readMethod.invoke(o, this); + current = null; + currentFields = null; + expectToken(TC_ENDBLOCKDATA); + } + } while ((classDesc = classDesc.superClassDesc) != null); + + return o; + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOException(e); + } + } + + private static class ClassDesc { + Class clazz; + int flags; + Field[] fields; + ClassDesc superClassDesc; + } + + private ClassDesc classDesc() throws ClassNotFoundException, IOException { + ClassDesc result = new ClassDesc(); + String className = rawString(); + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + result.clazz = loader.loadClass(className); + long serialVersionUID = rawLong(); + try { + Field field = result.clazz.getField("serialVersionUID"); + long expected = field.getLong(null); + if (expected != serialVersionUID) { + throw new IOException("Incompatible serial version UID: 0x" + + Long.toHexString(serialVersionUID) + " != 0x" + + Long.toHexString(expected)); + } + } catch (Exception ignored) { } + references.add(result); + + result.flags = rawByte(); + if ((result.flags & ~(SC_SERIALIZABLE | SC_WRITE_METHOD)) != 0) { + throw new UnsupportedOperationException("Cannot handle flags: 0x" + + Integer.toHexString(result.flags)); + } + + int fieldCount = rawShort(); + result.fields = new Field[fieldCount]; + for (int i = 0; i < result.fields.length; i++) { + int typeChar = rawByte(); + String fieldName = rawString(); + try { + result.fields[i] = result.clazz.getDeclaredField(fieldName); + } catch (Exception e) { + throw new IOException(e); + } + Class type; + if (typeChar == '[' || typeChar == 'L') { + String typeName = (String)readObject(); + if (typeName.startsWith("L") && typeName.endsWith(";")) { + typeName = typeName.substring(1, typeName.length() - 1) + .replace('/', '.'); + } + type = loader.loadClass(typeName); + } else { + type = charToPrimitiveType(typeChar); + } + if (result.fields[i].getType() != type) { + throw new IOException("Unexpected type of field " + fieldName + + ": expected " + result.fields[i].getType() + " but got " + type); + } + } + expectToken(TC_ENDBLOCKDATA); + int c = rawByte(); + if (c == TC_CLASSDESC) { + result.superClassDesc = classDesc(); + } else if (c != TC_NULL) { + throw new UnsupportedOperationException("Unexpected token: 0x" + + Integer.toHexString(c)); + } + + return result; + } + + private Object current; + private Field[] currentFields; + + public void defaultReadObject() throws IOException { + defaultReadObject(current, currentFields); + } + + private void defaultReadObject(Object o, Field[] fields) throws IOException { + try { + for (Field field : fields) { + field(field, o); + } + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOException(e); + } + } + + private static native Object makeInstance(VMClass c); +} diff --git a/sgx-jvm/avian/classpath/java/io/ObjectOutput.java b/sgx-jvm/avian/classpath/java/io/ObjectOutput.java new file mode 100644 index 0000000000..a93eec2bc4 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/ObjectOutput.java @@ -0,0 +1,20 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public interface ObjectOutput { + public void close(); + public void flush(); + public void write(byte[] b); + public void write(byte[] b, int off, int len); + public void write(int b); + public void writeObject(Object obj); +} diff --git a/sgx-jvm/avian/classpath/java/io/ObjectOutputStream.java b/sgx-jvm/avian/classpath/java/io/ObjectOutputStream.java new file mode 100644 index 0000000000..0ed12a2b76 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/ObjectOutputStream.java @@ -0,0 +1,337 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +import java.util.ArrayList; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +public class ObjectOutputStream extends OutputStream implements DataOutput { + final static short STREAM_MAGIC = (short)0xaced; + final static short STREAM_VERSION = 5; + final static byte TC_NULL = (byte)0x70; + final static byte TC_REFERENCE = (byte)0x71; + final static byte TC_CLASSDESC = (byte)0x72; + final static byte TC_OBJECT = (byte)0x73; + final static byte TC_STRING = (byte)0x74; + final static byte TC_ARRAY = (byte)0x75; + final static byte TC_CLASS = (byte)0x76; + final static byte TC_BLOCKDATA = (byte)0x77; + final static byte TC_ENDBLOCKDATA = (byte)0x78; + final static byte TC_RESET = (byte)0x79; + final static byte TC_BLOCKDATALONG = (byte)0x7a; + final static byte TC_EXCEPTION = (byte)0x7b; + final static byte TC_LONGSTRING = (byte)0x7c; + final static byte TC_PROXYCLASSDESC = (byte)0x7d; + final static byte TC_ENUM = (byte)0x7e; + final static byte SC_WRITE_METHOD = 0x01; //if SC_SERIALIZABLE + final static byte SC_BLOCK_DATA = 0x08; //if SC_EXTERNALIZABLE + final static byte SC_SERIALIZABLE = 0x02; + final static byte SC_EXTERNALIZABLE = 0x04; + final static byte SC_ENUM = 0x10; + + private final OutputStream out; + + public ObjectOutputStream(OutputStream out) throws IOException { + this.out = out; + rawShort(STREAM_MAGIC); + rawShort(STREAM_VERSION); + } + + public void write(int c) throws IOException { + out.write(c); + } + + public void write(byte[] b, int offset, int length) throws IOException { + out.write(b, offset, length); + } + + public void flush() throws IOException { + out.flush(); + } + + public void close() throws IOException { + out.close(); + } + + private void rawByte(int v) throws IOException { + out.write((byte)(v & 0xff)); + } + + private void rawShort(int v) throws IOException { + rawByte(v >> 8); + rawByte(v); + } + + private void rawInt(int v) throws IOException { + rawShort(v >> 16); + rawShort(v); + } + + private void rawLong(long v) throws IOException { + rawInt((int)(v >> 32)); + rawInt((int)(v & 0xffffffffl)); + } + + private void blockData(int... bytes) throws IOException { + blockData(bytes, null, null); + } + + private void blockData(int[] bytes, byte[] bytes2, char[] chars) throws IOException { + int count = (bytes == null ? 0 : bytes.length) + + (bytes2 == null ? 0 : bytes2.length) + + (chars == null ? 0 : chars.length * 2); + if (count < 0x100) { + rawByte(TC_BLOCKDATA); + rawByte(count); + } else { + rawByte(TC_BLOCKDATALONG); + rawInt(count); + } + if (bytes != null) { + for (int b : bytes) { + rawByte(b); + } + } + if (bytes2 != null) { + for (byte b : bytes2) { + rawByte(b & 0xff); + } + } + if (chars != null) { + for (char c : chars) { + rawShort((short)c); + } + } + } + + public void writeBoolean(boolean v) throws IOException { + blockData(v ? 1 : 0); + } + + public void writeByte(int v) throws IOException { + blockData(v); + } + + public void writeShort(int v) throws IOException { + blockData(v >> 8, v); + } + + public void writeChar(int v) throws IOException { + blockData(v >> 8, v); + } + + public void writeInt(int v) throws IOException { + blockData(v >> 24, v >> 16, v >> 8, v); + } + + public void writeLong(long v) throws IOException { + int u = (int)(v >> 32), l = (int)(v & 0xffffffff); + blockData(u >> 24, u >> 16, u >> 8, u, l >> 24, l >> 16, l >> 8, l); + } + + public void writeFloat(float v) throws IOException { + writeInt(Float.floatToIntBits(v)); + } + + public void writeDouble(double v) throws IOException { + writeLong(Double.doubleToLongBits(v)); + } + + public void writeBytes(String s) throws IOException { + blockData(null, s.getBytes(), null); + } + + public void writeChars(String s) throws IOException { + blockData(null, null, s.toCharArray()); + } + + public void writeUTF(String s) throws IOException { + byte[] bytes = s.getBytes(); + int length = bytes.length; + blockData(new int[] { length >> 8, length }, bytes, null); + } + + private int classHandle; + + private void string(String s) throws IOException { + int length = s.length(); + rawShort(length); + for (byte b : s.getBytes()) { + rawByte(b); + } + } + + private static char primitiveTypeChar(Class type) { + if (type == Byte.TYPE) { + return 'B'; + } else if (type == Character.TYPE) { + return 'C'; + } else if (type == Double.TYPE) { + return 'D'; + } else if (type == Float.TYPE) { + return 'F'; + } else if (type == Integer.TYPE) { + return 'I'; + } else if (type == Long.TYPE) { + return 'J'; + } else if (type == Short.TYPE) { + return 'S'; + } else if (type == Boolean.TYPE) { + return 'Z'; + } + throw new RuntimeException("Unhandled primitive type: " + type); + } + + private void classDesc(Class clazz, int scFlags) throws IOException { + rawByte(TC_CLASSDESC); + + // class name + string(clazz.getName()); + + // serial version UID + long serialVersionUID = 1l; + try { + Field field = clazz.getField("serialVersionUID"); + serialVersionUID = field.getLong(null); + } catch (Exception ignored) {} + rawLong(serialVersionUID); + + // handle + rawByte(SC_SERIALIZABLE | scFlags); + + Field[] fields = getFields(clazz); + rawShort(fields.length); + for (Field field : fields) { + Class fieldType = field.getType(); + if (fieldType.isPrimitive()) { + rawByte(primitiveTypeChar(fieldType)); + string(field.getName()); + } else { + rawByte(fieldType.isArray() ? '[' : 'L'); + string(field.getName()); + rawByte(TC_STRING); + string("L" + fieldType.getName().replace('.', '/') + ";"); + } + } + rawByte(TC_ENDBLOCKDATA); // TODO: write annotation + rawByte(TC_NULL); // super class desc + } + + private void field(Object o, Field field) throws IOException { + try { + field.setAccessible(true); + Class type = field.getType(); + if (!type.isPrimitive()) { + writeObject(field.get(o)); + } else if (type == Byte.TYPE) { + rawByte(field.getByte(o)); + } else if (type == Character.TYPE) { + char c = field.getChar(o); + rawShort((short)c); + } else if (type == Double.TYPE) { + double d = field.getDouble(o); + rawLong(Double.doubleToLongBits(d)); + } else if (type == Float.TYPE) { + float f = field.getFloat(o); + rawInt(Float.floatToIntBits(f)); + } else if (type == Integer.TYPE) { + int i = field.getInt(o); + rawInt(i); + } else if (type == Long.TYPE) { + long l = field.getLong(o); + rawLong(l); + } else if (type == Short.TYPE) { + short s = field.getShort(o); + rawShort(s); + } else if (type == Boolean.TYPE) { + boolean b = field.getBoolean(o); + rawByte(b ? 1 : 0); + } else { + throw new UnsupportedOperationException("Field '" + field.getName() + + "' has unsupported type: " + type); + } + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOException(e); + } + } + + private static Field[] getFields(Class clazz) { + ArrayList list = new ArrayList(); + Field[] fields = clazz.getDeclaredFields(); + for (Field field : fields) { + if (0 == (field.getModifiers() & + (Modifier.STATIC | Modifier.TRANSIENT))) { + list.add(field); + } + } + return list.toArray(new Field[list.size()]); + } + + public void writeObject(Object o) throws IOException { + if (o == null) { + rawByte(TC_NULL); + return; + } + if (o instanceof String) { + byte[] bytes = ((String)o).getBytes("UTF-8"); + rawByte(TC_STRING); + rawShort(bytes.length); + write(bytes); + return; + } + rawByte(TC_OBJECT); + Method writeObject = getReadOrWriteMethod(o, "writeObject"); + if (writeObject == null) { + classDesc(o.getClass(), 0); + defaultWriteObject(o); + } else try { + classDesc(o.getClass(), SC_WRITE_METHOD); + current = o; + writeObject.invoke(o, this); + current = null; + rawByte(TC_ENDBLOCKDATA); + } catch (Exception e) { + throw new IOException(e); + } + } + + static Method getReadOrWriteMethod(Object o, String methodName) { + try { + Method method = o.getClass().getDeclaredMethod(methodName, + new Class[] { methodName.startsWith("write") ? + ObjectOutputStream.class : ObjectInputStream.class }); + method.setAccessible(true); + int modifiers = method.getModifiers(); + if ((modifiers & Modifier.STATIC) == 0 || + (modifiers & Modifier.PRIVATE) != 0) { + return method; + } + } catch (NoSuchMethodException ignored) { } + return null; + } + + private Object current; + + public void defaultWriteObject() throws IOException { + defaultWriteObject(current); + } + + private void defaultWriteObject(Object o) throws IOException { + for (Field field : getFields(o.getClass())) { + field(o, field); + } + } +} diff --git a/sgx-jvm/avian/classpath/java/io/ObjectStreamException.java b/sgx-jvm/avian/classpath/java/io/ObjectStreamException.java new file mode 100644 index 0000000000..2e78ce6289 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/ObjectStreamException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class ObjectStreamException extends IOException { + public ObjectStreamException(String message) { + super(message); + } + + public ObjectStreamException() { + this(null); + } +} diff --git a/sgx-jvm/avian/classpath/java/io/OutputStream.java b/sgx-jvm/avian/classpath/java/io/OutputStream.java new file mode 100644 index 0000000000..b81d0b71d0 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/OutputStream.java @@ -0,0 +1,29 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public abstract class OutputStream implements Closeable, Flushable { + public abstract void write(int c) throws IOException; + + public void write(byte[] buffer) throws IOException { + write(buffer, 0, buffer.length); + } + + public void write(byte[] buffer, int offset, int length) throws IOException { + for (int i = 0; i < length; ++i) { + write(buffer[offset + i]); + } + } + + public void flush() throws IOException { } + + public void close() throws IOException { } +} diff --git a/sgx-jvm/avian/classpath/java/io/OutputStreamWriter.java b/sgx-jvm/avian/classpath/java/io/OutputStreamWriter.java new file mode 100644 index 0000000000..2a183d31d2 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/OutputStreamWriter.java @@ -0,0 +1,33 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +import avian.Utf8; + +public class OutputStreamWriter extends Writer { + private final OutputStream out; + + public OutputStreamWriter(OutputStream out) { + this.out = out; + } + + public void write(char[] b, int offset, int length) throws IOException { + out.write(Utf8.encode(b, offset, length)); + } + + public void flush() throws IOException { + out.flush(); + } + + public void close() throws IOException { + out.close(); + } +} diff --git a/sgx-jvm/avian/classpath/java/io/PrintStream.java b/sgx-jvm/avian/classpath/java/io/PrintStream.java new file mode 100644 index 0000000000..eda3feac64 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/PrintStream.java @@ -0,0 +1,170 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class PrintStream extends OutputStream { + private final OutputStream out; + private final boolean autoFlush; + + private static class Static { + private static final byte[] newline + = System.getProperty("line.separator").getBytes(); + } + + public PrintStream(OutputStream out, boolean autoFlush) { + this.out = out; + this.autoFlush = autoFlush; + } + + public PrintStream(OutputStream out, boolean autoFlush, String encoding) + throws UnsupportedEncodingException + { + this.out = out; + this.autoFlush = autoFlush; + + if (! (encoding.equals("UTF-8") || encoding.equals("ISO-8859-1"))) { + throw new UnsupportedEncodingException(encoding); + } + } + + public PrintStream(OutputStream out) { + this(out, false); + } + + public synchronized void print(String s) { + try { + out.write(s.getBytes()); + if (autoFlush) flush(); + } catch (IOException e) { } + } + + public void print(Object o) { + print(String.valueOf(o)); + } + + public void print(boolean v) { + print(String.valueOf(v)); + } + + public void print(char c) { + print(String.valueOf(c)); + } + + public void print(int v) { + print(String.valueOf(v)); + } + + public void print(long v) { + print(String.valueOf(v)); + } + + public void print(float v) { + print(String.valueOf(v)); + } + + public void print(double v) { + print(String.valueOf(v)); + } + + public void print(char[] s) { + print(String.valueOf(s)); + } + + public synchronized PrintStream printf(java.util.Locale locale, String format, Object... args) { + // should this be cached in an instance variable?? + final java.util.Formatter formatter = new java.util.Formatter(this); + formatter.format(locale, format, args); + return this; + } + + public synchronized PrintStream printf(String format, Object... args) { + final java.util.Formatter formatter = new java.util.Formatter(this); + formatter.format(format, args); + return this; + } + + public PrintStream format(String format, Object... args) { + return printf(format, args); + } + + public PrintStream format(java.util.Locale locale, String format, Object... args) { + return printf(locale, format, args); + } + + public synchronized void println(String s) { + try { + out.write(s.getBytes()); + out.write(Static.newline); + if (autoFlush) flush(); + } catch (IOException e) { } + } + + public synchronized void println() { + try { + out.write(Static.newline); + if (autoFlush) flush(); + } catch (IOException e) { } + } + + public void println(Object o) { + println(String.valueOf(o)); + } + + public void println(boolean v) { + println(String.valueOf(v)); + } + + public void println(char c) { + println(String.valueOf(c)); + } + + public void println(int v) { + println(String.valueOf(v)); + } + + public void println(long v) { + println(String.valueOf(v)); + } + + public void println(float v) { + println(String.valueOf(v)); + } + + public void println(double v) { + println(String.valueOf(v)); + } + + public void println(char[] s) { + println(String.valueOf(s)); + } + + public void write(int c) throws IOException { + out.write(c); + if (autoFlush && c == '\n') flush(); + } + + public void write(byte[] buffer, int offset, int length) throws IOException { + out.write(buffer, offset, length); + if (autoFlush) flush(); + } + + public void flush() { + try { + out.flush(); + } catch (IOException e) { } + } + + public void close() { + try { + out.close(); + } catch (IOException e) { } + } +} diff --git a/sgx-jvm/avian/classpath/java/io/PrintWriter.java b/sgx-jvm/avian/classpath/java/io/PrintWriter.java new file mode 100644 index 0000000000..604368d2b5 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/PrintWriter.java @@ -0,0 +1,90 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class PrintWriter extends Writer { + private static final char[] newline + = System.getProperty("line.separator").toCharArray(); + + private final Writer out; + private final boolean autoFlush; + + public PrintWriter(Writer out, boolean autoFlush) { + this.out = out; + this.autoFlush = autoFlush; + } + + public PrintWriter(Writer out) { + this(out, false); + } + + public PrintWriter(OutputStream out, boolean autoFlush) { + this(new OutputStreamWriter(out), autoFlush); + } + + public PrintWriter(OutputStream out) { + this(out, false); + } + + public synchronized void print(String s) { + try { + out.write(s.toCharArray()); + } catch (IOException e) { } + } + + public void print(Object o) { + print(o.toString()); + } + + public void print(char c) { + print(String.valueOf(c)); + } + + public synchronized void println(String s) { + try { + out.write(s.toCharArray()); + out.write(newline); + if (autoFlush) flush(); + } catch (IOException e) { } + } + + public synchronized void println() { + try { + out.write(newline); + if (autoFlush) flush(); + } catch (IOException e) { } + } + + public void println(Object o) { + println(o.toString()); + } + + public void println(char c) { + println(String.valueOf(c)); + } + + public void write(char[] buffer, int offset, int length) throws IOException { + out.write(buffer, offset, length); + if (autoFlush) flush(); + } + + public void flush() { + try { + out.flush(); + } catch (IOException e) { } + } + + public void close() { + try { + out.close(); + } catch (IOException e) { } + } +} diff --git a/sgx-jvm/avian/classpath/java/io/PushbackReader.java b/sgx-jvm/avian/classpath/java/io/PushbackReader.java new file mode 100644 index 0000000000..49acf98357 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/PushbackReader.java @@ -0,0 +1,74 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class PushbackReader extends Reader { + private final Reader in; + private char savedChar; + private boolean hasSavedChar; + + public PushbackReader(Reader in, int bufferSize) { + if (bufferSize > 1) { + throw new IllegalArgumentException(bufferSize + " > 1"); + } + this.in = in; + this.hasSavedChar = false; + } + + public PushbackReader(Reader in) { + this(in, 1); + } + + public int read(char[] b, int offset, int length) throws IOException { + int count = 0; + if (hasSavedChar && length > 0) { + length--; + b[offset++] = savedChar; + hasSavedChar = false; + count = 1; + } + if (length > 0) { + int c = in.read(b, offset, length); + if (c == -1) { + if (count == 0) { + count = -1; + } + } else { + count += c; + } + } + + return count; + } + + public void unread(char[] b, int offset, int length) throws IOException { + if (length != 1) { + throw new IOException("Can only push back 1 char, not " + length); + } else if (hasSavedChar) { + throw new IOException("Already have a saved char"); + } else { + hasSavedChar = true; + savedChar = b[offset]; + } + } + + public void unread(char[] b) throws IOException { + unread(b, 0, b.length); + } + + public void unread(int c) throws IOException { + unread(new char[] { (char) c }); + } + + public void close() throws IOException { + in.close(); + } +} diff --git a/sgx-jvm/avian/classpath/java/io/RandomAccessFile.java b/sgx-jvm/avian/classpath/java/io/RandomAccessFile.java new file mode 100644 index 0000000000..4739b9ea78 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/RandomAccessFile.java @@ -0,0 +1,282 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +import java.lang.IllegalArgumentException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; + +public class RandomAccessFile implements DataInput, Closeable { + + private long peer; + private File file; + private long position = 0; + private long length; + private boolean allowWrite; + + public RandomAccessFile(String name, String mode) + throws FileNotFoundException + { + this(new File(name), mode); + } + + public RandomAccessFile(File file, String mode) + throws FileNotFoundException + { + if (file == null) throw new NullPointerException(); + if (mode.equals("rw")) allowWrite = true; + else if (! mode.equals("r")) throw new IllegalArgumentException(); + this.file = file; + open(); + } + + private void open() throws FileNotFoundException { + long[] result = new long[2]; + open(file.getPath(), allowWrite, result); + peer = result[0]; + length = result[1]; + } + + private static native void open(String name, boolean allowWrite, long[] result) + throws FileNotFoundException; + + private void refresh() throws IOException { + if (file.length() != length) { + close(); + open(); + } + } + + public long length() throws IOException { + refresh(); + return length; + } + + public long getFilePointer() throws IOException { + return position; + } + + public void seek(long position) throws IOException { + if (position < 0 || (!allowWrite && position > length())) throw new IOException(); + + this.position = position; + } + + public int skipBytes(int count) throws IOException { + if (position + count > length()) throw new IOException(); + this.position = position + count; + return count; + } + + public int read(byte b[], int off, int len) throws IOException { + if(b == null) + throw new IllegalArgumentException(); + if (peer == 0) + throw new IOException(); + if(len == 0) + return 0; + if (position + len > this.length) + throw new EOFException(); + if (off < 0 || off + len > b.length) + throw new ArrayIndexOutOfBoundsException(); + int bytesRead = readBytes(peer, position, b, off, len); + position += bytesRead; + return bytesRead; + } + + public int read(byte b[]) throws IOException { + if(b == null) + throw new IllegalArgumentException(); + if (peer == 0) + throw new IOException(); + if(b.length == 0) + return 0; + if (position + b.length > this.length) + throw new EOFException(); + int bytesRead = readBytes(peer, position, b, 0, b.length); + position += bytesRead; + return bytesRead; + } + + public void readFully(byte b[], int off, int len) throws IOException { + if (b == null) + throw new IllegalArgumentException(); + if (peer == 0) + throw new IOException(); + if(len == 0) + return; + if (position + len > this.length) + throw new EOFException(); + if (off < 0 || off + len > b.length) + throw new ArrayIndexOutOfBoundsException(); + int n = 0; + do { + int count = readBytes(peer, position, b, off + n, len - n); + position += count; + if (count == 0) + throw new EOFException(); + n += count; + } while (n < len); + } + + public void readFully(byte b[]) throws IOException { + readFully(b, 0, b.length); + } + + private static native int readBytes(long peer, long position, byte[] buffer, + int offset, int length); + + public boolean readBoolean() throws IOException { + return readByte() != 0; + } + + public int read() throws IOException { + try { + return readByte() & 0xff; + } catch (final EOFException e) { + return -1; + } + } + + public byte readByte() throws IOException { + final byte[] buffer = new byte[1]; + readFully(buffer); + return buffer[0]; + } + + public short readShort() throws IOException { + final byte[] buffer = new byte[2]; + readFully(buffer); + return (short)((buffer[0] << 8) | buffer[1]); + } + + public int readInt() throws IOException { + byte[] buf = new byte[4]; + readFully(buf); + return ((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]); + } + + public float readFloat() throws IOException { + return Float.floatToIntBits(readInt()); + } + + public double readDouble() throws IOException { + return Double.doubleToLongBits(readLong()); + } + + public long readLong() throws IOException { + return ((readInt() & 0xffffffffl) << 32) | (readInt() & 0xffffffffl); + } + + public char readChar() throws IOException { + return (char)readShort(); + } + + public int readUnsignedByte() throws IOException { + return readByte() & 0xff; + } + + public int readUnsignedShort() throws IOException { + return readShort() & 0xffff; + } + + public String readUTF() throws IOException { + int length = readUnsignedShort(); + byte[] bytes = new byte[length]; + readFully(bytes); + return new String(bytes, "UTF-8"); + } + + @Deprecated + public String readLine() throws IOException { + int c = read(); + if (c < 0) { + return null; + } else if (c == '\n') { + return ""; + } + StringBuilder builder = new StringBuilder(); + for (;;) { + builder.append((char)c); + c = read(); + if (c < 0 || c == '\n') { + return builder.toString(); + } + } + } + + public void write(int b) throws IOException { + int count = writeBytes(peer, position, new byte[] { (byte)b }, 0, 1); + if (count > 0) position += count; + } + + private static native int writeBytes(long peer, long position, byte[] buffer, + int offset, int length); + + public void close() throws IOException { + if (peer != 0) { + close(peer); + peer = 0; + } + } + + private static native void close(long peer); + + public FileChannel getChannel() { + return new FileChannel() { + public void close() { + if (peer != 0) RandomAccessFile.close(peer); + } + + public boolean isOpen() { + return peer != 0; + } + + public int read(ByteBuffer dst, long position) throws IOException { + if (!dst.hasArray()) throw new IOException("Cannot handle " + dst.getClass()); + // TODO: this needs to be synchronized on the Buffer, no? + byte[] array = dst.array(); + return readBytes(peer, position, array, dst.position(), dst.remaining()); + } + + public int read(ByteBuffer dst) throws IOException { + int count = read(dst, position); + if (count > 0) position += count; + return count; + } + + public int write(ByteBuffer src, long position) throws IOException { + if (!src.hasArray()) throw new IOException("Cannot handle " + src.getClass()); + byte[] array = src.array(); + return writeBytes(peer, position, array, src.position(), src.remaining()); + } + + public int write(ByteBuffer src) throws IOException { + int count = write(src, position); + if (count > 0) position += count; + return count; + } + + public long position() throws IOException { + return getFilePointer(); + } + + public FileChannel position(long position) throws IOException { + seek(position); + return this; + } + + public long size() throws IOException { + return length(); + } + }; + } +} diff --git a/sgx-jvm/avian/classpath/java/io/Reader.java b/sgx-jvm/avian/classpath/java/io/Reader.java new file mode 100644 index 0000000000..bba2be4c13 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/Reader.java @@ -0,0 +1,58 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +import java.nio.CharBuffer; + +public abstract class Reader implements Closeable, Readable { + public int read(CharBuffer buffer) throws IOException { + int c = read(buffer.array(), + buffer.arrayOffset() + buffer.position(), + buffer.remaining()); + + if (c > 0) { + buffer.position(buffer.position() + c); + } + + return c; + } + + public int read() throws IOException { + char[] buffer = new char[1]; + int c = read(buffer); + if (c <= 0) { + return -1; + } else { + return (int) buffer[0]; + } + } + + public int read(char[] buffer) throws IOException { + return read(buffer, 0, buffer.length); + } + + public abstract int read(char[] buffer, int offset, int length) + throws IOException; + + public boolean markSupported() { + return false; + } + + public void mark(int readAheadLimit) throws IOException { + throw new IOException("mark not supported"); + } + + public void reset() throws IOException { + throw new IOException("reset not supported"); + } + + public abstract void close() throws IOException; +} diff --git a/sgx-jvm/avian/classpath/java/io/Serializable.java b/sgx-jvm/avian/classpath/java/io/Serializable.java new file mode 100644 index 0000000000..11978a051c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/Serializable.java @@ -0,0 +1,13 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public interface Serializable { } diff --git a/sgx-jvm/avian/classpath/java/io/StreamCorruptedException.java b/sgx-jvm/avian/classpath/java/io/StreamCorruptedException.java new file mode 100644 index 0000000000..06e56f9d62 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/StreamCorruptedException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class StreamCorruptedException extends IOException { + public StreamCorruptedException(String message) { + super(message); + } + + public StreamCorruptedException() { + this(null); + } +} diff --git a/sgx-jvm/avian/classpath/java/io/StringReader.java b/sgx-jvm/avian/classpath/java/io/StringReader.java new file mode 100644 index 0000000000..f8f863c35e --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/StringReader.java @@ -0,0 +1,34 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class StringReader extends Reader { + private final String in; + private int position = 0; + + public StringReader(String in) { + this.in = in; + } + + public int read(char[] b, int offset, int length) throws IOException { + if (length > in.length() - position) { + length = in.length() - position; + if (length <= 0) { + return -1; + } + } + in.getChars(position, position+length, b, offset); + position += length; + return length; + } + + public void close() throws IOException { } +} diff --git a/sgx-jvm/avian/classpath/java/io/StringWriter.java b/sgx-jvm/avian/classpath/java/io/StringWriter.java new file mode 100644 index 0000000000..98d111f077 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/StringWriter.java @@ -0,0 +1,31 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class StringWriter extends Writer { + private final StringBuffer out = new StringBuffer(); + + public void write(char[] b, int offset, int length) throws IOException { + out.append(b, offset, length); + } + + public String toString() { + return out.toString(); + } + + public void flush() throws IOException { } + + public void close() throws IOException { } + + public StringBuffer getBuffer() { + return out; + } +} diff --git a/sgx-jvm/avian/classpath/java/io/UTFDataFormatException.java b/sgx-jvm/avian/classpath/java/io/UTFDataFormatException.java new file mode 100644 index 0000000000..7938bbf6b0 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/UTFDataFormatException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class UTFDataFormatException extends IOException { + public UTFDataFormatException(String s) { + super(s); + } + + public UTFDataFormatException() { + super(); + } +} diff --git a/sgx-jvm/avian/classpath/java/io/UnsupportedEncodingException.java b/sgx-jvm/avian/classpath/java/io/UnsupportedEncodingException.java new file mode 100644 index 0000000000..80e8efeff3 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/UnsupportedEncodingException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class UnsupportedEncodingException extends IOException { + public UnsupportedEncodingException(String message) { + super(message); + } + + public UnsupportedEncodingException() { + this(null); + } +} diff --git a/sgx-jvm/avian/classpath/java/io/Writer.java b/sgx-jvm/avian/classpath/java/io/Writer.java new file mode 100644 index 0000000000..60f0ccdeaf --- /dev/null +++ b/sgx-jvm/avian/classpath/java/io/Writer.java @@ -0,0 +1,63 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public abstract class Writer implements Closeable, Flushable, Appendable { + public void write(int c) throws IOException { + char[] buffer = new char[] { (char) c }; + write(buffer); + } + + public void write(char[] buffer) throws IOException { + write(buffer, 0, buffer.length); + } + + public void write(String s) throws IOException { + write(s.toCharArray()); + } + + public void write(String s, int offset, int length) throws IOException { + char[] b = new char[length]; + s.getChars(offset, offset + length, b, 0); + write(b); + } + + public abstract void write(char[] buffer, int offset, int length) + throws IOException; + + public Appendable append(final char c) throws IOException { + write((int)c); + return this; + } + + public Appendable append(final CharSequence sequence) throws IOException { + return append(sequence, 0, sequence.length()); + } + + public Appendable append(CharSequence sequence, int start, int end) + throws IOException { + final int length = end - start; + if (sequence instanceof String) { + write((String)sequence, start, length); + } else { + final char[] charArray = new char[length]; + for (int i = start; i < end; i++) { + charArray[i] = sequence.charAt(i); + } + write(charArray, 0, length); + } + return this; + } + + public abstract void flush() throws IOException; + + public abstract void close() throws IOException; +} diff --git a/sgx-jvm/avian/classpath/java/lang/AbstractMethodError.java b/sgx-jvm/avian/classpath/java/lang/AbstractMethodError.java new file mode 100644 index 0000000000..185e10dee6 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/AbstractMethodError.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class AbstractMethodError extends IncompatibleClassChangeError { + public AbstractMethodError() { + super(); + } + + public AbstractMethodError(String message) { + super(message); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/Appendable.java b/sgx-jvm/avian/classpath/java/lang/Appendable.java new file mode 100644 index 0000000000..fc99187025 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Appendable.java @@ -0,0 +1,22 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +import java.io.IOException; + +public interface Appendable { + public Appendable append(char c) throws IOException; + + public Appendable append(CharSequence sequence) throws IOException; + + public Appendable append(CharSequence sequence, int start, int end) + throws IOException; +} diff --git a/sgx-jvm/avian/classpath/java/lang/ArithmeticException.java b/sgx-jvm/avian/classpath/java/lang/ArithmeticException.java new file mode 100644 index 0000000000..0d7822dab0 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/ArithmeticException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class ArithmeticException extends RuntimeException { + public ArithmeticException(String message) { + super(message); + } + + public ArithmeticException() { + this(null); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/ArrayIndexOutOfBoundsException.java b/sgx-jvm/avian/classpath/java/lang/ArrayIndexOutOfBoundsException.java new file mode 100644 index 0000000000..59913900fe --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/ArrayIndexOutOfBoundsException.java @@ -0,0 +1,33 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException { + public ArrayIndexOutOfBoundsException(String message, Throwable cause) { + super(message, cause); + } + + public ArrayIndexOutOfBoundsException(String message) { + this(message, null); + } + + public ArrayIndexOutOfBoundsException(Throwable cause) { + this(null, cause); + } + + public ArrayIndexOutOfBoundsException(int idx) { + this("Array index out of range: " + idx); + } + + public ArrayIndexOutOfBoundsException() { + this(null, null); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/ArrayStoreException.java b/sgx-jvm/avian/classpath/java/lang/ArrayStoreException.java new file mode 100644 index 0000000000..90ed0ddbd6 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/ArrayStoreException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class ArrayStoreException extends RuntimeException { + public ArrayStoreException(String message) { + super(message, null); + } + + public ArrayStoreException() { + this(null); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/AssertionError.java b/sgx-jvm/avian/classpath/java/lang/AssertionError.java new file mode 100644 index 0000000000..16d650a26a --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/AssertionError.java @@ -0,0 +1,45 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class AssertionError extends Error { + public AssertionError() { + super("", null); + } + + public AssertionError(boolean detailMessage) { + super(""+detailMessage, null); + } + + public AssertionError(char detailMessage) { + super(""+detailMessage, null); + } + + public AssertionError(double detailMessage) { + super(""+detailMessage, null); + } + + public AssertionError(float detailMessage) { + super(""+detailMessage, null); + } + + public AssertionError(int detailMessage) { + super(""+detailMessage, null); + } + + public AssertionError(long detailMessage) { + super(""+detailMessage, null); + } + + public AssertionError(Object detailMessage) { + super(""+detailMessage, null); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/AutoCloseable.java b/sgx-jvm/avian/classpath/java/lang/AutoCloseable.java new file mode 100644 index 0000000000..0693dd45a5 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/AutoCloseable.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public interface AutoCloseable { + void close() throws Exception; +} diff --git a/sgx-jvm/avian/classpath/java/lang/Boolean.java b/sgx-jvm/avian/classpath/java/lang/Boolean.java new file mode 100644 index 0000000000..44ebae2e09 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Boolean.java @@ -0,0 +1,69 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public final class Boolean implements Comparable { + public static final Class TYPE = avian.Classes.forCanonicalName("Z"); + + public static final Boolean FALSE = new Boolean(false); + public static final Boolean TRUE = new Boolean(true); + + private final boolean value; + + public Boolean(boolean value) { + this.value = value; + } + + public Boolean(String s) { + this.value = "true".equals(s); + } + + public static Boolean valueOf(boolean value) { + return (value ? Boolean.TRUE : Boolean.FALSE); + } + + public static Boolean valueOf(String s) { + Boolean.TRUE.booleanValue(); + return ("true".equals(s) ? Boolean.TRUE : Boolean.FALSE); + } + + public int compareTo(Boolean o) { + return (value ? (o.value ? 0 : 1) : (o.value ? -1 : 0)); + } + + public boolean equals(Object o) { + return o instanceof Boolean && ((Boolean) o).value == value; + } + + public int hashCode() { + return (value ? 1 : 0); + } + + public String toString() { + return toString(value); + } + + public static String toString(boolean v) { + return (v ? "true" : "false"); + } + + public boolean booleanValue() { + return value; + } + + public static boolean getBoolean(String name) { + return parseBoolean(System.getProperty(name)); + } + + public static boolean parseBoolean(String string) { + return string != null && string.equalsIgnoreCase("true"); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/Byte.java b/sgx-jvm/avian/classpath/java/lang/Byte.java new file mode 100644 index 0000000000..a28ec6a0f4 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Byte.java @@ -0,0 +1,80 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public final class Byte extends Number implements Comparable { + public static final Class TYPE = avian.Classes.forCanonicalName("B"); + + public static final byte MIN_VALUE = -128; + public static final byte MAX_VALUE = 127; + + private final byte value; + + public Byte(byte value) { + this.value = value; + } + + public static Byte valueOf(byte value) { + return new Byte(value); + } + + public boolean equals(Object o) { + return o instanceof Byte && ((Byte) o).value == value; + } + + public int hashCode() { + return value; + } + + public String toString() { + return toString(value); + } + + public int compareTo(Byte o) { + return value - o.value; + } + + public static String toString(byte v, int radix) { + return Long.toString(v, radix); + } + + public static String toString(byte v) { + return toString(v, 10); + } + + public static byte parseByte(String s) { + return (byte) Integer.parseInt(s); + } + + public byte byteValue() { + return value; + } + + public short shortValue() { + return value; + } + + public int intValue() { + return value; + } + + public long longValue() { + return value; + } + + public float floatValue() { + return (float) value; + } + + public double doubleValue() { + return (double) value; + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/CharSequence.java b/sgx-jvm/avian/classpath/java/lang/CharSequence.java new file mode 100644 index 0000000000..bde0354155 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/CharSequence.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public interface CharSequence { + public char charAt(int index); + + int length(); + + CharSequence subSequence(int start, int end); + + String toString(); +} diff --git a/sgx-jvm/avian/classpath/java/lang/Character.java b/sgx-jvm/avian/classpath/java/lang/Character.java new file mode 100644 index 0000000000..2a0882de85 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Character.java @@ -0,0 +1,258 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public final class Character implements Comparable { + public static final int MIN_RADIX = 2; + public static final int MAX_RADIX = 36; + + public static final Class TYPE = avian.Classes.forCanonicalName("C"); + + private final char value; + + public Character(char value) { + this.value = value; + } + + public static Character valueOf(char value) { + return new Character(value); + } + + public int compareTo(Character o) { + return value - o.value; + } + + public boolean equals(Object o) { + return o instanceof Character && ((Character) o).value == value; + } + + public int hashCode() { + return (int) value; + } + + public String toString() { + return toString(value); + } + + public static String toString(char v) { + return new String(new char[] { v }); + } + + public char charValue() { + return value; + } + + public static char toLowerCase(char c) { + if (c >= 'A' && c <= 'Z') { + return (char) ((c - 'A') + 'a'); + } else { + return c; + } + } + + public static int toLowerCase(int codePoint) { + if (isSupplementaryCodePoint(codePoint)) { + return codePoint; + } else { + return toLowerCase((char) codePoint); + } + } + + public static char toUpperCase(char c) { + if (c >= 'a' && c <= 'z') { + return (char) ((c - 'a') + 'A'); + } else { + return c; + } + } + + public static int toUpperCase(int codePoint) { + if (isSupplementaryCodePoint(codePoint)) { + return codePoint; + } else { + return toUpperCase((char) codePoint); + } + } + + public static boolean isDigit(char c) { + return c >= '0' && c <= '9'; + } + + public static boolean isDigit(int c) { + return c >= '0' && c <= '9'; + } + + public static int digit(char c, int radix) { + int digit = 0; + if ((c >= '0') && (c <= '9')) { + digit = c - '0'; + } else if ((c >= 'a') && (c <= 'z')) { + digit = c - 'a' + 10; + } else if ((c >= 'A') && (c <= 'Z')) { + digit = c - 'A' + 10; + } else { + return -1; + } + + if (digit < radix) { + return digit; + } else { + return -1; + } + } + + public static char forDigit(int digit, int radix) { + if (MIN_RADIX <= radix && radix <= MAX_RADIX) { + if (0 <= digit && digit < radix) { + return (char) (digit < 10 ? digit + '0' : digit + 'a' - 10); + } + } + return 0; + } + + public static boolean isLetter(int c) { + return canCastToChar(c) && isLetter((char) c); + } + + public static boolean isLetter(char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); + } + + public static boolean isLetterOrDigit(char c) { + return isDigit(c) || isLetter(c); + } + + public static boolean isLetterOrDigit(int c) { + return canCastToChar(c) && (isDigit((char) c) || isLetter((char) c)); + } + + public static boolean isLowerCase(int c) { + return canCastToChar(c) && isLowerCase((char) c); + } + + public static boolean isLowerCase(char c) { + return (c >= 'a' && c <= 'z'); + } + + public static boolean isUpperCase(char c) { + return (c >= 'A' && c <= 'Z'); + } + + public static boolean isUpperCase(int c) { + return canCastToChar(c) && isUpperCase((char) c); + } + + public static boolean isWhitespace(int c) { + return canCastToChar(c) && isWhitespace((char) c); + } + + public static boolean isWhitespace(char c) { + return c == ' ' || c == '\t' || c == '\n' || c == '\r'; + } + + public static boolean isSpaceChar(char c) { + return isWhitespace(c); + } + + public static boolean isHighSurrogate(char ch) { + return ch >= '\uD800' && ch <= '\uDBFF'; + } + + public static boolean isLowSurrogate(char ch) { + return ch >= '\uDC00' && ch <= '\uDFFF'; + } + + public static boolean isISOControl(char ch) { + return ch <= '\u001F' || (ch >= '\u007F' && ch <= '\u009F'); + } + + public static boolean isJavaIdentifierStart(char ch) { + return isLetter(ch) || ch == '$' || ch == '_'; + //TODO: add if (getType(ch)==LETTER_NUMBER) || getType(ch)==CURRENCY_SYMBOL + } + + public static boolean isJavaIdentifierStart(int c) { + return canCastToChar(c) && isJavaIdentifierStart((char) c); + } + + public static boolean isJavaIdentifierPart(char ch) { + return isJavaIdentifierStart(ch) || isDigit(ch); + //TODO:Check for numeric letters (such as a Roman numeral character),combining marks,non-spacing marks + //add isIdentifierIgnorable(ch) + } + + public static boolean isJavaIdentifierPart(int c) { + return canCastToChar(c) && isJavaIdentifierPart((char) c); + } + + public static int toCodePoint(char high, char low) { + return (((high & 0x3FF) << 10) | (low & 0x3FF)) + 0x10000; + } + + public static boolean isSupplementaryCodePoint(int codePoint) { + return codePoint >= 0x10000 && codePoint <= 0x10FFFF; + } + + private static boolean canCastToChar(int codePoint) { + return (codePoint >= 0 && codePoint <= 0xFFFF); + } + + public static char[] toChars(int codePoint) { + if (isSupplementaryCodePoint(codePoint)) { + int cpPrime = codePoint - 0x10000; + int high = 0xD800 | ((cpPrime >> 10) & 0x3FF); + int low = 0xDC00 | (cpPrime & 0x3FF); + return new char[] { (char) high, (char) low }; + } + return new char[] { (char) codePoint }; + } + + public static boolean isSurrogatePair(char high, char low) { + return isHighSurrogate(high) && isLowSurrogate(low); + } + + public static int codePointAt(CharSequence sequence, int offset) { + int length = sequence.length(); + if (offset < 0 || offset >= length) { + throw new IndexOutOfBoundsException(); + } + + char high = sequence.charAt(offset); + if (! isHighSurrogate(high) || offset >= length) { + return high; + } + char low = sequence.charAt(offset + 1); + if (! isLowSurrogate(low)) { + return high; + } + + return toCodePoint(high, low); + } + + public static int codePointCount(CharSequence sequence, int start, int end) { + int length = sequence.length(); + if (start < 0 || start > end || end >= length) { + throw new IndexOutOfBoundsException(); + } + + int count = 0; + for (int i = start; i < end; ++i) { + if (isHighSurrogate(sequence.charAt(i)) + && (i + 1) < end + && isLowSurrogate(sequence.charAt(i + 1))) + { + ++ i; + } + ++ count; + } + return count; + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/Class.java b/sgx-jvm/avian/classpath/java/lang/Class.java new file mode 100644 index 0000000000..5cba3cd7e0 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Class.java @@ -0,0 +1,791 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +import avian.VMClass; +import avian.ClassAddendum; +import avian.AnnotationInvocationHandler; +import avian.SystemClassLoader; +import avian.Classes; +import avian.InnerClassReference; + +import java.lang.reflect.Constructor; +import java.lang.reflect.GenericDeclaration; +import java.lang.reflect.Method; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.Proxy; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.SignatureParser; +import java.lang.annotation.Annotation; +import java.io.InputStream; +import java.io.IOException; +import java.net.URL; +import java.util.Arrays; +import java.util.List; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Map; +import java.util.HashMap; +import java.security.ProtectionDomain; +import java.security.Permissions; +import java.security.AllPermission; + +public final class Class + implements Type, AnnotatedElement, GenericDeclaration +{ + private static final int PrimitiveFlag = 1 << 5; + private static final int EnumFlag = 1 << 14; + + public final VMClass vmClass; + + public Class(VMClass vmClass) { + this.vmClass = vmClass; + } + + public String toString() { + String res; + if (isInterface()) res = "interface "; + else if (isAnnotation()) res = "annotation "; + else res = "class "; + return res + getName(); + } + + private static byte[] replace(int a, int b, byte[] s, int offset, + int length) + { + byte[] array = new byte[length]; + for (int i = 0; i < length; ++i) { + byte c = s[i]; + array[i] = (byte) (c == a ? b : c); + } + return array; + } + + public String getName() { + return getName(vmClass); + } + + public static String getName(VMClass c) { + if (c.name == null) { + if ((c.vmFlags & PrimitiveFlag) != 0) { + if (c == Classes.primitiveClass('V')) { + c.name = "void\0".getBytes(); + } else if (c == Classes.primitiveClass('Z')) { + c.name = "boolean\0".getBytes(); + } else if (c == Classes.primitiveClass('B')) { + c.name = "byte\0".getBytes(); + } else if (c == Classes.primitiveClass('C')) { + c.name = "char\0".getBytes(); + } else if (c == Classes.primitiveClass('S')) { + c.name = "short\0".getBytes(); + } else if (c == Classes.primitiveClass('I')) { + c.name = "int\0".getBytes(); + } else if (c == Classes.primitiveClass('F')) { + c.name = "float\0".getBytes(); + } else if (c == Classes.primitiveClass('J')) { + c.name = "long\0".getBytes(); + } else if (c == Classes.primitiveClass('D')) { + c.name = "double\0".getBytes(); + } else { + throw new AssertionError(); + } + } else { + throw new AssertionError(); + } + } + + return Classes.makeString + (replace('/', '.', c.name, 0, c.name.length - 1), 0, c.name.length - 1); + } + + public String getCanonicalName() { + if ((vmClass.vmFlags & PrimitiveFlag) != 0) { + return getName(); + } else if (isArray()) { + return getComponentType().getCanonicalName() + "[]"; + } else { + return getName().replace('$', '.'); + } + } + + public String getSimpleName() { + if ((vmClass.vmFlags & PrimitiveFlag) != 0) { + return getName(); + } else if (isArray()) { + return getComponentType().getSimpleName() + "[]"; + } else { + String name = getCanonicalName(); + int index = name.lastIndexOf('.'); + if (index >= 0) { + return name.substring(index + 1); + } else { + return name; + } + } + } + + public T newInstance() + throws IllegalAccessException, InstantiationException + { + try { + return (T) getConstructor().newInstance(); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + public static Class forName(String name) throws ClassNotFoundException { + return forName(name, true, Method.getCaller().class_.loader); + } + + public static Class forName(String name, boolean initialize, + ClassLoader loader) + throws ClassNotFoundException + { + return Classes.forName(name, initialize, loader); + } + + public Class getComponentType() { + if (isArray()) { + String n = getName(); + if ("[Z".equals(n)) { + return SystemClassLoader.getClass(Classes.primitiveClass('Z')); + } else if ("[B".equals(n)) { + return SystemClassLoader.getClass(Classes.primitiveClass('B')); + } else if ("[S".equals(n)) { + return SystemClassLoader.getClass(Classes.primitiveClass('S')); + } else if ("[C".equals(n)) { + return SystemClassLoader.getClass(Classes.primitiveClass('C')); + } else if ("[I".equals(n)) { + return SystemClassLoader.getClass(Classes.primitiveClass('I')); + } else if ("[F".equals(n)) { + return SystemClassLoader.getClass(Classes.primitiveClass('F')); + } else if ("[J".equals(n)) { + return SystemClassLoader.getClass(Classes.primitiveClass('J')); + } else if ("[D".equals(n)) { + return SystemClassLoader.getClass(Classes.primitiveClass('D')); + } + + if (vmClass.arrayElementClass == null) throw new AssertionError(); + return SystemClassLoader.getClass((VMClass) vmClass.arrayElementClass); + } else { + return null; + } + } + + public boolean isAssignableFrom(Class c) { + return Classes.isAssignableFrom(vmClass, c.vmClass); + } + + public Field getDeclaredField(String name) throws NoSuchFieldException { + int index = Classes.findField(vmClass, name); + if (index < 0) { + throw new NoSuchFieldException(name); + } else { + return new Field(vmClass.fieldTable[index]); + } + } + + public Field getField(String name) throws NoSuchFieldException { + for (VMClass c = vmClass; c != null; c = c.super_) { + int index = Classes.findField(c, name); + if (index >= 0) { + return new Field(vmClass.fieldTable[index]); + } + } + throw new NoSuchFieldException(name); + } + + public Method getDeclaredMethod(String name, Class ... parameterTypes) + throws NoSuchMethodException + { + if (name.startsWith("<")) { + throw new NoSuchMethodException(name); + } + int index = Classes.findMethod(vmClass, name, parameterTypes); + if (index < 0) { + throw new NoSuchMethodException(name); + } else { + return new Method(vmClass.methodTable[index]); + } + } + + public Method getMethod(String name, Class ... parameterTypes) + throws NoSuchMethodException + { + if (name.startsWith("<")) { + throw new NoSuchMethodException(name); + } + for (VMClass c = vmClass; c != null; c = c.super_) { + int index = Classes.findMethod(c, name, parameterTypes); + if (index >= 0) { + return new Method(c.methodTable[index]); + } + } + throw new NoSuchMethodException(name); + } + + public Constructor getConstructor(Class ... parameterTypes) + throws NoSuchMethodException + { + int index = Classes.findMethod(vmClass, "", parameterTypes); + if (index < 0) { + throw new NoSuchMethodException(); + } else { + return new Constructor(new Method(vmClass.methodTable[index])); + } + } + + public Constructor getDeclaredConstructor(Class ... parameterTypes) + throws NoSuchMethodException + { + Constructor c = null; + Constructor[] constructors = getDeclaredConstructors(); + + for (int i = 0; i < constructors.length; ++i) { + if (Classes.match(parameterTypes, constructors[i].getParameterTypes())) { + c = constructors[i]; + } + } + + if (c == null) { + throw new NoSuchMethodException(); + } else { + return c; + } + } + + private int countConstructors(boolean publicOnly) { + int count = 0; + if (vmClass.methodTable != null) { + for (int i = 0; i < vmClass.methodTable.length; ++i) { + if (((! publicOnly) + || ((vmClass.methodTable[i].flags & Modifier.PUBLIC)) + != 0) + && Method.getName(vmClass.methodTable[i]).equals("")) + { + ++ count; + } + } + } + return count; + } + + public Constructor[] getDeclaredConstructors() { + Constructor[] array = new Constructor[countConstructors(false)]; + if (vmClass.methodTable != null) { + Classes.link(vmClass); + + int index = 0; + for (int i = 0; i < vmClass.methodTable.length; ++i) { + if (Method.getName(vmClass.methodTable[i]).equals("")) { + array[index++] = new Constructor(new Method(vmClass.methodTable[i])); + } + } + } + + return array; + } + + public Constructor[] getConstructors() { + Constructor[] array = new Constructor[countConstructors(true)]; + if (vmClass.methodTable != null) { + Classes.link(vmClass); + + int index = 0; + for (int i = 0; i < vmClass.methodTable.length; ++i) { + if (((vmClass.methodTable[i].flags & Modifier.PUBLIC) != 0) + && Method.getName(vmClass.methodTable[i]).equals("")) + { + array[index++] = new Constructor(new Method(vmClass.methodTable[i])); + } + } + } + + return array; + } + + public Field[] getDeclaredFields() { + if (vmClass.fieldTable != null) { + Field[] array = new Field[vmClass.fieldTable.length]; + for (int i = 0; i < vmClass.fieldTable.length; ++i) { + array[i] = new Field(vmClass.fieldTable[i]); + } + return array; + } else { + return new Field[0]; + } + } + + private int countPublicFields() { + int count = 0; + if (vmClass.fieldTable != null) { + for (int i = 0; i < vmClass.fieldTable.length; ++i) { + if (((vmClass.fieldTable[i].flags & Modifier.PUBLIC)) != 0) { + ++ count; + } + } + } + return count; + } + + public Field[] getFields() { + Field[] array = new Field[countPublicFields()]; + if (vmClass.fieldTable != null) { + Classes.link(vmClass); + + int ai = 0; + for (int i = 0; i < vmClass.fieldTable.length; ++i) { + if (((vmClass.fieldTable[i].flags & Modifier.PUBLIC)) != 0) { + array[ai++] = new Field(vmClass.fieldTable[i]); + } + } + } + return array; + } + + private static void getAllFields(VMClass vmClass, ArrayList fields) { + if (vmClass.super_ != null) { + getAllFields(vmClass.super_, fields); + } + if (vmClass.fieldTable != null) { + Classes.link(vmClass); + + for (int i = 0; i < vmClass.fieldTable.length; ++i) { + fields.add(new Field(vmClass.fieldTable[i])); + } + } + } + + public Field[] getAllFields() { + ArrayList fields = new ArrayList(); + getAllFields(vmClass, fields); + return fields.toArray(new Field[fields.size()]); + } + + public Method[] getDeclaredMethods() { + return Classes.getMethods(vmClass, false); + } + + public Method[] getMethods() { + return Classes.getMethods(vmClass, true); + } + + public Class[] getInterfaces() { + ClassAddendum addendum = vmClass.addendum; + if (addendum != null) { + Object[] table = addendum.interfaceTable; + if (table != null) { + Class[] array = new Class[table.length]; + for (int i = 0; i < table.length; ++i) { + array[i] = SystemClassLoader.getClass((VMClass) table[i]); + } + return array; + } + } + return new Class[0]; + } + + public native Class getEnclosingClass(); + + public native Method getEnclosingMethod(); + + public native Constructor getEnclosingConstructor(); + + public T[] getEnumConstants() { + if (Enum.class.isAssignableFrom(this)) { + try { + return (T[]) getMethod("values").invoke(null); + } catch (Exception e) { + throw new Error(); + } + } else { + return null; + } + } + + public Class[] getDeclaredClasses() { + ClassAddendum addendum = vmClass.addendum; + if (addendum != null) { + InnerClassReference[] table = addendum.innerClassTable; + if (table != null) { + int count = 0; + for (int i = 0; i < table.length; ++i) { + InnerClassReference reference = table[i]; + if (reference.outer != null + && Arrays.equals(vmClass.name, reference.outer)) + { + ++ count; + } + } + + Class[] result = new Class[count]; + for (int i = 0; i < table.length; ++i) { + InnerClassReference reference = table[i]; + if (reference.outer != null + && Arrays.equals(vmClass.name, reference.outer)) + { + try { + result[--count] = getClassLoader().loadClass + (new String(reference.inner, 0, reference.inner.length - 1)); + } catch (ClassNotFoundException e) { + throw new Error(e); + } + } + } + + return result; + } + } + return new Class[0]; + } + + public Class getDeclaringClass() { + ClassAddendum addendum = vmClass.addendum; + if (addendum != null) { + InnerClassReference[] table = addendum.innerClassTable; + if (table != null) { + for (int i = 0; i < table.length; ++i) { + InnerClassReference reference = table[i]; + if (Arrays.equals(vmClass.name, reference.inner)) { + if (reference.outer != null) { + try { + return getClassLoader().loadClass + (new String(reference.outer, 0, reference.outer.length - 1)); + } catch (ClassNotFoundException e) { + throw new Error(e); + } + } else { + return null; + } + } + } + } + } + return null; + } + + public ClassLoader getClassLoader() { + return vmClass.loader; + } + + public int getModifiers() { + ClassAddendum addendum = vmClass.addendum; + if (addendum != null) { + InnerClassReference[] table = addendum.innerClassTable; + if (table != null) { + for (int i = 0; i < table.length; ++i) { + InnerClassReference reference = table[i]; + if (Arrays.equals(vmClass.name, reference.inner)) { + return reference.flags; + } + } + } + } + + return vmClass.flags; + } + + public boolean isInterface() { + return (vmClass.flags & Modifier.INTERFACE) != 0; + } + + public boolean isAnnotation() { + return (vmClass.flags & 0x2000) != 0; + } + + public Class getSuperclass() { + return (vmClass.super_ == null ? null : SystemClassLoader.getClass(vmClass.super_)); + } + + private enum ClassType { GLOBAL, MEMBER, LOCAL, ANONYMOUS } + + /** + * Determines the class type. + * + * There are four class types: global (no dollar sign), anonymous (only digits after the dollar sign), + * local (starts with digits after the dollar, ends in class name) and member (does not start with digits + * after the dollar sign). + * + * @return the class type + */ + private ClassType getClassType() { + final String name = getName(); + // Find the last dollar, as classes can be nested + int dollar = name.lastIndexOf('$'); + if (dollar < 0) return ClassType.GLOBAL; + + // Find the first non-digit after the dollar, if any + final char[] chars = name.toCharArray(); + int skipDigits; + for (skipDigits = dollar + 1; skipDigits < chars.length; skipDigits++) { + if (chars[skipDigits] < '0' || chars[skipDigits] > '9') break; + } + + if (skipDigits == chars.length) { + return ClassType.ANONYMOUS; + } else if (skipDigits == dollar + 1) { + return ClassType.MEMBER; + } else { + return ClassType.LOCAL; + } + } + + public boolean isAnonymousClass () { + return getClassType() == ClassType.ANONYMOUS; + } + + public boolean isLocalClass () { + return getClassType() == ClassType.LOCAL; + } + + public boolean isMemberClass () { + return getClassType() == ClassType.MEMBER; + } + + public boolean isArray() { + return vmClass.arrayDimensions != 0; + } + + public static boolean isInstance(VMClass c, Object o) { + return o != null && Classes.isAssignableFrom + (c, Classes.getVMClass(o)); + } + + public boolean isInstance(Object o) { + return isInstance(vmClass, o); + } + + public boolean isPrimitive() { + return (vmClass.vmFlags & PrimitiveFlag) != 0; + } + + public boolean isEnum() { + return getSuperclass() == Enum.class && (vmClass.flags & EnumFlag) != 0; + } + + public URL getResource(String path) { + if (path.startsWith("/")) { + path = path.substring(1); + } else { + String name = Classes.makeString + (vmClass.name, 0, vmClass.name.length - 1); + int index = name.lastIndexOf('/'); + if (index >= 0) { + path = name.substring(0, index) + "/" + path; + } + } + return getClassLoader().getResource(path); + } + + public InputStream getResourceAsStream(String path) { + URL url = getResource(path); + try { + return (url == null ? null : url.openStream()); + } catch (IOException e) { + return null; + } + } + + public boolean desiredAssertionStatus() { + return false; + } + + public Class asSubclass(Class c) { + if (! c.isAssignableFrom(this)) { + throw new ClassCastException(); + } + + return (Class) this; + } + + public T cast(Object o) { + return (T) o; + } + + public Package getPackage() { + if ((vmClass.vmFlags & PrimitiveFlag) != 0 || isArray()) { + return null; + } else { + String name = getCanonicalName(); + int index = name.lastIndexOf('.'); + if (index >= 0) { + return getClassLoader().getPackage(name.substring(0, index)); + } else { + return null; + } + } + } + + public boolean isAnnotationPresent + (Class class_) + { + return getAnnotation(class_) != null; + } + + private static Annotation getAnnotation(VMClass c, Object[] a) { + if (a[0] == null) { + a[0] = Proxy.newProxyInstance + (c.loader, new Class[] { (Class) a[1] }, + new AnnotationInvocationHandler(a)); + } + return (Annotation) a[0]; + } + + public T getAnnotation(Class class_) { + for (VMClass c = vmClass; c != null; c = c.super_) { + if (c.addendum != null && c.addendum.annotationTable != null) { + Classes.link(c, c.loader); + + Object[] table = (Object[]) c.addendum.annotationTable; + for (int i = 0; i < table.length; ++i) { + Object[] a = (Object[]) table[i]; + if (a[1] == class_) { + return (T) getAnnotation(c, a); + } + } + } + } + return null; + } + + public Annotation[] getDeclaredAnnotations() { + if (vmClass.addendum.annotationTable != null) { + Classes.link(vmClass); + + Object[] table = (Object[]) vmClass.addendum.annotationTable; + Annotation[] array = new Annotation[table.length]; + for (int i = 0; i < table.length; ++i) { + array[i] = getAnnotation(vmClass, (Object[]) table[i]); + } + return array; + } else { + return new Annotation[0]; + } + } + + private int countAnnotations() { + int count = 0; + for (VMClass c = vmClass; c != null; c = c.super_) { + if (c.addendum != null && c.addendum.annotationTable != null) { + count += ((Object[]) c.addendum.annotationTable).length; + } + } + return count; + } + + public Annotation[] getAnnotations() { + Annotation[] array = new Annotation[countAnnotations()]; + int i = 0; + for (VMClass c = vmClass; c != null; c = c.super_) { + if (c.addendum != null && c.addendum.annotationTable != null) { + Object[] table = (Object[]) c.addendum.annotationTable; + for (int j = 0; j < table.length; ++j) { + array[i++] = getAnnotation(vmClass, (Object[]) table[j]); + } + } + } + + return array; + } + + public ProtectionDomain getProtectionDomain() { + return Classes.getProtectionDomain(vmClass); + } + + public TypeVariable[] getTypeParameters() { + throw new UnsupportedOperationException("not yet implemented"); + } + + /** + * The first one is the superclass, the others are interfaces + **/ + private String[] getGenericTypeSignatures() { + String signature = Classes.toString((byte[]) vmClass.addendum.signature); + final char[] signChars = signature.toCharArray(); + + // Addendum format: + // LBaseClass;LIface1;LIface2;... + // We should split it + + int i = -1; + + // Passing the generic args + int angles = 0; + do { + i++; + if (signChars[i] == '<') angles ++; + else if (signChars[i] == '>') angles --; + } while (angles > 0); + if (signChars[i] == '>') i++; + + // Splitting types list + LinkedList typeSigns = new LinkedList(); + StringBuilder curTypeSign = new StringBuilder(); + for (; i < signChars.length; i++) { + // Counting braces + if (signChars[i] == '<') angles ++; + else if (signChars[i] == '>') angles --; + + // Appending character + curTypeSign.append(signChars[i]); + + // Splitting + if (angles == 0 && signChars[i] == ';') { + typeSigns.add(curTypeSign.toString()); + curTypeSign.setLength(0); + } + } + if (curTypeSign.length() > 0) typeSigns.add(curTypeSign.toString()); + + String[] res = new String[typeSigns.size()]; + return typeSigns.toArray(res); + } + + public Type[] getGenericInterfaces() { + if (vmClass.addendum == null || vmClass.addendum.signature == null) { + return getInterfaces(); + } + + String[] typeSigns = getGenericTypeSignatures(); + if (typeSigns.length < 1) { + throw new RuntimeException("Class signature doesn't contain any type"); + } + + // Parsing types + Type[] res = new Type[typeSigns.length - 1]; + for (int i = 0; i < typeSigns.length - 1; i++) { + res[i] = SignatureParser.parse(vmClass.loader, typeSigns[i + 1], this); + } + + return res; + } + + public Type getGenericSuperclass() { + if (vmClass.addendum == null || vmClass.addendum.signature == null) { + return getSuperclass(); + } + String[] typeSigns = getGenericTypeSignatures(); + if (typeSigns.length < 1) { + throw new RuntimeException("Class signature doesn't contain any type"); + } + + return SignatureParser.parse(vmClass.loader, typeSigns[0], this); + } + +} diff --git a/sgx-jvm/avian/classpath/java/lang/ClassCastException.java b/sgx-jvm/avian/classpath/java/lang/ClassCastException.java new file mode 100644 index 0000000000..c244f63ca7 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/ClassCastException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class ClassCastException extends RuntimeException { + public ClassCastException(String message) { + super(message); + } + + public ClassCastException() { + super(); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/ClassLoader.java b/sgx-jvm/avian/classpath/java/lang/ClassLoader.java new file mode 100644 index 0000000000..85404fa924 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/ClassLoader.java @@ -0,0 +1,213 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +import java.io.InputStream; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Map; +import java.util.HashMap; +import java.security.ProtectionDomain; + +public abstract class ClassLoader { + private final ClassLoader parent; + private Map packages; + + protected ClassLoader(ClassLoader parent) { + if (parent == null) { + this.parent = getSystemClassLoader(); + } else { + this.parent = parent; + } + } + + protected ClassLoader() { + this(getSystemClassLoader()); + } + + private Map packages() { + if (packages == null) { + packages = new HashMap(); + } + return packages; + } + + protected Package getPackage(String name) { + synchronized (this) { + return packages().get(name); + } + } + + protected Package[] getPackages() { + synchronized (this) { + return packages().values().toArray(new Package[packages().size()]); + } + } + + protected Package definePackage(String name, + String specificationTitle, + String specificationVersion, + String specificationVendor, + String implementationTitle, + String implementationVersion, + String implementationVendor, + URL sealBase) + { + Package p = new Package + (name, implementationTitle, implementationVersion, + implementationVendor, specificationTitle, specificationVersion, + specificationVendor, sealBase, this); + + synchronized (this) { + packages().put(name, p); + return p; + } + } + + public static ClassLoader getSystemClassLoader() { + return ClassLoader.class.getClassLoader(); + } + + protected Class defineClass(String name, byte[] b, int offset, int length) { + if (b == null) { + throw new NullPointerException(); + } + + if (offset < 0 || offset > length || offset + length > b.length) { + throw new IndexOutOfBoundsException(); + } + + return avian.SystemClassLoader.getClass + (avian.Classes.defineVMClass(this, b, offset, length)); + } + + protected Class defineClass(String name, byte[] b, int offset, int length, + ProtectionDomain domain) + { + return defineClass(name, b, offset, length); + } + + protected Class findClass(String name) throws ClassNotFoundException { + throw new ClassNotFoundException(); + } + + protected Class reallyFindLoadedClass(String name) { + return null; + } + + protected final Class findLoadedClass(String name) { + return reallyFindLoadedClass(name); + } + + public Class loadClass(String name) throws ClassNotFoundException { + return loadClass(name, false); + } + + protected Class loadClass(String name, boolean resolve) + throws ClassNotFoundException + { + Class c = findLoadedClass(name); + if (c == null) { + if (parent != null) { + try { + c = parent.loadClass(name); + } catch (ClassNotFoundException ok) { } + } + + if (c == null) { + c = findClass(name); + } + } + + if (resolve) { + resolveClass(c); + } + + return c; + } + + protected void resolveClass(Class c) { + avian.Classes.link(c.vmClass, this); + } + + public final ClassLoader getParent() { + return parent; + } + + protected URL findResource(String path) { + return null; + } + + protected Enumeration findResources(String name) throws IOException { + return Collections.enumeration(new ArrayList(0)); + } + + public URL getResource(String path) { + URL url = null; + if (parent != null) { + url = parent.getResource(path); + } + + if (url == null) { + url = findResource(path); + } + + return url; + } + + public InputStream getResourceAsStream(String path) { + URL url = getResource(path); + try { + return (url == null ? null : url.openStream()); + } catch (IOException e) { + return null; + } + } + + public static URL getSystemResource(String path) { + return getSystemClassLoader().getResource(path); + } + + public static InputStream getSystemResourceAsStream(String path) { + return getSystemClassLoader().getResourceAsStream(path); + } + + public static Enumeration getSystemResources(String name) throws IOException { + return getSystemClassLoader().getResources(name); + } + + public Enumeration getResources(String name) + throws IOException { + Collection resources = collectResources(name); + return Collections.enumeration(resources); + } + + private Collection collectResources(String name) { + Collection urls = parent != null ? parent.collectResources(name) : new ArrayList(5); + URL url = findResource(name); + if (url != null) { + urls.add(url); + } + return urls; + } + + protected String findLibrary(String name) { + return null; + } + + static native Class getCaller(); + + static native void load(String name, Class caller, boolean mapName); +} diff --git a/sgx-jvm/avian/classpath/java/lang/ClassNotFoundException.java b/sgx-jvm/avian/classpath/java/lang/ClassNotFoundException.java new file mode 100644 index 0000000000..9562f95d88 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/ClassNotFoundException.java @@ -0,0 +1,33 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class ClassNotFoundException extends Exception { + private final Throwable cause2; + + public ClassNotFoundException(String message, Throwable cause) { + super(message, cause); + cause2 = cause; + } + + public ClassNotFoundException(String message) { + this(message, null); + } + + public ClassNotFoundException() { + this(null, null); + } + + public Throwable getException() { + return cause2; + } + +} diff --git a/sgx-jvm/avian/classpath/java/lang/CloneNotSupportedException.java b/sgx-jvm/avian/classpath/java/lang/CloneNotSupportedException.java new file mode 100644 index 0000000000..b29f66a547 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/CloneNotSupportedException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class CloneNotSupportedException extends Exception { + public CloneNotSupportedException(String message) { + super(message); + } + + public CloneNotSupportedException() { + super(); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/Cloneable.java b/sgx-jvm/avian/classpath/java/lang/Cloneable.java new file mode 100644 index 0000000000..84e202c12e --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Cloneable.java @@ -0,0 +1,13 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public interface Cloneable { } diff --git a/sgx-jvm/avian/classpath/java/lang/Comparable.java b/sgx-jvm/avian/classpath/java/lang/Comparable.java new file mode 100644 index 0000000000..fdf345bb08 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Comparable.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public interface Comparable { + public int compareTo(T o); +} diff --git a/sgx-jvm/avian/classpath/java/lang/Deprecated.java b/sgx-jvm/avian/classpath/java/lang/Deprecated.java new file mode 100644 index 0000000000..b7e833d254 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Deprecated.java @@ -0,0 +1,17 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Deprecated { } diff --git a/sgx-jvm/avian/classpath/java/lang/Double.java b/sgx-jvm/avian/classpath/java/lang/Double.java new file mode 100644 index 0000000000..ff224dd54e --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Double.java @@ -0,0 +1,119 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public final class Double extends Number { + public static final Class TYPE = avian.Classes.forCanonicalName("D"); + + public static final double NEGATIVE_INFINITY = -1.0 / 0.0; + public static final double POSITIVE_INFINITY = 1.0 / 0.0; + public static final double NaN = 0.0 / 0.0; + + public static final double MIN_VALUE = 2.22507385850720138309e-308; + public static final double MAX_VALUE = 1.79769313486231570815e+308; + + private final double value; + + public Double(String value) { + this.value = parseDouble(value); + } + + public Double(double value) { + this.value = value; + } + + public static Double valueOf(double value) { + return new Double(value); + } + + public static Double valueOf(String s) { + return new Double(s); + } + + public boolean equals(Object o) { + return o instanceof Double && ((Double) o).value == value; + } + + public int hashCode() { + long v = doubleToRawLongBits(value); + return (int) ((v >> 32) ^ (v & 0xFF)); + } + + public String toString() { + return toString(value); + } + + public static String toString(double v) { + byte[] buffer = new byte[20]; + int numChars = fillBufferWithDouble(v, buffer, 20); + return new String(buffer, 0, numChars, false); + } + + public byte byteValue() { + return (byte) value; + } + + public short shortValue() { + return (short) value; + } + + public int intValue() { + return (int) value; + } + + public long longValue() { + return (long) value; + } + + public float floatValue() { + return (float) value; + } + + public double doubleValue() { + return value; + } + + public boolean isInfinite() { + return isInfinite(value); + } + + public boolean isNaN() { + return isNaN(value); + } + + public static double parseDouble(String s) { + int[] numRead = new int[1]; + double d = doubleFromString(s, numRead); + if (numRead[0] == 1) { + return d; + } else { + throw new NumberFormatException(s); + } + } + + public static long doubleToLongBits(double value) { + if (isNaN(value)) return 0x7ff8000000000000L; + return doubleToRawLongBits(value); + } + + public static native int fillBufferWithDouble(double value, byte[] buffer, + int charCount); + + public static native long doubleToRawLongBits(double value); + + public static native double longBitsToDouble(long bits); + + public static native boolean isInfinite(double value); + + public static native boolean isNaN(double value); + + public static native double doubleFromString(String s, int[] numRead); +} diff --git a/sgx-jvm/avian/classpath/java/lang/Enum.java b/sgx-jvm/avian/classpath/java/lang/Enum.java new file mode 100644 index 0000000000..38ea903e5d --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Enum.java @@ -0,0 +1,72 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +import java.lang.reflect.Method; + +public abstract class Enum> implements Comparable { + private final String name; + protected final int ordinal; + + public Enum(String name, int ordinal) { + this.name = name; + this.ordinal = ordinal; + } + + public int compareTo(E other) { + if (getDeclaringClass() != other.getDeclaringClass()) { + throw new ClassCastException(); + } + + return ordinal - other.ordinal; + } + + public static > T valueOf(Class enumType, String name) { + if (name == null) throw new NullPointerException("name"); + if (!enumType.isEnum()) + throw new IllegalArgumentException(enumType.getCanonicalName() + " is not an enum."); + + try { + Method method = enumType.getMethod("values"); + Enum values[] = (Enum[]) method.invoke(null); + for (Enum value: values) { + if (name.equals(value.name)) { + return (T) value; + } + } + } catch (Exception ex) { + // Cannot happen + throw new Error(ex); + } + + throw new IllegalArgumentException(enumType.getCanonicalName() + "." + name + " is not an enum constant."); + } + + public int ordinal() { + return ordinal; + } + + public final String name() { + return name; + } + + public String toString() { + return name; + } + + public Class getDeclaringClass() { + Class c = getClass(); + while (c.getSuperclass() != Enum.class) { + c = c.getSuperclass(); + } + return c; + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/Error.java b/sgx-jvm/avian/classpath/java/lang/Error.java new file mode 100644 index 0000000000..8851c25979 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Error.java @@ -0,0 +1,29 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class Error extends Throwable { + public Error(String message, Throwable cause) { + super(message, cause); + } + + public Error(String message) { + this(message, null); + } + + public Error(Throwable cause) { + this(null, cause); + } + + public Error() { + this(null, null); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/Exception.java b/sgx-jvm/avian/classpath/java/lang/Exception.java new file mode 100644 index 0000000000..09bec5b62d --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Exception.java @@ -0,0 +1,29 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class Exception extends Throwable { + public Exception(String message, Throwable cause) { + super(message, cause); + } + + public Exception(String message) { + this(message, null); + } + + public Exception(Throwable cause) { + this(null, cause); + } + + public Exception() { + this(null, null); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/ExceptionInInitializerError.java b/sgx-jvm/avian/classpath/java/lang/ExceptionInInitializerError.java new file mode 100644 index 0000000000..4e56a2a1f6 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/ExceptionInInitializerError.java @@ -0,0 +1,24 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class ExceptionInInitializerError extends Error { + private final Throwable exception; + + public ExceptionInInitializerError(String message) { + super(message); + exception = null; + } + + public ExceptionInInitializerError() { + this(null); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/Float.java b/sgx-jvm/avian/classpath/java/lang/Float.java new file mode 100644 index 0000000000..3bebc1079c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Float.java @@ -0,0 +1,121 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public final class Float extends Number { + public static final Class TYPE = avian.Classes.forCanonicalName("F"); + private static final int EXP_BIT_MASK = 0x7F800000; + private static final int SIGNIF_BIT_MASK = 0x007FFFFF; + + public static final float NEGATIVE_INFINITY = -1.0f / 0.0f; + public static final float POSITIVE_INFINITY = 1.0f / 0.0f; + public static final float NaN = 0.0f / 0.0f; + + public static final float MIN_VALUE = 1.17549435082228750797e-38f; + public static final float MAX_VALUE = 3.40282346638528859812e+38f; + + private final float value; + + public Float(String value) { + this.value = parseFloat(value); + } + + public Float(float value) { + this.value = value; + } + + public static Float valueOf(float value) { + return new Float(value); + } + + public static Float valueOf(String s) { + return new Float(s); + } + + public boolean equals(Object o) { + return o instanceof Float && ((Float) o).value == value; + } + + public int hashCode() { + return floatToRawIntBits(value); + } + + public String toString() { + return toString(value); + } + + public static String toString(float v) { + return Double.toString(v); + } + + public byte byteValue() { + return (byte) value; + } + + public short shortValue() { + return (short) value; + } + + public int intValue() { + return (int) value; + } + + public long longValue() { + return (long) value; + } + + public float floatValue() { + return value; + } + + public double doubleValue() { + return (double) value; + } + + public boolean isInfinite() { + return isInfinite(value); + } + + public boolean isNaN() { + return isNaN(value); + } + + public static float parseFloat(String s) { + int[] numRead = new int[1]; + float f = floatFromString(s, numRead); + if (numRead[0] == 1) { + return f; + } else { + throw new NumberFormatException(s); + } + } + + public static int floatToIntBits(float value) { + int result = floatToRawIntBits(value); + + // Check for NaN based on values of bit fields, maximum + // exponent and nonzero significand. + if (((result & EXP_BIT_MASK) == EXP_BIT_MASK) && (result & SIGNIF_BIT_MASK) != 0) { + result = 0x7fc00000; + } + return result; + } + + public static native int floatToRawIntBits(float value); + + public static native float intBitsToFloat(int bits); + + public static native boolean isInfinite(float value); + + public static native boolean isNaN(float value); + + public static native float floatFromString(String s, int[] numRead); +} diff --git a/sgx-jvm/avian/classpath/java/lang/IllegalAccessError.java b/sgx-jvm/avian/classpath/java/lang/IllegalAccessError.java new file mode 100644 index 0000000000..c53ba72f4d --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/IllegalAccessError.java @@ -0,0 +1,26 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +/** + * TODO : current Avian runtime doesn't check, need to be implemented. + * + */ +public class IllegalAccessError extends IncompatibleClassChangeError { + + public IllegalAccessError(String message) { + super(message); + } + + public IllegalAccessError() { + } + +} diff --git a/sgx-jvm/avian/classpath/java/lang/IllegalAccessException.java b/sgx-jvm/avian/classpath/java/lang/IllegalAccessException.java new file mode 100644 index 0000000000..e4205fb176 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/IllegalAccessException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class IllegalAccessException extends Exception { + public IllegalAccessException(String message) { + super(message); + } + + public IllegalAccessException() { + super(); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/IllegalArgumentException.java b/sgx-jvm/avian/classpath/java/lang/IllegalArgumentException.java new file mode 100644 index 0000000000..124810a25c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/IllegalArgumentException.java @@ -0,0 +1,29 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class IllegalArgumentException extends RuntimeException { + public IllegalArgumentException(String message, Throwable cause) { + super(message, cause); + } + + public IllegalArgumentException(String message) { + this(message, null); + } + + public IllegalArgumentException(Throwable cause) { + this(null, cause); + } + + public IllegalArgumentException() { + this(null, null); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/IllegalMonitorStateException.java b/sgx-jvm/avian/classpath/java/lang/IllegalMonitorStateException.java new file mode 100644 index 0000000000..31b65b587f --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/IllegalMonitorStateException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class IllegalMonitorStateException extends RuntimeException { + public IllegalMonitorStateException(String message) { + super(message, null); + } + + public IllegalMonitorStateException() { + this(null); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/IllegalStateException.java b/sgx-jvm/avian/classpath/java/lang/IllegalStateException.java new file mode 100644 index 0000000000..5374f35e4c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/IllegalStateException.java @@ -0,0 +1,29 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class IllegalStateException extends RuntimeException { + public IllegalStateException(String message, Throwable cause) { + super(message, cause); + } + + public IllegalStateException(String message) { + this(message, null); + } + + public IllegalStateException(Throwable cause) { + this(null, cause); + } + + public IllegalStateException() { + this(null, null); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/IllegalThreadStateException.java b/sgx-jvm/avian/classpath/java/lang/IllegalThreadStateException.java new file mode 100644 index 0000000000..3defa035d7 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/IllegalThreadStateException.java @@ -0,0 +1,30 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class IllegalThreadStateException extends IllegalArgumentException { + public IllegalThreadStateException(String message, Throwable cause) { + super(message, cause); + } + + public IllegalThreadStateException(String message) { + this(message, null); + } + + public IllegalThreadStateException(Throwable cause) { + this(null, cause); + } + + public IllegalThreadStateException() { + this(null, null); + } + +} diff --git a/sgx-jvm/avian/classpath/java/lang/IncompatibleClassChangeError.java b/sgx-jvm/avian/classpath/java/lang/IncompatibleClassChangeError.java new file mode 100644 index 0000000000..3af1db4c88 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/IncompatibleClassChangeError.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class IncompatibleClassChangeError extends LinkageError { + public IncompatibleClassChangeError(String message) { + super(message); + } + + public IncompatibleClassChangeError() { + super(); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/IndexOutOfBoundsException.java b/sgx-jvm/avian/classpath/java/lang/IndexOutOfBoundsException.java new file mode 100644 index 0000000000..35bbef659b --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/IndexOutOfBoundsException.java @@ -0,0 +1,29 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class IndexOutOfBoundsException extends RuntimeException { + public IndexOutOfBoundsException(String message, Throwable cause) { + super(message, cause); + } + + public IndexOutOfBoundsException(String message) { + this(message, null); + } + + public IndexOutOfBoundsException(Throwable cause) { + this(null, cause); + } + + public IndexOutOfBoundsException() { + this(null, null); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/InheritableThreadLocal.java b/sgx-jvm/avian/classpath/java/lang/InheritableThreadLocal.java new file mode 100644 index 0000000000..cf26eb07ad --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/InheritableThreadLocal.java @@ -0,0 +1,19 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +import java.util.Map; + +public class InheritableThreadLocal extends ThreadLocal { + protected T childValue(T parentValue) { + return parentValue; + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/InstantiationError.java b/sgx-jvm/avian/classpath/java/lang/InstantiationError.java new file mode 100644 index 0000000000..d659bfdfbd --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/InstantiationError.java @@ -0,0 +1,26 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +/** + * TODO : current Avian runtime doesn't check, need to be implemented. + * + */ +public class InstantiationError extends IncompatibleClassChangeError { + + public InstantiationError(String message) { + super(message); + } + + public InstantiationError() { + } + +} diff --git a/sgx-jvm/avian/classpath/java/lang/InstantiationException.java b/sgx-jvm/avian/classpath/java/lang/InstantiationException.java new file mode 100644 index 0000000000..112484b9f4 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/InstantiationException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class InstantiationException extends Exception { + public InstantiationException(String message) { + super(message); + } + + public InstantiationException() { + super(); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/Integer.java b/sgx-jvm/avian/classpath/java/lang/Integer.java new file mode 100644 index 0000000000..0dbf20ae21 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Integer.java @@ -0,0 +1,142 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public final class Integer extends Number implements Comparable { + public static final Class TYPE = avian.Classes.forCanonicalName("I"); + + public static final int MIN_VALUE = 0x80000000; + public static final int MAX_VALUE = 0x7FFFFFFF; + + private final int value; + + public Integer(int value) { + this.value = value; + } + + public Integer(String s) { + this.value = parseInt(s); + } + + public static Integer valueOf(int value) { + return new Integer(value); + } + + public static Integer valueOf(String value) { + return valueOf(parseInt(value)); + } + + public boolean equals(Object o) { + return o instanceof Integer && ((Integer) o).value == value; + } + + public int hashCode() { + return value; + } + + public int compareTo(Integer other) { + return value - other.value; + } + + public String toString() { + return toString(value); + } + + public static String toString(int v, int radix) { + return Long.toString(v, radix); + } + + public static String toString(int v) { + return toString(v, 10); + } + + public static String toHexString(int v) { + return Long.toString(((long) v) & 0xFFFFFFFFL, 16); + } + + public static String toOctalString(int v) { + return Long.toString(((long) v) & 0xFFFFFFFFL, 8); + } + + public static String toBinaryString(int v) { + return Long.toString(((long) v) & 0xFFFFFFFFL, 2); + } + + public byte byteValue() { + return (byte) value; + } + + public short shortValue() { + return (short) value; + } + + public int intValue() { + return value; + } + + public long longValue() { + return value; + } + + public float floatValue() { + return (float) value; + } + + public double doubleValue() { + return (double) value; + } + + public static int signum(int v) { + if (v == 0) return 0; + else if (v > 0) return 1; + else return -1; + } + + // See http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel + public static int bitCount(int v) { + v = v - ((v >> 1) & 0x55555555); + v = (v & 0x33333333) + ((v >> 2) & 0x33333333); + return ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; + } + + public static int reverseBytes(int v) { + int byte3 = v >>> 24; + int byte2 = (v >>> 8) & 0xFF00; + int byte1 = (v << 8) & 0xFF00; + int byte0 = v << 24; + return (byte0 | byte1 | byte2 | byte3); + } + + public static int parseInt(String s) { + return parseInt(s, 10); + } + + public static int parseInt(String s, int radix) { + return (int) Long.parseLong(s, radix); + } + + public static Integer decode(String string) { + if (string.startsWith("-")) { + if (string.startsWith("-0") || string.startsWith("-#")) { + return new Integer(-decode(string.substring(1))); + } + } else if (string.startsWith("0")) { + char c = string.length() < 2 ? (char)-1 : string.charAt(1); + if (c == 'x' || c == 'X') { + return new Integer(parseInt(string.substring(2), 0x10)); + } + return new Integer(parseInt(string, 010)); + } else if (string.startsWith("#")) { + return new Integer(parseInt(string.substring(1), 0x10)); + } + return new Integer(parseInt(string, 10)); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/InternalError.java b/sgx-jvm/avian/classpath/java/lang/InternalError.java new file mode 100644 index 0000000000..69ac254db4 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/InternalError.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class InternalError extends VirtualMachineError { + public InternalError(String message) { + super(message); + } + + public InternalError() { + this(null); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/InterruptedException.java b/sgx-jvm/avian/classpath/java/lang/InterruptedException.java new file mode 100644 index 0000000000..9a4c0d8178 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/InterruptedException.java @@ -0,0 +1,29 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class InterruptedException extends Exception { + public InterruptedException(String message, Throwable cause) { + super(message, cause); + } + + public InterruptedException(String message) { + this(message, null); + } + + public InterruptedException(Throwable cause) { + this(null, cause); + } + + public InterruptedException() { + this(null, null); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/Iterable.java b/sgx-jvm/avian/classpath/java/lang/Iterable.java new file mode 100644 index 0000000000..4618e2ced7 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Iterable.java @@ -0,0 +1,17 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +import java.util.Iterator; + +public interface Iterable { + public Iterator iterator(); +} diff --git a/sgx-jvm/avian/classpath/java/lang/LinkageError.java b/sgx-jvm/avian/classpath/java/lang/LinkageError.java new file mode 100644 index 0000000000..05313ffc1c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/LinkageError.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class LinkageError extends Error { + public LinkageError(String message) { + super(message, null); + } + + public LinkageError() { + this(null); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/Long.java b/sgx-jvm/avian/classpath/java/lang/Long.java new file mode 100644 index 0000000000..dda7ed1ed6 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Long.java @@ -0,0 +1,174 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public final class Long extends Number implements Comparable { + public static final long MIN_VALUE = -9223372036854775808l; + public static final long MAX_VALUE = 9223372036854775807l; + + public static final Class TYPE = avian.Classes.forCanonicalName("J"); + + private final long value; + + public Long(long value) { + this.value = value; + } + + public Long(String s) { + this.value = parseLong(s); + } + + public static Long valueOf(String value) { + return new Long(value); + } + + public static Long valueOf(long value) { + return new Long(value); + } + + public int compareTo(Long o) { + return value > o.value ? 1 : (value < o.value ? -1 : 0); + } + + public boolean equals(Object o) { + return o instanceof Long && ((Long) o).value == value; + } + + public int hashCode() { + return (int) ((value >> 32) ^ (value & 0xFF)); + } + + public String toString() { + return String.valueOf(value); + } + + public static String toString(long v, int radix) { + if (radix < 1 || radix > 36) { + throw new IllegalArgumentException("radix " + radix + " not in [1,36]"); + } + + if (v == 0) { + return "0"; + } + + boolean negative = v < 0; + + int size = (negative ? 1 : 0); + for (long n = v; n != 0; n /= radix) ++size; + + char[] array = new char[size]; + + int i = size - 1; + for (long n = v; n != 0; n /= radix) { + long digit = n % radix; + if (negative) digit = -digit; + + if (digit >= 0 && digit <= 9) { + array[i] = (char) ('0' + digit); + } else { + array[i] = (char) ('a' + (digit - 10)); + } + --i; + } + + if (negative) { + array[i] = '-'; + } + + return new String(array, 0, size, false); + } + + public static String toString(long v) { + return toString(v, 10); + } + + public static String toHexString(long v) { + return toString(v, 16); + } + + public static String toOctalString(long v) { + return toString(v, 8); + } + + public static String toBinaryString(long v) { + return toString(v, 2); + } + + public byte byteValue() { + return (byte) value; + } + + public short shortValue() { + return (short) value; + } + + public int intValue() { + return (int) value; + } + + public long longValue() { + return value; + } + + public float floatValue() { + return (float) value; + } + + public double doubleValue() { + return (double) value; + } + + public static int signum(long v) { + if (v == 0) return 0; + else if (v > 0) return 1; + else return -1; + } + + private static long pow(long a, long b) { + long c = 1; + for (int i = 0; i < b; ++i) c *= a; + return c; + } + + public static long parseLong(String s) { + return parseLong(s, 10); + } + + public static long parseLong(String s, int radix) { + int i = 0; + long number = 0; + boolean negative = s.startsWith("-"); + int length = s.length(); + if (negative) { + i = 1; + -- length; + } + + long factor = pow(radix, length - 1); + for (; i < s.length(); ++i) { + char c = s.charAt(i); + int digit = Character.digit(c, radix); + if (digit >= 0) { + number += digit * factor; + factor /= radix; + } else { + throw new NumberFormatException("invalid character " + c + " code " + + (int) c); + } + } + + if (negative) { + number = -number; + } + + return number; + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/Math.java b/sgx-jvm/avian/classpath/java/lang/Math.java new file mode 100644 index 0000000000..c1d1c0bdaf --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Math.java @@ -0,0 +1,124 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +import java.util.Random; + +public final class Math { + public static final double E = 2.718281828459045; + public static final double PI = 3.141592653589793; + + private static class Static { + public static final Random random = new Random(); + } + + private Math() { } + + public static double max(double a, double b) { + return (a < b ? b : a); + } + + public static double min(double a, double b) { + return (a > b ? b : a); + } + + public static float max(float a, float b) { + return (a < b ? b : a); + } + + public static float min(float a, float b) { + return (a > b ? b : a); + } + + public static long max(long a, long b) { + return (a < b ? b : a); + } + + public static long min(long a, long b) { + return (a > b ? b : a); + } + + public static int max(int a, int b) { + return (a < b ? b : a); + } + + public static int min(int a, int b) { + return (a > b ? b : a); + } + + public static int abs(int v) { + return (v < 0 ? -v : v); + } + + public static long abs(long v) { + return (v < 0 ? -v : v); + } + + public static float abs(float v) { + return (v < 0 ? -v : v); + } + + public static double abs(double v) { + return (v < 0 ? -v : v); + } + + public static long round(double v) { + return (long) Math.floor(v + 0.5); + } + + public static int round(float v) { + return (int) Math.floor(v + 0.5); + } + + public static double signum(double d) { + return d > 0 ? +1.0 : d < 0 ? -1.0 : 0; + } + + public static float signum(float f) { + return f > 0 ? +1.0f : f < 0 ? -1.0f : 0; + } + + public static double random() { + return Static.random.nextDouble(); + } + + public static native double floor(double v); + + public static native double ceil(double v); + + public static native double exp(double v); + + public static native double log(double v); + + public static native double cos(double v); + + public static native double sin(double v); + + public static native double tan(double v); + + public static native double cosh(double v); + + public static native double sinh(double v); + + public static native double tanh(double v); + + public static native double acos(double v); + + public static native double asin(double v); + + public static native double atan(double v); + + public static native double atan2(double y, double x); + + public static native double sqrt(double v); + + public static native double pow(double v, double e); +} diff --git a/sgx-jvm/avian/classpath/java/lang/NegativeArraySizeException.java b/sgx-jvm/avian/classpath/java/lang/NegativeArraySizeException.java new file mode 100644 index 0000000000..a51048ad1b --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/NegativeArraySizeException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class NegativeArraySizeException extends RuntimeException { + public NegativeArraySizeException(String message) { + super(message); + } + + public NegativeArraySizeException() { + super(); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/NoClassDefFoundError.java b/sgx-jvm/avian/classpath/java/lang/NoClassDefFoundError.java new file mode 100644 index 0000000000..1b01a3b150 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/NoClassDefFoundError.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class NoClassDefFoundError extends LinkageError { + public NoClassDefFoundError(String message) { + super(message); + } + + public NoClassDefFoundError() { + super(); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/NoSuchFieldError.java b/sgx-jvm/avian/classpath/java/lang/NoSuchFieldError.java new file mode 100644 index 0000000000..1fde5606c6 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/NoSuchFieldError.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class NoSuchFieldError extends IncompatibleClassChangeError { + public NoSuchFieldError(String message) { + super(message); + } + + public NoSuchFieldError() { + super(); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/NoSuchFieldException.java b/sgx-jvm/avian/classpath/java/lang/NoSuchFieldException.java new file mode 100644 index 0000000000..1323172b7b --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/NoSuchFieldException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class NoSuchFieldException extends Exception { + public NoSuchFieldException(String message) { + super(message); + } + + public NoSuchFieldException() { + super(); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/NoSuchMethodError.java b/sgx-jvm/avian/classpath/java/lang/NoSuchMethodError.java new file mode 100644 index 0000000000..0bf772a3fb --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/NoSuchMethodError.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class NoSuchMethodError extends IncompatibleClassChangeError { + public NoSuchMethodError(String message) { + super(message); + } + + public NoSuchMethodError() { + super(); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/NoSuchMethodException.java b/sgx-jvm/avian/classpath/java/lang/NoSuchMethodException.java new file mode 100644 index 0000000000..63e1fb9ddc --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/NoSuchMethodException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class NoSuchMethodException extends Exception { + public NoSuchMethodException(String message) { + super(message); + } + + public NoSuchMethodException() { + super(); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/NullPointerException.java b/sgx-jvm/avian/classpath/java/lang/NullPointerException.java new file mode 100644 index 0000000000..ba4c87c365 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/NullPointerException.java @@ -0,0 +1,29 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class NullPointerException extends RuntimeException { + public NullPointerException(String message, Throwable cause) { + super(message, cause); + } + + public NullPointerException(String message) { + this(message, null); + } + + public NullPointerException(Throwable cause) { + this(null, cause); + } + + public NullPointerException() { + this(null, null); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/Number.java b/sgx-jvm/avian/classpath/java/lang/Number.java new file mode 100644 index 0000000000..bbfff3a72e --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Number.java @@ -0,0 +1,22 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +import java.io.Serializable; + +public abstract class Number implements Serializable { + public abstract byte byteValue(); + public abstract short shortValue(); + public abstract int intValue(); + public abstract long longValue(); + public abstract float floatValue(); + public abstract double doubleValue(); +} diff --git a/sgx-jvm/avian/classpath/java/lang/NumberFormatException.java b/sgx-jvm/avian/classpath/java/lang/NumberFormatException.java new file mode 100644 index 0000000000..9b38d55a09 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/NumberFormatException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class NumberFormatException extends IllegalArgumentException { + public NumberFormatException(String message) { + super(message); + } + + public NumberFormatException() { + super(); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/Object.java b/sgx-jvm/avian/classpath/java/lang/Object.java new file mode 100644 index 0000000000..0ad1b78e82 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Object.java @@ -0,0 +1,58 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class Object { + protected Object clone() throws CloneNotSupportedException { + if ((this instanceof Cloneable) || getClass().isArray()) { + return clone(this); + } else { + throw new CloneNotSupportedException(getClass().getName()); + } + } + + private static native Object clone(Object o); + + public boolean equals(Object o) { + return this == o; + } + + protected void finalize() throws Throwable { } + + public final Class getClass() { + return avian.SystemClassLoader.getClass(getVMClass(this)); + } + + private static native avian.VMClass getVMClass(Object o); + + public native int hashCode(); + + public native final void notify(); + + public native final void notifyAll(); + + public native String toString(); + + public final void wait() throws InterruptedException { + wait(0); + } + + public native final void wait(long milliseconds) throws InterruptedException; + + public final void wait(long milliseconds, int nanoseconds) + throws InterruptedException + { + if (nanoseconds != 0) { + ++ milliseconds; + } + wait(milliseconds); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/OutOfMemoryError.java b/sgx-jvm/avian/classpath/java/lang/OutOfMemoryError.java new file mode 100644 index 0000000000..3dc9830b3c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/OutOfMemoryError.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class OutOfMemoryError extends VirtualMachineError { + public OutOfMemoryError(String message) { + super(message); + } + + public OutOfMemoryError() { + this(null); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/Override.java b/sgx-jvm/avian/classpath/java/lang/Override.java new file mode 100644 index 0000000000..78161ff9a8 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Override.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public @interface Override { + +} diff --git a/sgx-jvm/avian/classpath/java/lang/Package.java b/sgx-jvm/avian/classpath/java/lang/Package.java new file mode 100644 index 0000000000..f982d48caf --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Package.java @@ -0,0 +1,82 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +import java.net.URL; + +public class Package { + private final String name; + private final String implementationTitle; + private final String implementationVendor; + private final String implementationVersion; + private final String specificationTitle; + private final String specificationVendor; + private final String specificationVersion; + private final URL sealed; + private final ClassLoader loader; + + Package(String name, + String implementationTitle, + String implementationVendor, + String implementationVersion, + String specificationTitle, + String specificationVendor, + String specificationVersion, + URL sealed, + ClassLoader loader) + { + this.name = name; + this.implementationTitle = implementationTitle; + this.implementationVendor = implementationVendor; + this.implementationVersion = implementationVersion; + this.specificationTitle = specificationTitle; + this.specificationVendor = specificationVendor; + this.specificationVersion = specificationVersion; + this.sealed = sealed; + this.loader = loader; + } + + public String getName() { + return name; + } + + public String getImplementationTitle() { + return implementationTitle; + } + + public String getImplementationVendor() { + return implementationVendor; + } + + public String getImplementationVersion() { + return implementationVersion; + } + + public String getSpecificationTitle() { + return specificationTitle; + } + + public String getSpecificationVendor() { + return specificationVendor; + } + + public String getSpecificationVersion() { + return specificationVersion; + } + + public boolean isSealed() { + return sealed != null; + } + + public boolean isSealed(URL url) { + return sealed.equals(url); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/Process.java b/sgx-jvm/avian/classpath/java/lang/Process.java new file mode 100644 index 0000000000..12f3f550c3 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Process.java @@ -0,0 +1,28 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +import java.io.InputStream; +import java.io.OutputStream; + +public abstract class Process { + public abstract void destroy(); + + public abstract int exitValue(); + + public abstract InputStream getInputStream(); + + public abstract OutputStream getOutputStream(); + + public abstract InputStream getErrorStream(); + + public abstract int waitFor() throws InterruptedException; +} diff --git a/sgx-jvm/avian/classpath/java/lang/Readable.java b/sgx-jvm/avian/classpath/java/lang/Readable.java new file mode 100644 index 0000000000..f54e4d95ce --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Readable.java @@ -0,0 +1,18 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +import java.nio.CharBuffer; +import java.io.IOException; + +public interface Readable { + int read(CharBuffer buffer) throws IOException; +} diff --git a/sgx-jvm/avian/classpath/java/lang/ReflectiveOperationException.java b/sgx-jvm/avian/classpath/java/lang/ReflectiveOperationException.java new file mode 100644 index 0000000000..add39584e0 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/ReflectiveOperationException.java @@ -0,0 +1,13 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class ReflectiveOperationException extends Exception { } diff --git a/sgx-jvm/avian/classpath/java/lang/Runnable.java b/sgx-jvm/avian/classpath/java/lang/Runnable.java new file mode 100644 index 0000000000..ef186d197c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Runnable.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public interface Runnable { + public void run(); +} diff --git a/sgx-jvm/avian/classpath/java/lang/Runtime.java b/sgx-jvm/avian/classpath/java/lang/Runtime.java new file mode 100644 index 0000000000..e73d74b35b --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Runtime.java @@ -0,0 +1,185 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +import java.io.IOException; +import java.io.InputStream; +import java.io.FileInputStream; +import java.io.OutputStream; +import java.io.FileOutputStream; +import java.io.FileDescriptor; +import java.util.StringTokenizer; + +public class Runtime { + private static final Runtime instance = new Runtime(); + + private Runtime() { } + + public static Runtime getRuntime() { + return instance; + } + + public void load(String path) { + if (path != null) { + ClassLoader.load(path, ClassLoader.getCaller(), false); + } else { + throw new NullPointerException(); + } + } + + public void loadLibrary(String path) { + if (path != null) { + ClassLoader.load(path, ClassLoader.getCaller(), true); + } else { + throw new NullPointerException(); + } + } + + public Process exec(String command) throws IOException { + StringTokenizer t = new StringTokenizer(command); + String[] cmd = new String[t.countTokens()]; + for (int i = 0; i < cmd.length; i++) + cmd[i] = t.nextToken(); + + return exec(cmd); + } + + public Process exec(final String[] command) throws IOException { + final MyProcess[] process = new MyProcess[1]; + final Throwable[] exception = new Throwable[1]; + + synchronized (process) { + Thread t = new Thread() { + public void run() { + synchronized (process) { + try { + long[] info = new long[5]; + exec(command, info); + process[0] = new MyProcess + (info[0], info[1], (int) info[2], (int) info[3], + (int) info[4]); + } catch (Throwable e) { + exception[0] = e; + } finally { + process.notifyAll(); + } + } + + MyProcess p = process[0]; + if (p != null) { + synchronized (p) { + try { + if (p.pid != 0) { + p.exitCode = Runtime.waitFor(p.pid, p.tid); + p.pid = 0; + p.tid = 0; + } + } finally { + p.notifyAll(); + } + } + } + } + }; + t.setDaemon(true); + t.start(); + + while (process[0] == null && exception[0] == null) { + try { + process.wait(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + + if (exception[0] != null) { + if (exception[0] instanceof IOException) { + String message = "Failed to run \"" + command[0] + "\": " + exception[0].getMessage(); + throw new IOException(message); + } else { + throw new RuntimeException(exception[0]); + } + } + + return process[0]; + } + + public native void addShutdownHook(Thread t); + + private static native void exec(String[] command, long[] process) + throws IOException; + + private static native int waitFor(long pid, long tid); + + private static native void kill(long pid); + + public native void gc(); + + public native void exit(int code); + + public native long freeMemory(); + + public native long totalMemory(); + + public native long maxMemory(); + + private static class MyProcess extends Process { + private long pid; + private long tid; + private final int in; + private final int out; + private final int err; + private int exitCode; + + public MyProcess(long pid, long tid, int in, int out, int err) { + this.pid = pid; + this.tid = tid; + this.in = in; + this.out = out; + this.err = err; + } + + public void destroy() { + if (pid != 0) { + kill(pid); + } + } + + public InputStream getInputStream() { + return new FileInputStream(new FileDescriptor(in)); + } + + public OutputStream getOutputStream() { + return new FileOutputStream(new FileDescriptor(out)); + } + + public InputStream getErrorStream() { + return new FileInputStream(new FileDescriptor(err)); + } + + public synchronized int exitValue() { + if (pid != 0) { + throw new IllegalThreadStateException(); + } + + return exitCode; + } + + public synchronized int waitFor() throws InterruptedException { + while (pid != 0) { + wait(); + } + + return exitCode; + } + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/RuntimeException.java b/sgx-jvm/avian/classpath/java/lang/RuntimeException.java new file mode 100644 index 0000000000..8eb8ecffb5 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/RuntimeException.java @@ -0,0 +1,29 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class RuntimeException extends Exception { + public RuntimeException(String message, Throwable cause) { + super(message, cause); + } + + public RuntimeException(String message) { + this(message, null); + } + + public RuntimeException(Throwable cause) { + this(null, cause); + } + + public RuntimeException() { + this(null, null); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/RuntimePermission.java b/sgx-jvm/avian/classpath/java/lang/RuntimePermission.java new file mode 100644 index 0000000000..b61e22eff9 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/RuntimePermission.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +import java.security.BasicPermission; + +public class RuntimePermission extends BasicPermission { + + public RuntimePermission(String name) { + super(name); + } + +} diff --git a/sgx-jvm/avian/classpath/java/lang/SecurityException.java b/sgx-jvm/avian/classpath/java/lang/SecurityException.java new file mode 100644 index 0000000000..af0fa9612f --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/SecurityException.java @@ -0,0 +1,29 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class SecurityException extends RuntimeException { + public SecurityException(String message, Throwable cause) { + super(message, cause); + } + + public SecurityException(String message) { + this(message, null); + } + + public SecurityException(Throwable cause) { + this(null, cause); + } + + public SecurityException() { + this(null, null); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/SecurityManager.java b/sgx-jvm/avian/classpath/java/lang/SecurityManager.java new file mode 100644 index 0000000000..29c6b65f74 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/SecurityManager.java @@ -0,0 +1,30 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +import java.security.AccessController; +import java.security.Permission; +import java.security.SecurityPermission; + +public class SecurityManager { + + public SecurityManager() { + } + + public void checkPermission(Permission perm) { + AccessController.checkPermission(perm); + } + + public void checkSecurityAccess(String target) { + checkPermission(new SecurityPermission(target)); + } + +} diff --git a/sgx-jvm/avian/classpath/java/lang/Short.java b/sgx-jvm/avian/classpath/java/lang/Short.java new file mode 100644 index 0000000000..409eae36c4 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Short.java @@ -0,0 +1,76 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public final class Short extends Number implements Comparable { + public static final Class TYPE = avian.Classes.forCanonicalName("S"); + + public static final short MIN_VALUE = -32768; + public static final short MAX_VALUE = 32767; + + private final short value; + + public Short(short value) { + this.value = value; + } + + public static Short valueOf(short value) { + return new Short(value); + } + + public int compareTo(Short o) { + return value - o.value; + } + + public boolean equals(Object o) { + return o instanceof Short && ((Short) o).value == value; + } + + public int hashCode() { + return value; + } + + public String toString() { + return toString(value); + } + + public static String toString(short v, int radix) { + return Long.toString(v, radix); + } + + public static String toString(short v) { + return toString(v, 10); + } + + public byte byteValue() { + return (byte) value; + } + + public short shortValue() { + return value; + } + + public int intValue() { + return value; + } + + public long longValue() { + return value; + } + + public float floatValue() { + return (float) value; + } + + public double doubleValue() { + return (double) value; + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/StackOverflowError.java b/sgx-jvm/avian/classpath/java/lang/StackOverflowError.java new file mode 100644 index 0000000000..9bb536e681 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/StackOverflowError.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class StackOverflowError extends VirtualMachineError { + public StackOverflowError(String message) { + super(message); + } + + public StackOverflowError() { + this(null); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/StackTraceElement.java b/sgx-jvm/avian/classpath/java/lang/StackTraceElement.java new file mode 100644 index 0000000000..25b6e57afd --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/StackTraceElement.java @@ -0,0 +1,77 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class StackTraceElement { + private static int NativeLine = -1; + + private String class_; + private String method; + private String file; + private int line; + + public StackTraceElement(String class_, String method, String file, + int line) + { + this.class_ = class_; + this.method = method; + this.file = file; + this.line = line; + } + + public int hashCode() { + return class_.hashCode() ^ method.hashCode() ^ line; + } + + public boolean equals(Object o) { + if (o instanceof StackTraceElement) { + StackTraceElement e = (StackTraceElement) o; + return class_.equals(e.class_) + && method.equals(e.method) + && line == e.line; + } else { + return false; + } + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(class_).append(".").append(method); + + if (line == NativeLine) { + sb.append(" (native)"); + } else if (line >= 0) { + sb.append(" (line ").append(line).append(")"); + } + + return sb.toString(); + } + + public String getClassName() { + return class_; + } + + public String getMethodName() { + return method; + } + + public String getFileName() { + return file; + } + + public int getLineNumber() { + return line; + } + + public boolean isNativeMethod() { + return line == NativeLine; + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/String.java b/sgx-jvm/avian/classpath/java/lang/String.java new file mode 100644 index 0000000000..be45b3c464 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/String.java @@ -0,0 +1,698 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.util.Comparator; +import java.util.Formatter; +import java.util.Locale; +import java.util.regex.Pattern; + +import avian.Iso88591; +import avian.Utf8; + +public final class String + implements Comparable, CharSequence, Serializable +{ + private static final String UTF_8_ENCODING = "UTF-8"; + private static final String ISO_8859_1_ENCODING = "ISO-8859-1"; + private static final String LATIN_1_ENCODING = "LATIN-1"; + private static final String DEFAULT_ENCODING = UTF_8_ENCODING; + + public static Comparator CASE_INSENSITIVE_ORDER + = new Comparator() { + @Override + public int compare(String a, String b) { + return a.compareToIgnoreCase(b); + } + }; + + private final Object data; + private final int offset; + private final int length; + private int hashCode; + + public String() { + this(new char[0], 0, 0); + } + + public String(char[] data, int offset, int length, boolean copy) { + this((Object) data, offset, length, copy); + } + + public String(char[] data, int offset, int length) { + this(data, offset, length, true); + } + + public String(char[] data) { + this(data, 0, data.length); + } + + public String(byte bytes[], int offset, int length, String charsetName) + throws UnsupportedEncodingException + { + this(bytes, offset, length); + if (! (charsetName.equalsIgnoreCase(UTF_8_ENCODING) + || charsetName.equalsIgnoreCase(ISO_8859_1_ENCODING))) + { + throw new UnsupportedEncodingException(charsetName); + } + } + + public String(byte[] data, int offset, int length, boolean copy) { + this((Object) data, offset, length, copy); + } + + public String(byte[] data, int offset, int length) { + this(data, offset, length, true); + } + + public String(byte[] data) { + this(data, 0, data.length); + } + + public String(String s) { + this(s.toCharArray()); + } + + public String(byte[] data, String charset) + throws UnsupportedEncodingException + { + this(data, 0, data.length, charset); + } + + public String(byte bytes[], int highByte, int offset, int length) { + if (offset < 0 ) + throw new StringIndexOutOfBoundsException(offset); + else if (offset + length > bytes.length) + throw new StringIndexOutOfBoundsException(offset + length); + else if (length < 0) + throw new StringIndexOutOfBoundsException(length); + + char[] c = new char[length]; + int mask = highByte << 8; + for (int i = 0; i < length; ++i) { + c[i] = (char) ((bytes[offset + i] & 0xFF) | mask); + } + + this.data = c; + this.offset = 0; + this.length = length; + } + + private String(Object data, int offset, int length, boolean copy) { + int l; + if (data instanceof char[]) { + l = ((char[]) data).length; + } else { + l = ((byte[]) data).length; + } + + if (offset < 0 ) + throw new StringIndexOutOfBoundsException(offset); + else if (offset + length > l) + throw new StringIndexOutOfBoundsException(offset + length); + else if (length < 0) + throw new StringIndexOutOfBoundsException(length); + + if(!copy && Utf8.test(data)) copy = true; + + if (copy) { + Object c; + if (data instanceof char[]) { + c = new char[length]; + System.arraycopy(data, offset, c, 0, length); + } else { + c = Utf8.decode((byte[])data, offset, length); + if(c instanceof char[]) length = ((char[])c).length; + if (c == null) { + throw new RuntimeException + ("unable to parse \"" + new String(data, offset, length, false) + + "\""); + } + } + + this.data = c; + this.offset = 0; + this.length = length; + } else { + this.data = data; + this.offset = offset; + this.length = length; + } + } + + @Override + public String toString() { + return this; + } + + @Override + public int length() { + return length; + } + + @Override + public int hashCode() { + if (hashCode == 0) { + int h = 0; + for (int i = 0; i < length; ++i) h = (h * 31) + charAt(i); + hashCode = h; + } + return hashCode; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o instanceof String) { + String s = (String) o; + return s.length == length && compareTo(s) == 0; + } else { + return false; + } + } + + public boolean equalsIgnoreCase(String s) { + if (this == s) { + return true; + } else { + return s != null && s.length == length && compareToIgnoreCase(s) == 0; + } + } + + @Override + public int compareTo(String s) { + if (this == s) return 0; + + int idx = 0; + int result; + + int end = (length < s.length ? length : s.length); + + while (idx < end) { + if ((result = charAt(idx) - s.charAt(idx)) != 0) { + return result; + } + idx++; + } + return length - s.length; + } + + public int compareToIgnoreCase(String s) { + if (this == s) return 0; + + int idx = 0; + int result; + + int end = (length < s.length ? length : s.length); + + while (idx < end) { + if ((result = + Character.toLowerCase(charAt(idx)) - + Character.toLowerCase(s.charAt(idx))) != 0) { + return result; + } + idx++; + } + return length - s.length; + } + + public String trim() { + int start = -1; + for (int i = 0; i < length; ++i) { + char c = charAt(i); + if (start == -1 && ! Character.isWhitespace(c)) { + start = i; + break; + } + } + + int end = -1; + for (int i = length - 1; i >= 0; --i) { + char c = charAt(i); + if (end == -1 && ! Character.isWhitespace(c)) { + end = i + 1; + break; + } + } + + if (start >= end) { + return ""; + } else { + return substring(start, end); + } + } + + public String toLowerCase() { + for (int j = 0; j < length; ++j) { + char ch = charAt(j); + if (Character.toLowerCase(ch) != ch) { + char[] b = new char[length]; + for (int i = 0; i < length; ++i) { + b[i] = Character.toLowerCase(charAt(i)); + } + return new String(b, 0, length, false); + } + } + return this; + } + + public String toUpperCase() { + for (int j = 0; j < length; ++j) { + char ch = charAt(j); + if (Character.toUpperCase(ch) != ch) { + char[] b = new char[length]; + for (int i = 0; i < length; ++i) { + b[i] = Character.toUpperCase(charAt(i)); + } + return new String(b, 0, length, false); + } + } + return this; + } + + public int indexOf(int c) { + return indexOf(c, 0); + } + + public int indexOf(int c, int start) { + for (int i = start; i < length; ++i) { + if (charAt(i) == c) { + return i; + } + } + + return -1; + } + + public int lastIndexOf(int ch) { + return lastIndexOf(ch, length-1); + } + + public int indexOf(String s) { + return indexOf(s, 0); + } + + public int indexOf(String s, int start) { + if (s.length == 0) return start; + + for (int i = start; i < length - s.length + 1; ++i) { + int j = 0; + for (; j < s.length; ++j) { + if (charAt(i + j) != s.charAt(j)) { + break; + } + } + if (j == s.length) { + return i; + } + } + + return -1; + } + + public int lastIndexOf(String s) { + return lastIndexOf(s, length - s.length); + } + + public int lastIndexOf(String s, int lastIndex) { + if (s.length == 0) return lastIndex; + + for (int i = Math.min(length - s.length, lastIndex); i >= 0; --i) { + int j = 0; + for (; j < s.length && i + j < length; ++j) { + if (charAt(i + j) != s.charAt(j)) { + break; + } + } + if (j == s.length) { + return i; + } + } + + return -1; + } + + public String replace(char oldChar, char newChar) { + if (data instanceof char[]) { + char[] buf = new char[length]; + for (int i=0; i < length; i++) { + if (charAt(i) == oldChar) { + buf[i] = newChar; + } else { + buf[i] = charAt(i); + } + } + return new String(buf, 0, length, false); + } else { + byte[] buf = new byte[length]; + byte[] orig = (byte[])data; + byte oldByte = (byte)oldChar; + byte newByte = (byte)newChar; + for (int i=0; i < length; i++) { + if (orig[i+offset] == oldByte) { + buf[i] = newByte; + } else { + buf[i] = orig[i+offset]; + } + } + return new String(buf, 0, length, false); + } + } + + public String substring(int start) { + return substring(start, length); + } + + public String substring(int start, int end) { + if (start < 0) + throw new StringIndexOutOfBoundsException(start); + else if (end > length) + throw new StringIndexOutOfBoundsException(end); + int newLen = end - start; + if (newLen < 0) + throw new StringIndexOutOfBoundsException(newLen); + + if (start == 0 && end == length) + return this; + else if (end - start == 0) + return ""; + else + return new String(data, offset + start, newLen, false); + } + + public boolean startsWith(String s) { + if (length >= s.length) { + return substring(0, s.length).compareTo(s) == 0; + } else { + return false; + } + } + + public boolean startsWith(String s, int start) { + if (length >= s.length + start) { + return substring(start, s.length).compareTo(s) == 0; + } else { + return false; + } + } + + public boolean endsWith(String s) { + if (length >= s.length) { + return substring(length - s.length).compareTo(s) == 0; + } else { + return false; + } + } + + public String concat(String s) { + if (s.length() == 0) { + return this; + } else { + return this + s; + } + } + + public void getBytes(int srcOffset, int srcLength, + byte[] dst, int dstOffset) + { + if (srcOffset < 0) + throw new StringIndexOutOfBoundsException(srcOffset); + else if (srcOffset + srcLength > length) + throw new StringIndexOutOfBoundsException(srcOffset + srcLength); + else if (srcLength < 0) + throw new StringIndexOutOfBoundsException(srcLength); + + if (data instanceof char[]) { + char[] src = (char[]) data; + for (int i = 0; i < srcLength; ++i) { + dst[i + dstOffset] = (byte) src[i + offset + srcOffset]; + } + } else { + byte[] src = (byte[]) data; + System.arraycopy(src, offset + srcOffset, dst, dstOffset, srcLength); + } + } + + public byte[] getBytes() { + try { + return getBytes(DEFAULT_ENCODING); + } catch (java.io.UnsupportedEncodingException ex) { + throw new RuntimeException( + "Default '" + DEFAULT_ENCODING + "' encoding not handled", ex); + } + } + + public byte[] getBytes(String format) + throws java.io.UnsupportedEncodingException + { + if(data instanceof byte[]) { + byte[] b = new byte[length]; + getBytes(0, length, b, 0); + return b; + } + String fmt = format.trim().toUpperCase(); + if (DEFAULT_ENCODING.equals(fmt)) { + return Utf8.encode((char[])data, offset, length); + } else if (ISO_8859_1_ENCODING.equals(fmt) || LATIN_1_ENCODING.equals(fmt)) { + return Iso88591.encode((char[])data, offset, length); + } else { + throw new java.io.UnsupportedEncodingException( + "Encoding " + format + " not supported"); + } + } + + public void getChars(int srcOffset, int srcEnd, + char[] dst, int dstOffset) + { + if (srcOffset < 0) + throw new StringIndexOutOfBoundsException(srcOffset); + else if (srcEnd > length) + throw new StringIndexOutOfBoundsException(srcEnd); + + int srcLength = srcEnd-srcOffset; + if (data instanceof char[]) { + char[] src = (char[]) data; + System.arraycopy(src, offset + srcOffset, dst, dstOffset, srcLength); + } else { + byte[] src = (byte[]) data; + for (int i = 0; i < srcLength; ++i) { + dst[i + dstOffset] = (char) src[i + offset + srcOffset]; + } + } + } + + public char[] toCharArray() { + char[] b = new char[length]; + getChars(0, length, b, 0); + return b; + } + + @Override + public char charAt(int index) { + if (index < 0 || index > length) { + throw new StringIndexOutOfBoundsException(index); + } + + if (data instanceof char[]) { + return ((char[]) data)[index + offset]; + } else { + return (char) ((byte[]) data)[index + offset]; + } + } + + public String[] split(String regex) { + return split(regex, 0); + } + + public String[] split(String regex, int limit) { + return Pattern.compile(regex).split(this, limit); + } + + @Override + public CharSequence subSequence(int start, int end) { + return substring(start, end); + } + + public boolean matches(String regex) { + return Pattern.matches(regex, this); + } + + public String replaceFirst(String regex, String replacement) { + return Pattern.compile(regex).matcher(this).replaceFirst(replacement); + } + + public String replaceAll(String regex, String replacement) { + return Pattern.compile(regex).matcher(this).replaceAll(replacement); + } + + public String replace(CharSequence target, CharSequence replace) { + if (target.length() == 0) { + return this.infuse(replace.toString()); + } + + String targetString = target.toString(); + String replaceString = replace.toString(); + + int targetSize = target.length(); + + StringBuilder returnValue = new StringBuilder(); + String unhandled = this; + + int index = -1; + while ((index = unhandled.indexOf(targetString)) != -1) { + returnValue.append(unhandled.substring(0, index)).append(replaceString); + unhandled = unhandled.substring(index + targetSize, + unhandled.length()); + } + + returnValue.append(unhandled); + return returnValue.toString(); + } + + private String infuse(String infuseWith) { + StringBuilder retVal = new StringBuilder(); + + String me = this; + for (int i = 0; i < me.length(); i++) { + retVal.append(infuseWith).append(me.substring(i, i + 1)); + } + + retVal.append(infuseWith); + return retVal.toString(); + } + + public native String intern(); + + public static String format(String fmt, Object... args) { + final Formatter formatter = new Formatter(); + final String result = formatter.format(fmt, args).toString(); + formatter.close(); + return result; + } + + public static String format(Locale l, String fmt, Object... args) { + final Formatter formatter = new Formatter(); + final String result = formatter.format(l, fmt, args).toString(); + formatter.close(); + return result; + } + + public static String valueOf(Object s) { + return s == null ? "null" : s.toString(); + } + + public static String valueOf(boolean v) { + return Boolean.toString(v); + } + + public static String valueOf(byte v) { + return Byte.toString(v); + } + + public static String valueOf(short v) { + return Short.toString(v); + } + + public static String valueOf(char v) { + return Character.toString(v); + } + + public static String valueOf(int v) { + return Integer.toString(v); + } + + public static String valueOf(long v) { + return Long.toString(v); + } + + public static String valueOf(float v) { + return Float.toString(v); + } + + public static String valueOf(double v) { + return Double.toString(v); + } + + public static String valueOf(char[] data, int offset, int length) { + return new String(data, offset, length); + } + + public static String valueOf(char[] data) { + return valueOf(data, 0, data.length); + } + + public int lastIndexOf(int ch, int lastIndex) { + if (lastIndex >= length) { + lastIndex = length - 1; + } + for (int i = lastIndex ; i >= 0; --i) { + if (charAt(i) == ch) { + return i; + } + } + + return -1; + } + + public boolean regionMatches(int thisOffset, String match, int matchOffset, + int length) + { + return regionMatches(false, thisOffset, match, matchOffset, length); + } + + public boolean regionMatches(boolean ignoreCase, int thisOffset, + String match, int matchOffset, int length) + { + String a = substring(thisOffset, thisOffset + length); + String b = match.substring(matchOffset, matchOffset + length); + if (ignoreCase) { + return a.equalsIgnoreCase(b); + } else { + return a.equals(b); + } + } + + public boolean isEmpty() { + return length == 0; + } + + public boolean contains(CharSequence match) { + return indexOf(match.toString()) != -1; + } + + public int codePointAt(int offset) { + return Character.codePointAt(this, offset); + } + + public int codePointCount(int start, int end) { + return Character.codePointCount(this, start, end); + } + + public String toUpperCase(Locale locale) { + if (locale == Locale.ENGLISH) { + return toUpperCase(); + } else { + throw new UnsupportedOperationException("toUpperCase("+locale+')'); + } + } + + public String toLowerCase(Locale locale) { + if (locale == Locale.ENGLISH) { + return toLowerCase(); + } else { + throw new UnsupportedOperationException("toLowerCase("+locale+')'); + } + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/StringBuffer.java b/sgx-jvm/avian/classpath/java/lang/StringBuffer.java new file mode 100644 index 0000000000..66ff1051ae --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/StringBuffer.java @@ -0,0 +1,159 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class StringBuffer implements CharSequence { + private final StringBuilder sb; + + public StringBuffer(String s) { + sb = new StringBuilder(s); + } + + public StringBuffer(int capacity) { + sb = new StringBuilder(capacity); + } + + public StringBuffer() { + this(0); + } + + public synchronized StringBuffer append(String s) { + sb.append(s); + return this; + } + + public synchronized StringBuffer append(CharSequence s) { + sb.append(s); + return this; + } + + public synchronized StringBuffer append(StringBuffer s) { + sb.append(s); + return this; + } + + public synchronized StringBuffer append(Object o) { + sb.append(o); + return this; + } + + public synchronized StringBuffer append(char v) { + sb.append(v); + return this; + } + + public synchronized StringBuffer append(boolean v) { + sb.append(v); + return this; + } + + public synchronized StringBuffer append(int v) { + sb.append(v); + return this; + } + + public synchronized StringBuffer append(long v) { + sb.append(v); + return this; + } + + public synchronized StringBuffer append(float v) { + sb.append(v); + return this; + } + + public synchronized StringBuffer append(double v) { + sb.append(v); + return this; + } + + public synchronized StringBuffer append(char[] b, int offset, int length) { + sb.append(b, offset, length); + return this; + } + + public synchronized StringBuffer append(char[] b) { + sb.append(b, 0, b.length); + return this; + } + + public synchronized int indexOf(String s) { + return sb.indexOf(s); + } + + public synchronized int indexOf(String s, int fromIndex) { + return sb.indexOf(s, fromIndex); + } + + public synchronized StringBuffer insert(int i, String s) { + sb.insert(i, s); + return this; + } + + public synchronized StringBuffer insert(int i, char c) { + sb.insert(i, c); + return this; + } + + public synchronized StringBuffer insert(int i, int v) { + sb.insert(i, v); + return this; + } + + public synchronized StringBuffer delete(int start, int end) { + sb.delete(start, end); + return this; + } + + public synchronized StringBuffer deleteCharAt(int i) { + sb.deleteCharAt(i); + return this; + } + + public synchronized char charAt(int i) { + return sb.charAt(i); + } + + public synchronized int length() { + return sb.length(); + } + + public synchronized StringBuffer replace(int start, int end, String str) { + sb.replace(start, end, str); + return this; + } + + public synchronized void setLength(int v) { + sb.setLength(v); + } + + public synchronized void setCharAt(int index, char ch) { + sb.setCharAt(index, ch); + } + + public synchronized void getChars(int srcStart, int srcEnd, char[] dst, + int dstStart) + { + sb.getChars(srcStart, srcEnd, dst, dstStart); + } + + public synchronized String toString() { + return sb.toString(); + } + + public String substring(int start, int end) { + return sb.substring(start, end); + } + + public CharSequence subSequence(int start, int end) { + return sb.subSequence(start, end); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/StringBuilder.java b/sgx-jvm/avian/classpath/java/lang/StringBuilder.java new file mode 100644 index 0000000000..b5bf4b6725 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/StringBuilder.java @@ -0,0 +1,384 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class StringBuilder implements CharSequence, Appendable { + private static final int BufferSize = 32; + + private Cell chain; + private int length; + private char[] buffer; + private int position; + + public StringBuilder(String s) { + append(s); + } + + public StringBuilder(int capacity) { } + + public StringBuilder() { + this(0); + } + + private void flush() { + if (position > 0) { + chain = new Cell(new String(buffer, 0, position, false), chain); + buffer = null; + position = 0; + } + } + + public StringBuilder append(String s) { + if (s == null) { + return append("null"); + } else { + if (s.length() > 0) { + if (buffer != null && s.length() <= buffer.length - position) { + s.getChars(0, s.length(), buffer, position); + position += s.length(); + } else { + flush(); + chain = new Cell(s, chain); + } + length += s.length(); + } + return this; + } + } + + public StringBuilder append(StringBuffer sb) { + return append(sb.toString()); + } + + public StringBuilder append(CharSequence sequence) { + return append(sequence.toString()); + } + + public Appendable append(CharSequence sequence, int start, int end) { + return append(sequence.subSequence(start, end)); + } + + public StringBuilder append(char[] b, int offset, int length) { + return append(new String(b, offset, length)); + } + + public StringBuilder append(char[] b) { + return append(new String(b)); + } + + public StringBuilder append(Object o) { + return append(o == null ? "null" : o.toString()); + } + + public StringBuilder append(char v) { + if (buffer == null) { + buffer = new char[BufferSize]; + } else if (position >= buffer.length) { + flush(); + buffer = new char[BufferSize]; + } + + buffer[position++] = v; + ++ length; + + return this; + } + + public StringBuilder append(boolean v) { + return append(String.valueOf(v)); + } + + public StringBuilder append(int v) { + return append(String.valueOf(v)); + } + + public StringBuilder append(long v) { + return append(String.valueOf(v)); + } + + public StringBuilder append(float v) { + return append(String.valueOf(v)); + } + + public StringBuilder append(double v) { + return append(String.valueOf(v)); + } + + public char charAt(int i) { + if (i < 0 || i >= length) { + throw new IndexOutOfBoundsException(); + } + + flush(); + + int index = length; + for (Cell c = chain; c != null; c = c.next) { + int start = index - c.value.length(); + index = start; + + if (i >= start) { + return c.value.charAt(i - start); + } + } + + throw new RuntimeException(); + } + + public StringBuilder insert(int i, String s) { + if (i < 0 || i > length) { + throw new IndexOutOfBoundsException(); + } + + if (i == length) { + append(s); + } else { + flush(); + + int index = length; + for (Cell c = chain; c != null; c = c.next) { + int start = index - c.value.length(); + index = start; + + if (i >= start) { + if (i == start) { + c.next = new Cell(s, c.next); + } else { + String v = c.value; + c.value = v.substring(i - start, v.length()); + c.next = new Cell(s, new Cell(v.substring(0, i - start), c.next)); + } + break; + } + } + + length += s.length(); + } + + return this; + } + + public StringBuilder insert(int i, CharSequence s) { + return insert(i, s.toString()); + } + + public StringBuilder insert(int i, char c) { + return insert(i, new String(new char[] { c }, 0, 1, false)); + } + + public StringBuilder insert(int i, int v) { + return insert(i, String.valueOf(v)); + } + + public StringBuilder delete(int start, int end) { + if (start >= end) { + return this; + } + + if (start < 0 || end > length) { + throw new IndexOutOfBoundsException(); + } + + flush(); + + int index = length; + Cell p = null; + for (Cell c = chain; c != null; c = c.next) { + int e = index; + int s = index - c.value.length(); + index = s; + + if (end >= e) { + if (start <= s) { + if (p == null) { + chain = c.next; + } else { + p.next = c.next; + } + } else { + c.value = c.value.substring(0, start - s); + break; + } + } else { + if (start <= s) { + c.value = c.value.substring(end - s, e - s); + } else { + String v = c.value; + c.value = v.substring(end - s, e - s); + c.next = new Cell(v.substring(0, start - s), c.next); + break; + } + } + } + + length -= (end - start); + + return this; + } + + public StringBuilder deleteCharAt(int i) { + return delete(i, i + 1); + } + + public StringBuilder replace(int start, int end, String str) { + delete(start, end); + insert(start, str); + return this; + } + + public int indexOf(String s) { + return indexOf(s, 0); + } + + public int indexOf(String s, int start) { + int slength = s.length(); + if (slength == 0) return start; + + for (int i = start; i < length - slength + 1; ++i) { + int j = 0; + for (; j < slength; ++j) { + if (charAt(i + j) != s.charAt(j)) { + break; + } + } + if (j == slength) { + return i; + } + } + + return -1; + } + + public int lastIndexOf(String s) { + return lastIndexOf(s, length - s.length()); + } + + public int lastIndexOf(String s, int lastIndex) { + int slength = s.length(); + if (slength == 0) return lastIndex; + + for (int i = Math.min(length - slength, lastIndex); i >= 0; --i) { + int j = 0; + for (; j < slength && i + j < length; ++j) { + if (charAt(i + j) != s.charAt(j)) { + break; + } + } + if (j == slength) { + return i; + } + } + + return -1; + } + + public int length() { + return length; + } + + public void setLength(int v) { + if (v < 0) { + throw new IndexOutOfBoundsException(); + } + + if (v == 0) { + length = 0; + chain = null; + return; + } + + flush(); + + int index = length; + length = v; + for (Cell c = chain; c != null; c = c.next) { + int start = index - c.value.length(); + + if (v > start) { + if (v < index) { + c.value = c.value.substring(0, v - start); + } + break; + } + + chain = c.next; + index = start; + } + } + + public void getChars(int srcStart, int srcEnd, char[] dst, int dstStart) { + if (srcStart < 0 || srcEnd > length) { + throw new IndexOutOfBoundsException(); + } + + flush(); + + int index = length; + for (Cell c = chain; c != null; c = c.next) { + int start = index - c.value.length(); + int end = index; + index = start; + + if (start < srcStart) { + start = srcStart; + } + + if (end > srcEnd) { + end = srcEnd; + } + + if (start < end) { + c.value.getChars(start - index, end - index, + dst, dstStart + (start - srcStart)); + } + } + } + + public String toString() { + char[] array = new char[length]; + getChars(0, length, array, 0); + return new String(array, 0, length, false); + } + + private static class Cell { + public String value; + public Cell next; + + public Cell(String value, Cell next) { + this.value = value; + this.next = next; + } + } + + public String substring(int start) { + return substring(start, length); + } + + public String substring(int start, int end) { + int len = end-start; + char[] buf = new char[len]; + getChars(start, end, buf, 0); + return new String(buf, 0, len, false); + } + + public CharSequence subSequence(int start, int end) { + return substring(start, end); + } + + public void setCharAt(int index, char ch) { + if(index < 0 || index >= length) throw new IndexOutOfBoundsException(); + deleteCharAt(index); + insert(index, ch); + } + + public void ensureCapacity(int capacity) { + // ignore + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/StringIndexOutOfBoundsException.java b/sgx-jvm/avian/classpath/java/lang/StringIndexOutOfBoundsException.java new file mode 100644 index 0000000000..b66d9ae43d --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/StringIndexOutOfBoundsException.java @@ -0,0 +1,29 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +/** + * Used by String to signal that a given index is either less than + * or greater than the allowed range. + */ +public class StringIndexOutOfBoundsException extends IndexOutOfBoundsException { + private static final long serialVersionUID = -6762910422159637258L; + + public StringIndexOutOfBoundsException(int index) { + super("String index out of range: "+index); + } + + public StringIndexOutOfBoundsException(String message) { + super(message); + } + + public StringIndexOutOfBoundsException() {} +} diff --git a/sgx-jvm/avian/classpath/java/lang/SuppressWarnings.java b/sgx-jvm/avian/classpath/java/lang/SuppressWarnings.java new file mode 100644 index 0000000000..fd73e8f4c7 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/SuppressWarnings.java @@ -0,0 +1,16 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + + +public @interface SuppressWarnings { + String[] value(); +} diff --git a/sgx-jvm/avian/classpath/java/lang/System.java b/sgx-jvm/avian/classpath/java/lang/System.java new file mode 100644 index 0000000000..f6ceabad6b --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/System.java @@ -0,0 +1,168 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +import java.io.PrintStream; +import java.io.InputStream; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileDescriptor; +import java.util.Map; +import java.util.Hashtable; +import java.util.Properties; + +public abstract class System { + private static class NanoTime { + public static final long BaseInMillis = currentTimeMillis(); + } + + private static class Static { + public static Properties properties = makeProperties(); + } + + private static Map environment; + + private static SecurityManager securityManager; + // static { + // loadLibrary("natives"); + // } + + public static final PrintStream out = new PrintStream + (new BufferedOutputStream(new FileOutputStream(FileDescriptor.out)), true); + + public static final PrintStream err = new PrintStream + (new BufferedOutputStream(new FileOutputStream(FileDescriptor.err)), true); + + public static final InputStream in + = new BufferedInputStream(new FileInputStream(FileDescriptor.in)); + + public static native void arraycopy(Object src, int srcOffset, Object dst, + int dstOffset, int length); + + public static String getProperty(String name) { + return (String) Static.properties.get(name); + } + + public static String getProperty(String name, String defaultValue) { + String result = getProperty(name); + if (result==null) { + return defaultValue; + } + return result; + } + + public static String setProperty(String name, String value) { + return (String) Static.properties.put(name, value); + } + + public static Properties getProperties() { + return Static.properties; + } + + private static Properties makeProperties() { + Properties properties = new Properties(); + + for (String p: getNativeProperties()) { + if (p == null) break; + int index = p.indexOf('='); + properties.put(p.substring(0, index), p.substring(index + 1)); + } + + for (String p: getVMProperties()) { + if (p == null) break; + int index = p.indexOf('='); + properties.put(p.substring(0, index), p.substring(index + 1)); + } + + return properties; + } + + private static native String[] getNativeProperties(); + + private static native String[] getVMProperties(); + + public static native long currentTimeMillis(); + + public static native int identityHashCode(Object o); + + public static long nanoTime() { + return (currentTimeMillis() - NanoTime.BaseInMillis) * 1000000; + } + + public static String mapLibraryName(String name) { + if (name != null) { + return doMapLibraryName(name); + } else { + throw new NullPointerException(); + } + } + + private static native String doMapLibraryName(String name); + + public static void load(String path) { + ClassLoader.load(path, ClassLoader.getCaller(), false); + } + + public static void loadLibrary(String name) { + ClassLoader.load(name, ClassLoader.getCaller(), true); + } + + public static void gc() { + Runtime.getRuntime().gc(); + } + + public static void exit(int code) { + Runtime.getRuntime().exit(code); + } + + public static SecurityManager getSecurityManager() { + return securityManager; + } + + public static void setSecurityManager(SecurityManager securityManager) { + System.securityManager = securityManager; + } + + public static String getenv(String name) throws NullPointerException, + SecurityException { + if (getSecurityManager() != null) { // is this allowed? + getSecurityManager(). + checkPermission(new RuntimePermission("getenv." + name)); + } + return getenv().get(name); + } + + public static Map getenv() throws SecurityException { + if (getSecurityManager() != null) { // is this allowed? + getSecurityManager().checkPermission(new RuntimePermission("getenv.*")); + } + + if (environment == null) { // build environment table + String[] vars = getEnvironment(); + environment = new Hashtable(vars.length); + for (String var : vars) { // parse name-value pairs + int equalsIndex = var.indexOf('='); + // null names and values are forbidden + if (equalsIndex != -1 && equalsIndex < var.length() - 1) { + environment.put(var.substring(0, equalsIndex), + var.substring(equalsIndex + 1)); + } + } + } + + return environment; + } + + /** Returns the native process environment. */ + private static native String[] getEnvironment(); +} diff --git a/sgx-jvm/avian/classpath/java/lang/Thread.java b/sgx-jvm/avian/classpath/java/lang/Thread.java new file mode 100644 index 0000000000..0dea70aae4 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Thread.java @@ -0,0 +1,311 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +import java.util.Map; +import java.util.WeakHashMap; + +public class Thread implements Runnable { + // set and accessed from within LockSupport + protected volatile Object parkBlocker; + + private long peer; + private volatile boolean interrupted; + private volatile boolean unparked; + private boolean daemon; + private byte state; + private byte priority; + private final Runnable task; + private Map locals; + private Object sleepLock; + private ClassLoader classLoader; + private UncaughtExceptionHandler exceptionHandler; + private String name; + private ThreadGroup group; + + private static UncaughtExceptionHandler defaultExceptionHandler; + + public static final int MIN_PRIORITY = 1; + public static final int NORM_PRIORITY = 5; + public static final int MAX_PRIORITY = 10; + + public Thread(ThreadGroup group, Runnable task, String name, long stackSize) + { + this.group = (group == null ? Thread.currentThread().group : group); + this.task = task; + this.name = name; + + Thread current = currentThread(); + + Map map = current.locals; + if (map != null) { + for (Map.Entry e: map.entrySet()) { + if (e.getKey() instanceof InheritableThreadLocal) { + InheritableThreadLocal itl = (InheritableThreadLocal) e.getKey(); + locals().put(itl, itl.childValue(e.getValue())); + } + } + } + + classLoader = current.classLoader; + } + + public Thread(ThreadGroup group, Runnable task, String name) { + this(group, task, name, 0); + } + + public Thread(ThreadGroup group, String name) { + this(null, null, name); + } + + public Thread(Runnable task, String name) { + this(null, task, name); + } + + public Thread(Runnable task) { + this(null, task, "Thread["+task+"]"); + } + + public Thread(String name) { + this(null, null, name); + } + + public Thread() { + this((Runnable) null); + } + + public synchronized void start() { + if (peer != 0) { + throw new IllegalStateException("thread already started"); + } + + state = (byte) State.RUNNABLE.ordinal(); + + peer = doStart(); + if (peer == 0) { + state = (byte) State.NEW.ordinal(); + throw new RuntimeException("unable to start native thread"); + } + } + + private native long doStart(); + + private static void run(Thread t) throws Throwable { + try { + t.run(); + } catch (Throwable e) { + UncaughtExceptionHandler eh = t.exceptionHandler; + UncaughtExceptionHandler deh = defaultExceptionHandler; + if (eh != null) { + eh.uncaughtException(t, e); + } else if (deh != null) { + deh.uncaughtException(t, e); + } else { + throw e; + } + } finally { + synchronized (t) { + t.state = (byte) State.TERMINATED.ordinal(); + t.notifyAll(); + } + } + } + + public void run() { + if (task != null) { + task.run(); + } + } + + public ClassLoader getContextClassLoader() { + return classLoader; + } + + public void setContextClassLoader(ClassLoader v) { + classLoader = v; + } + + public Map locals() { + if (locals == null) { + locals = new WeakHashMap(); + } + return locals; + } + + public static native Thread currentThread(); + + public void interrupt() { + interrupt(peer); + } + + private static native boolean interrupt(long peer); + + public static boolean interrupted() { + return interrupted(currentThread().peer); + } + + private static native boolean interrupted(long peer); + + public boolean isInterrupted() { + return interrupted; + } + + public static void sleep(long milliseconds) throws InterruptedException { + if (milliseconds <= 0) { + milliseconds = 1; + } + + Thread t = currentThread(); + if (t.sleepLock == null) { + t.sleepLock = new Object(); + } + synchronized (t.sleepLock) { + t.sleepLock.wait(milliseconds); + } + } + + public static void sleep(long milliseconds, int nanoseconds) + throws InterruptedException + { + if (nanoseconds > 0) { + ++ milliseconds; + } + + sleep(milliseconds); + } + + public StackTraceElement[] getStackTrace() { + long p = peer; + if (p == 0) { + return new StackTraceElement[0]; + } + return Throwable.resolveTrace(getStackTrace(p)); + } + + private static native Object getStackTrace(long peer); + + public static native int activeCount(); + + public static native int enumerate(Thread[] array); + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public UncaughtExceptionHandler getUncaughtExceptionHandler() { + UncaughtExceptionHandler eh = exceptionHandler; + return (eh == null ? group : eh); + } + + public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() { + return defaultExceptionHandler; + } + + public void setUncaughtExceptionHandler(UncaughtExceptionHandler h) { + exceptionHandler = h; + } + + public static void setDefaultUncaughtExceptionHandler + (UncaughtExceptionHandler h) + { + defaultExceptionHandler = h; + } + + public State getState() { + return State.values()[state]; + } + + public boolean isAlive() { + switch (getState()) { + case NEW: + case TERMINATED: + return false; + + default: + return true; + } + } + + public int getPriority() { + return priority; + } + + public void setPriority(int priority) { + if (priority < MIN_PRIORITY || priority > MAX_PRIORITY) { + throw new IllegalArgumentException(); + } + this.priority = (byte) priority; + } + + public boolean isDaemon() { + return daemon; + } + + public synchronized void setDaemon(boolean v) { + if (getState() != State.NEW) { + throw new IllegalStateException(); + } + + daemon = v; + } + + public static native void yield(); + + public synchronized void join() throws InterruptedException { + while (getState() != State.TERMINATED) { + wait(); + } + } + + public synchronized void join(long milliseconds) throws InterruptedException + { + long then = System.currentTimeMillis(); + long remaining = milliseconds; + while (remaining > 0 && getState() != State.TERMINATED) { + wait(remaining); + + remaining = milliseconds - (System.currentTimeMillis() - then); + } + } + + public void join(long milliseconds, int nanoseconds) + throws InterruptedException + { + if (nanoseconds > 0) { + ++ milliseconds; + } + + join(milliseconds); + } + + public ThreadGroup getThreadGroup() { + return group; + } + + public static native boolean holdsLock(Object o); + + public long getId() { + return peer; + } + + public interface UncaughtExceptionHandler { + public void uncaughtException(Thread t, Throwable e); + } + + public enum State { + NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED + } + +} diff --git a/sgx-jvm/avian/classpath/java/lang/ThreadDeath.java b/sgx-jvm/avian/classpath/java/lang/ThreadDeath.java new file mode 100644 index 0000000000..31711ca64a --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/ThreadDeath.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class ThreadDeath extends Error { + public ThreadDeath() { } +} diff --git a/sgx-jvm/avian/classpath/java/lang/ThreadGroup.java b/sgx-jvm/avian/classpath/java/lang/ThreadGroup.java new file mode 100644 index 0000000000..0aef6ed6a4 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/ThreadGroup.java @@ -0,0 +1,117 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +import avian.Cell; + +public class ThreadGroup implements Thread.UncaughtExceptionHandler { + private final ThreadGroup parent; + private final String name; + private Cell subgroups; + + public ThreadGroup(ThreadGroup parent, String name) { + this.parent = parent; + this.name = name; + + synchronized (parent) { + parent.subgroups = new Cell(this, subgroups); + } + } + + public ThreadGroup(String name) { + this(Thread.currentThread().getThreadGroup(), name); + } + + public void uncaughtException(Thread t, Throwable e) { + if (parent != null) { + parent.uncaughtException(t, e); + } else { + Thread.UncaughtExceptionHandler deh + = Thread.getDefaultUncaughtExceptionHandler(); + if (deh != null) { + deh.uncaughtException(t, e); + } else if (! (e instanceof ThreadDeath)) { + e.printStackTrace(); + } + } + } + + public ThreadGroup getParent() { + return parent; + } + + public String getName() { + return name; + } + + public int activeCount() { + int allCount = Thread.activeCount(); + Thread[] all = new Thread[allCount]; + allCount = Thread.enumerate(all); + + int count = 0; + for (int i = 0; i < allCount; ++i) { + if (parentOf(all[i].getThreadGroup())) { + ++ count; + } + } + + return count; + } + + public int enumerate(Thread[] threads) { + return enumerate(threads, true); + } + + public int enumerate(Thread[] threads, boolean recurse) { + int allCount = Thread.activeCount(); + Thread[] all = new Thread[allCount]; + allCount = Thread.enumerate(all); + + int count = 0; + for (int i = 0; i < allCount && count < threads.length; ++i) { + Thread t = all[i]; + ThreadGroup g = t.getThreadGroup(); + if (g == this || (recurse && parentOf(g))) { + threads[count++] = t; + } + } + + return count; + } + + public boolean parentOf(ThreadGroup g) { + for (; g != null; g = g.parent) { + if (g == this) { + return true; + } + } + + return false; + } + + public int enumerate(ThreadGroup[] groups, boolean recurse) { + return enumerate(groups, recurse, 0); + } + + private int enumerate(ThreadGroup[] groups, boolean recurse, int count) { + for (Cell c = subgroups; c != null && count < groups.length; + c = c.next) + { + ThreadGroup g = c.value; + groups[count++] = g; + if (recurse) { + count = g.enumerate(groups, true, count); + } + } + return count; + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/ThreadLocal.java b/sgx-jvm/avian/classpath/java/lang/ThreadLocal.java new file mode 100644 index 0000000000..b15112a742 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/ThreadLocal.java @@ -0,0 +1,46 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +import java.util.Map; + +public class ThreadLocal { + private static final Object Null = new Object(); + + protected T initialValue() { + return null; + } + + public T get() { + Map map = Thread.currentThread().locals(); + Object o = map.get(this); + if (o == null) { + o = initialValue(); + if (o == null) { + o = Null; + } + map.put(this, o); + } + if (o == Null) { + o = null; + } + return (T) o; + } + + public void set(T value) { + Map map = Thread.currentThread().locals(); + Object o = value; + if (o == null) { + o = Null; + } + map.put(this, o); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/Throwable.java b/sgx-jvm/avian/classpath/java/lang/Throwable.java new file mode 100644 index 0000000000..57c42290f1 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Throwable.java @@ -0,0 +1,129 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.Serializable; + +public class Throwable implements Serializable { + private String message; + private Object trace; + private Throwable cause; + + public Throwable(String message, Throwable cause) { + this.message = message; + this.trace = trace(1); + this.cause = cause; + } + + public Throwable(String message) { + this(message, null); + } + + public Throwable(Throwable cause) { + this(null, cause); + } + + public Throwable() { + this(null, null); + } + + public Throwable getCause() { + return cause; + } + + public Throwable initCause(Throwable e) { + if (cause == null) { + cause = e; + return this; + } else { + throw new IllegalStateException(); + } + } + + public String getMessage() { + return message; + } + + public String getLocalizedMessage() { + return getMessage(); + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getName()); + if (message != null) { + sb.append(": ").append(message); + } + return sb.toString(); + } + + private static native Object trace(int skipCount); + + static native StackTraceElement[] resolveTrace(Object trace); + + private StackTraceElement[] resolveTrace() { + if (! (trace instanceof StackTraceElement[])) { + trace = resolveTrace(trace); + } + return (StackTraceElement[]) trace; + } + + public StackTraceElement[] getStackTrace() { + return resolveTrace(); + } + + public void setStackTrace(StackTraceElement[] trace) { + this.trace = trace; + } + + public void printStackTrace(PrintStream out) { + StringBuilder sb = new StringBuilder(); + printStackTrace(sb, System.getProperty("line.separator")); + out.print(sb.toString()); + out.flush(); + } + + public void printStackTrace(PrintWriter out) { + StringBuilder sb = new StringBuilder(); + printStackTrace(sb, System.getProperty("line.separator")); + out.print(sb.toString()); + out.flush(); + } + + public void printStackTrace() { + printStackTrace(System.err); + } + + private void printStackTrace(StringBuilder sb, String nl) { + sb.append(toString()).append(nl); + + StackTraceElement[] trace = resolveTrace(); + for (int i = 0; i < trace.length; ++i) { + sb.append(" at ").append(trace[i].toString()).append(nl); + } + + Throwable printCause = getCause(); + if (printCause != null) { + sb.append("caused by: "); + printCause.printStackTrace(sb, nl); + } + } + + public Throwable fillInStackTrace() { + trace = trace(0); + return this; + } + + public void addSuppressed(Throwable exception) { + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/TypeNotPresentException.java b/sgx-jvm/avian/classpath/java/lang/TypeNotPresentException.java new file mode 100644 index 0000000000..eb1f31ed73 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/TypeNotPresentException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class TypeNotPresentException extends Exception { + public TypeNotPresentException(String message) { + super(message); + } + + public TypeNotPresentException() { + this(null); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/UnsatisfiedLinkError.java b/sgx-jvm/avian/classpath/java/lang/UnsatisfiedLinkError.java new file mode 100644 index 0000000000..9c94470345 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/UnsatisfiedLinkError.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class UnsatisfiedLinkError extends LinkageError { + public UnsatisfiedLinkError(String message) { + super(message); + } + + public UnsatisfiedLinkError() { + this(null); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/UnsupportedOperationException.java b/sgx-jvm/avian/classpath/java/lang/UnsupportedOperationException.java new file mode 100644 index 0000000000..4e5edb1cd4 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/UnsupportedOperationException.java @@ -0,0 +1,29 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class UnsupportedOperationException extends RuntimeException { + public UnsupportedOperationException(String message, Throwable cause) { + super(message, cause); + } + + public UnsupportedOperationException(String message) { + this(message, null); + } + + public UnsupportedOperationException(Throwable cause) { + this(null, cause); + } + + public UnsupportedOperationException() { + this(null, null); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/VirtualMachineError.java b/sgx-jvm/avian/classpath/java/lang/VirtualMachineError.java new file mode 100644 index 0000000000..3ad721dc22 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/VirtualMachineError.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class VirtualMachineError extends Error { + public VirtualMachineError(String message) { + super(message); + } + + public VirtualMachineError() { + this(null); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/Void.java b/sgx-jvm/avian/classpath/java/lang/Void.java new file mode 100644 index 0000000000..d721269f02 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/Void.java @@ -0,0 +1,17 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public final class Void { + public static final Class TYPE = avian.Classes.forCanonicalName("V"); + + private Void() { } +} diff --git a/sgx-jvm/avian/classpath/java/lang/annotation/Annotation.java b/sgx-jvm/avian/classpath/java/lang/annotation/Annotation.java new file mode 100644 index 0000000000..238abeb47e --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/annotation/Annotation.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.annotation; + +public interface Annotation { + Class annotationType(); +} diff --git a/sgx-jvm/avian/classpath/java/lang/annotation/ElementType.java b/sgx-jvm/avian/classpath/java/lang/annotation/ElementType.java new file mode 100644 index 0000000000..36e62e08f6 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/annotation/ElementType.java @@ -0,0 +1,22 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.annotation; + +public enum ElementType { + ANNOTATION_TYPE, + CONSTRUCTOR, + FIELD, + LOCAL_VARIABLE, + METHOD, + PACKAGE, + PARAMETER, + TYPE +} diff --git a/sgx-jvm/avian/classpath/java/lang/annotation/Retention.java b/sgx-jvm/avian/classpath/java/lang/annotation/Retention.java new file mode 100644 index 0000000000..71db2d5f95 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/annotation/Retention.java @@ -0,0 +1,16 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.annotation; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Retention { + public RetentionPolicy value(); +} diff --git a/sgx-jvm/avian/classpath/java/lang/annotation/RetentionPolicy.java b/sgx-jvm/avian/classpath/java/lang/annotation/RetentionPolicy.java new file mode 100644 index 0000000000..aa8f20f02a --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/annotation/RetentionPolicy.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.annotation; + +public enum RetentionPolicy { + CLASS, RUNTIME, SOURCE +} diff --git a/sgx-jvm/avian/classpath/java/lang/annotation/Target.java b/sgx-jvm/avian/classpath/java/lang/annotation/Target.java new file mode 100644 index 0000000000..d4dfb88f84 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/annotation/Target.java @@ -0,0 +1,17 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.annotation; + +@Retention(value=RetentionPolicy.RUNTIME) +@Target(value=ElementType.ANNOTATION_TYPE) +public @interface Target { + public ElementType[] value(); +} diff --git a/sgx-jvm/avian/classpath/java/lang/invoke/CallSite.java b/sgx-jvm/avian/classpath/java/lang/invoke/CallSite.java new file mode 100644 index 0000000000..806ac6e5ee --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/invoke/CallSite.java @@ -0,0 +1,19 @@ +/* Copyright (c) 2008-2016, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.invoke; + +public class CallSite { + private final MethodHandle target; + + CallSite(MethodHandle target) { + this.target = target; + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/invoke/LambdaConversionException.java b/sgx-jvm/avian/classpath/java/lang/invoke/LambdaConversionException.java new file mode 100644 index 0000000000..bbc4aa131c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/invoke/LambdaConversionException.java @@ -0,0 +1,19 @@ +/* Copyright (c) 2008-2016, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.invoke; + +public class LambdaConversionException extends java.lang.Exception { + public LambdaConversionException() { throw new RuntimeException(); } + public LambdaConversionException(String s) { throw new RuntimeException(); } + public LambdaConversionException(String s, Throwable th) { throw new RuntimeException(); } + public LambdaConversionException(Throwable th) { throw new RuntimeException(); } + public LambdaConversionException(String s, Throwable th, boolean b, boolean b2) { throw new RuntimeException(); } +} diff --git a/sgx-jvm/avian/classpath/java/lang/invoke/LambdaMetafactory.java b/sgx-jvm/avian/classpath/java/lang/invoke/LambdaMetafactory.java new file mode 100644 index 0000000000..4eb5cf771a --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/invoke/LambdaMetafactory.java @@ -0,0 +1,410 @@ +/* Copyright (c) 2008-2016, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.invoke; + +import static avian.Stream.write1; +import static avian.Stream.write2; +import static avian.Stream.write4; +import static avian.Stream.set4; +import static avian.Assembler.*; + +import java.lang.reflect.Proxy; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.List; +import java.util.ArrayList; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import avian.Classes; +import avian.ConstantPool; +import avian.Assembler; +import avian.ConstantPool.PoolEntry; +import avian.SystemClassLoader; + +// To understand what this is all about, please read: +// +// http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html + +public class LambdaMetafactory { + private static int nextNumber = 0; + + public static final int FLAG_SERIALIZABLE = 1; + public static final int FLAG_MARKERS = 2; + public static final int FLAG_BRIDGES = 4; + + private static Class resolveReturnInterface(MethodType type) { + int index = 1; + byte[] s = type.spec; + + while (s[index] != ')') ++ index; + + if (s[++ index] != 'L') throw new AssertionError(); + + ++ index; + + int end = index + 1; + while (s[end] != ';') ++ end; + + Class c = SystemClassLoader.getClass + (Classes.loadVMClass(type.loader, s, index, end - index)); + + if (! c.isInterface()) throw new AssertionError(); + + return c; + } + + private static int indexOf(int c, byte[] array) { + int i = 0; + while (array[i] != c) ++i; + return i; + } + + private static String constructorSpec(MethodType type) { + return Classes.makeString(type.spec, 0, indexOf(')', type.spec) + 1) + "V"; + } + + private static byte[] makeFactoryCode(List pool, + String className, + String constructorSpec, + MethodType type) + throws IOException + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + write2(out, type.footprint() + 2); // max stack + write2(out, type.footprint()); // max locals + write4(out, 0); // length (we'll set the real value later) + + write1(out, new_); + write2(out, ConstantPool.addClass(pool, className) + 1); + write1(out, dup); + + for (MethodType.Parameter p: type.parameters()) { + write1(out, p.load()); + write1(out, p.position()); + } + + write1(out, invokespecial); + write2(out, ConstantPool.addMethodRef + (pool, className, "", constructorSpec) + 1); + + write1(out, areturn); + + write2(out, 0); // exception handler table length + write2(out, 0); // attribute count + + byte[] result = out.toByteArray(); + set4(result, 4, result.length - 12); + + return result; + } + + private static byte[] makeConstructorCode(List pool, + String className, + MethodType type) + throws IOException + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + write2(out, 3); // max stack + write2(out, type.footprint() + 1); // max locals + write4(out, 0); // length (we'll set the real value later) + + write1(out, aload_0); + write1(out, invokespecial); + write2(out, ConstantPool.addMethodRef + (pool, "java/lang/Object", "", "()V") + 1); + + for (MethodType.Parameter p: type.parameters()) { + write1(out, aload_0); + write1(out, p.load()); + write1(out, p.position() + 1); + write1(out, putfield); + write2(out, ConstantPool.addFieldRef + (pool, className, "field" + p.index(), p.spec()) + 1); + } + + write1(out, return_); + + write2(out, 0); // exception handler table length + write2(out, 0); // attribute count + + byte[] result = out.toByteArray(); + set4(result, 4, result.length - 12); + + return result; + } + + private static byte[] makeInvocationCode(List pool, + String className, + String constructorSpec, + MethodType fieldType, + MethodType localType, + MethodHandle implementation) + throws IOException + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + write2(out, fieldType.footprint() + + localType.footprint() + 2); // max stack + write2(out, localType.footprint() + 1); // max locals + write4(out, 0); // length (we'll set the real value later) + + write1(out, aload_0); + + for (MethodType.Parameter p: fieldType.parameters()) { + write1(out, aload_0); + write1(out, getfield); + write2(out, ConstantPool.addFieldRef + (pool, className, "field" + p.index(), p.spec()) + 1); + } + + for (MethodType.Parameter p: localType.parameters()) { + write1(out, p.load()); + write1(out, p.position() + 1); + } + + switch (implementation.kind) { + case MethodHandle.REF_invokeStatic: + write1(out, invokestatic); + break; + + case MethodHandle.REF_invokeSpecial: + write1(out, invokespecial); + break; + + default: throw new AssertionError + ("todo: implement per http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.5"); + } + + write2(out, ConstantPool.addMethodRef + (pool, + Classes.makeString(implementation.method.class_.name, 0, + implementation.method.class_.name.length - 1), + Classes.makeString(implementation.method.name, 0, + implementation.method.name.length - 1), + Classes.makeString(implementation.method.spec, 0, + implementation.method.spec.length - 1)) + 1); + + write1(out, implementation.type().result().return_()); + + write2(out, 0); // exception handler table length + write2(out, 0); // attribute count + + byte[] result = out.toByteArray(); + set4(result, 4, result.length - 12); + + return result; + } + + 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), + emptyInterfaceList); + } + + private static byte[] makeLambda(String invokedName, + MethodType invokedType, + MethodType methodType, + MethodHandle methodImplementation, + Class[] interfaces) + { + String className; + { int number; + synchronized (LambdaMetafactory.class) { + number = nextNumber++; + } + className = "Lambda-" + number; + } + + List pool = new ArrayList(); + + int[] interfaceIndexes = new int[interfaces.length + 1]; + interfaceIndexes[0] = ConstantPool.addClass(pool, invokedType.returnType().getName().replace('.', '/')); + for (int i = 0; i < interfaces.length; i++) { + String name = interfaces[i].getName().replace('.', '/'); + interfaceIndexes[i + 1] = ConstantPool.addClass(pool, name); + } + + List fieldTable = new ArrayList(); + + for (MethodType.Parameter p: invokedType.parameters()) { + fieldTable.add + (new FieldData(0, + ConstantPool.addUtf8(pool, "field" + p.index()), + ConstantPool.addUtf8(pool, p.spec()))); + } + + String constructorSpec = constructorSpec(invokedType); + + List methodTable = new ArrayList(); + + try { + methodTable.add + (new MethodData + (Modifier.PUBLIC | Modifier.STATIC, + ConstantPool.addUtf8(pool, "make"), + ConstantPool.addUtf8(pool, invokedType.toMethodDescriptorString()), + makeFactoryCode(pool, className, constructorSpec, invokedType))); + + methodTable.add + (new MethodData + (Modifier.PUBLIC, + ConstantPool.addUtf8(pool, ""), + ConstantPool.addUtf8(pool, constructorSpec), + makeConstructorCode(pool, className, invokedType))); + + methodTable.add + (new MethodData + (Modifier.PUBLIC, + ConstantPool.addUtf8(pool, invokedName), + ConstantPool.addUtf8(pool, methodType.toMethodDescriptorString()), + makeInvocationCode(pool, className, constructorSpec, invokedType, + methodType, methodImplementation))); + } catch (IOException e) { + AssertionError error = new AssertionError(); + error.initCause(e); + throw error; + } + + int nameIndex = ConstantPool.addClass(pool, className); + int superIndex = ConstantPool.addClass(pool, "java/lang/Object"); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + Assembler.writeClass + (out, pool, nameIndex, superIndex, interfaceIndexes, + fieldTable.toArray(new FieldData[fieldTable.size()]), + methodTable.toArray(new MethodData[methodTable.size()])); + } catch (IOException e) { + AssertionError error = new AssertionError(); + error.initCause(e); + throw error; + } + + return out.toByteArray(); + } + + private static CallSite makeCallSite(MethodType invokedType, byte[] classData) throws AssertionError { + try { + return new CallSite + (new MethodHandle + (MethodHandle.REF_invokeStatic, invokedType.loader, Classes.toVMMethod + (avian.SystemClassLoader.getClass + (avian.Classes.defineVMClass + (invokedType.loader, classData, 0, classData.length)) + .getMethod("make", invokedType.parameterArray())))); + } catch (NoSuchMethodException e) { + AssertionError error = new AssertionError(); + error.initCause(e); + throw error; + } + } + + private static final Class[] emptyInterfaceList = new Class[] {}; + + 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, emptyInterfaceList); + return makeCallSite(invokedType, classData); + } + + public static CallSite altMetafactory(MethodHandles.Lookup caller, + String invokedName, + MethodType invokedType, + Object... args) throws LambdaConversionException { + // See openjdk8/jdk/src/share/classes/java/lang/invoke/LambdaMetafactory.java + // Behaves as if the prototype is like this: + // + // CallSite altMetafactory( + // MethodHandles.Lookup caller, + // String invokedName, + // MethodType invokedType, + // MethodType methodType, + // MethodHandle methodImplementation, + // MethodType instantiatedMethodType, + // int flags, + // int markerInterfaceCount, // IF flags has MARKERS set + // Class... markerInterfaces, // IF flags has MARKERS set + // int bridgeCount, // IF flags has BRIDGES set + // MethodType... bridges // IF flags has BRIDGES set + // ) + MethodType methodType = (MethodType) args[0]; + MethodHandle methodImplementation = (MethodHandle) args[1]; + + int flags = (Integer) args[3]; + boolean serializable = (flags & FLAG_SERIALIZABLE) != 0; + + // Marker interfaces are added to a lambda when they're written like this: + // + // Runnable r = (Runnable & Serializable) () -> foo() + // + // The intersection type in the cast here indicates to the compiler what interfaces + // the generated lambda class should implement. Because a lambda has (by definition) + // one method only, it is meaningless for these interfaces to contain anything, thus + // they are only allowed to be empty marker interfaces. In practice the Serializable + // interface is handled specially and the use of markers is extremely rare. Adding + // support would be easy though. + if ((flags & FLAG_MARKERS) != 0) + throw new UnsupportedOperationException("Marker interfaces on lambdas are not supported on Avian yet. Sorry."); + + // In some cases there is a mismatch between what the JVM type system supports and + // what the Java language supports. In other cases the type of a lambda expression + // may not perfectly match the functional interface which represents it. Consider the + // following case: + // + // interface I { void foo(Integer i, String s1, Strings s2) } + // class Foo { static void m(Number i, Object... rest) {} } + // + // I lambda = Foo::m + // + // This is allowed by the Java language, even though the interface representing the + // lambda specifies three specific arguments and the method implementing the lambda + // uses varargs and a different type signature. Behind the scenes the compiler generates + // a "bridge" method that does the adaptation. + // + // You can learn more here: http://www.oracle.com/technetwork/java/jvmls2013heid-2013922.pdf + // and here: http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html + if ((flags & FLAG_BRIDGES) != 0) { + int bridgeCount = (Integer) args[4]; + if (bridgeCount > 0) + throw new UnsupportedOperationException("A lambda that requires bridge methods was used, this is not yet supported by Avian. Sorry."); + } + + // TODO: This is not necessary if the function type interface is already inheriting + // from Serializable. + Class[] interfaces = new Class[serializable ? 1 : 0]; + if (serializable) + interfaces[0] = java.io.Serializable.class; + + byte[] classData = makeLambda(invokedName, invokedType, methodType, methodImplementation, interfaces); + return makeCallSite(invokedType, classData); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/invoke/MethodHandle.java b/sgx-jvm/avian/classpath/java/lang/invoke/MethodHandle.java new file mode 100644 index 0000000000..bbb5cabdec --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/invoke/MethodHandle.java @@ -0,0 +1,65 @@ +/* Copyright (c) 2008-2016, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.invoke; + +import avian.Classes; +import avian.SystemClassLoader; + +public class MethodHandle { + static final int REF_invokeStatic = 6; + static final int REF_invokeSpecial = 7; + + final int kind; + private final ClassLoader loader; + final avian.VMMethod method; + private volatile MethodType type; + + MethodHandle(int kind, ClassLoader loader, avian.VMMethod method) { + this.kind = kind; + this.loader = loader; + 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) { + sb.append(Classes.makeString(method.class_.name, 0, + method.class_.name.length - 1)); + sb.append("."); + } + sb.append(Classes.makeString(method.name, 0, + method.name.length - 1)); + sb.append(Classes.makeString(method.spec, 0, + method.spec.length - 1)); + return sb.toString(); + } + + public MethodType type() { + if (type == null) { + type = new MethodType(loader, method.spec); + } + return type; + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/invoke/MethodHandles.java b/sgx-jvm/avian/classpath/java/lang/invoke/MethodHandles.java new file mode 100644 index 0000000000..b4af275a5c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/invoke/MethodHandles.java @@ -0,0 +1,28 @@ +/* Copyright (c) 2008-2016, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.invoke; + +public class MethodHandles { + public static class Lookup { + final avian.VMClass class_; + private final int modes; + + private Lookup(avian.VMClass class_, int modes) { + this.class_ = class_; + this.modes = modes; + } + + public String toString() { + return "lookup[" + avian.SystemClassLoader.getClass(class_) + ", " + + modes + "]"; + } + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/invoke/MethodType.java b/sgx-jvm/avian/classpath/java/lang/invoke/MethodType.java new file mode 100644 index 0000000000..4ac62810f5 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/invoke/MethodType.java @@ -0,0 +1,322 @@ +/* Copyright (c) 2008-2016, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.invoke; + +import static avian.Assembler.*; + +import avian.Assembler; +import avian.Classes; +import avian.SystemClassLoader; +import avian.VMClass; + +import java.util.List; +import java.util.ArrayList; + +public final class MethodType implements java.io.Serializable { + private static final char[] Primitives = new char[] { + 'V', 'Z', 'B', 'C', 'S', 'I', 'F', 'J', 'D' + }; + + final ClassLoader loader; + final byte[] spec; + private volatile List parameters; + private volatile Result result; + private volatile int footprint; + + MethodType(ClassLoader loader, byte[] spec) { + this.loader = loader; + 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); + } + + private static String spec(Class c) { + if (c.isPrimitive()) { + VMClass vmc = Classes.toVMClass(c); + for (char p: Primitives) { + if (vmc == Classes.primitiveClass(p)) { + return String.valueOf(p); + } + } + throw new AssertionError(); + } else if (c.isArray()) { + return "[" + spec(c.getComponentType()); + } else { + return "L" + c.getName().replace('.', '/') + ";"; + } + } + + private MethodType(Class rtype, + Class ... ptypes) + { + loader = rtype.getClassLoader(); + + StringBuilder sb = new StringBuilder(); + sb.append('('); + parameters = new ArrayList(ptypes.length); + int position = 0; + for (int i = 0; i < ptypes.length; ++i) { + String spec = spec(ptypes[i]); + sb.append(spec); + + Type type = type(spec); + + parameters.add(new Parameter(i, + position, + spec, + ptypes[i], + type.load)); + + position += type.size; + } + sb.append(')'); + + footprint = position; + + String spec = spec(rtype); + sb.append(spec); + + result = new Result(spec, rtype, type(spec).return_); + + this.spec = sb.toString().getBytes(); + } + + public static MethodType methodType(Class rtype, + Class ptype0, + Class ... ptypes) + { + Class[] array = new Class[ptypes.length + 1]; + array[0] = ptype0; + System.arraycopy(ptypes, 0, array, 1, ptypes.length); + return methodType(rtype, array); + } + + public static MethodType methodType(Class rtype, + Class ... ptypes) + { + return new MethodType(rtype, ptypes); + } + + public String toString() { + return Classes.makeString(spec, 0, spec.length - 1); + } + + public int footprint() { + parameters(); // ensure spec is parsed + + return footprint; + } + + public Class returnType() { + parameters(); // ensure spec is parsed + + return result.type; + } + + public Class[] parameterArray() { + parameters(); // ensure spec is parsed + + Class[] array = new Class[parameters.size()]; + for (int i = 0; i < parameters.size(); ++i) { + array[i] = parameters.get(i).type; + } + + return array; + } + + public Iterable parameters() { + if (parameters == null) { + List list = new ArrayList(); + int i; + int index = 0; + int position = 0; + for (i = 1; spec[i] != ')'; ++i) { + int start = i; + switch (spec[i]) { + case 'L': { + ++ i; + while (spec[i] != ';') ++ i; + } break; + + case '[': { + ++ i; + while (spec[i] == '[') ++ i; + + switch (spec[i]) { + case 'L': + ++ i; + while (spec[i] != ';') ++ i; + break; + + default: + break; + } + } break; + + case 'Z': + case 'B': + case 'S': + case 'C': + case 'I': + case 'F': + case 'J': + case 'D': + break; + + default: throw new AssertionError(); + } + + String paramSpec = Classes.makeString(spec, start, (i - start) + 1); + Type type = type(paramSpec); + + list.add(new Parameter + (index, + position, + paramSpec, + Classes.forCanonicalName(loader, paramSpec), + type.load)); + + ++ index; + position += type.size; + } + + footprint = position; + + ++ i; + + String paramSpec = Classes.makeString(spec, i, spec.length - i - 1); + Type type = type(paramSpec); + + result = new Result(paramSpec, + Classes.forCanonicalName(loader, paramSpec), + type.return_); + + parameters = list; + } + + return parameters; + } + + public Result result() { + parameters(); // ensure spec has been parsed + + return result; + } + + private static Type type(String spec) { + switch (spec.charAt(0)) { + case 'L': + case '[': + return Type.ObjectType; + + case 'Z': + case 'B': + case 'S': + case 'C': + case 'I': + return Type.IntegerType; + + case 'F': + return Type.FloatType; + + case 'J': + return Type.LongType; + + case 'D': + return Type.DoubleType; + + case 'V': + return Type.VoidType; + + default: throw new AssertionError(); + } + } + + private static enum Type { + ObjectType(aload, areturn, 1), + IntegerType(iload, ireturn, 1), + FloatType(fload, freturn, 1), + LongType(lload, lreturn, 2), + DoubleType(dload, dreturn, 2), + VoidType(-1, Assembler.return_, -1); + + public final int load; + public final int return_; + public final int size; + + private Type(int load, int return_, int size) { + this.load = load; + this.return_ = return_; + this.size = size; + } + } + + public static class Parameter { + private final int index; + private final int position; + private final String spec; + private final Class type; + private final int load; + + private Parameter(int index, + int position, + String spec, + Class type, + int load) + { + this.index = index; + this.position = position; + this.spec = spec; + this.type = type; + this.load = load; + } + + public int index() { + return index; + } + + public int position() { + return position; + } + + public String spec() { + return spec; + } + + public int load() { + return load; + } + } + + public static class Result { + private final String spec; + private final Class type; + private final int return_; + + public Result(String spec, Class type, int return_) { + this.spec = spec; + this.type = type; + this.return_ = return_; + } + + public int return_() { + return return_; // :) + } + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/invoke/SerializedLambda.java b/sgx-jvm/avian/classpath/java/lang/invoke/SerializedLambda.java new file mode 100644 index 0000000000..749a11f049 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/invoke/SerializedLambda.java @@ -0,0 +1,53 @@ +/* Copyright (c) 2008-2016, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.invoke; + +public class SerializedLambda implements java.io.Serializable { + public Object getCapturedArg(int i) { + // todo + return null; + } + + public int getImplMethodKind() { + // todo + return 0; + } + + public String getImplClass() { + // todo + return null; + } + + public String getImplMethodName() { + // todo + return null; + } + + public String getImplMethodSignature() { + // todo + return null; + } + + public String getFunctionalInterfaceClass() { + // todo + return null; + } + + public String getFunctionalInterfaceMethodName() { + // todo + return null; + } + + public String getFunctionalInterfaceMethodSignature() { + // todo + return null; + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/ref/PhantomReference.java b/sgx-jvm/avian/classpath/java/lang/ref/PhantomReference.java new file mode 100644 index 0000000000..3843154721 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/ref/PhantomReference.java @@ -0,0 +1,25 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.ref; + +public class PhantomReference extends Reference { + public PhantomReference(T target, ReferenceQueue queue) { + super(target, queue); + } + + public PhantomReference(T target) { + this(target, null); + } + + public T get() { + return null; + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/ref/Reference.java b/sgx-jvm/avian/classpath/java/lang/ref/Reference.java new file mode 100644 index 0000000000..443e0c820c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/ref/Reference.java @@ -0,0 +1,50 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.ref; + +public abstract class Reference { + private Object vmNext; + private T target; + private ReferenceQueue queue; + + Reference jNext; + + protected Reference(T target, ReferenceQueue queue) { + this.target = target; + this.queue = queue; + } + + protected Reference(T target) { + this(target, null); + } + + public T get() { + return target; + } + + public void clear() { + target = null; + } + + public boolean isEnqueued() { + return jNext != null; + } + + public boolean enqueue() { + if (queue != null) { + queue.add(this); + queue = null; + return true; + } else { + return false; + } + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/ref/ReferenceQueue.java b/sgx-jvm/avian/classpath/java/lang/ref/ReferenceQueue.java new file mode 100644 index 0000000000..c5ccd6b320 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/ref/ReferenceQueue.java @@ -0,0 +1,36 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.ref; + +public class ReferenceQueue { + private Reference front; + + public Reference poll() { + Reference r = front; + if (front != null) { + if (front == front.jNext) { + front = null; + } else { + front = front.jNext; + } + } + return r; + } + + void add(Reference r) { + if (front == null) { + r.jNext = r; + } else { + r.jNext = front; + } + front = r; + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/ref/SoftReference.java b/sgx-jvm/avian/classpath/java/lang/ref/SoftReference.java new file mode 100644 index 0000000000..412cf0204c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/ref/SoftReference.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.ref; + +public class SoftReference extends Reference { + public SoftReference(T target, ReferenceQueue queue) { + super(target, queue); + } + + public SoftReference(T target) { + this(target, null); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/ref/WeakReference.java b/sgx-jvm/avian/classpath/java/lang/ref/WeakReference.java new file mode 100644 index 0000000000..4b6835c643 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/ref/WeakReference.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.ref; + +public class WeakReference extends Reference { + public WeakReference(T target, ReferenceQueue queue) { + super(target, queue); + } + + public WeakReference(T target) { + this(target, null); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/reflect/AccessibleObject.java b/sgx-jvm/avian/classpath/java/lang/reflect/AccessibleObject.java new file mode 100644 index 0000000000..9c8c0b57cd --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/reflect/AccessibleObject.java @@ -0,0 +1,35 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.reflect; + +import java.lang.annotation.Annotation; + +public abstract class AccessibleObject implements AnnotatedElement { + protected static final int Accessible = 1 << 0; + + // Access and property flags for Member descendants + protected static final int ACC_VARARGS = 0x0080; + protected static final int ACC_SYNTHETIC = 0x1000; + + public boolean isAnnotationPresent + (Class class_) + { + return getAnnotation(class_) != null; + } + + public abstract boolean isAccessible(); + + public abstract void setAccessible(boolean v); + + public static void setAccessible(AccessibleObject[] array, boolean v) { + for (AccessibleObject o: array) o.setAccessible(v); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/reflect/AnnotatedElement.java b/sgx-jvm/avian/classpath/java/lang/reflect/AnnotatedElement.java new file mode 100644 index 0000000000..d47b0c9fa3 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/reflect/AnnotatedElement.java @@ -0,0 +1,23 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.reflect; + +import java.lang.annotation.Annotation; + +public interface AnnotatedElement { + public boolean isAnnotationPresent(Class class_); + + public T getAnnotation(Class class_); + + public Annotation[] getAnnotations(); + + public Annotation[] getDeclaredAnnotations(); +} diff --git a/sgx-jvm/avian/classpath/java/lang/reflect/Array.java b/sgx-jvm/avian/classpath/java/lang/reflect/Array.java new file mode 100644 index 0000000000..09846019ba --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/reflect/Array.java @@ -0,0 +1,130 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.reflect; + +public final class Array { + private Array() { } + + public static Object get(Object array, int index) { + String className = array.getClass().getName(); + if (! className.startsWith("[")) { + throw new IllegalArgumentException(); + } + + switch (className.charAt(1)) { + case 'B': + return Byte.valueOf(((byte[]) array)[index]); + case 'C': + return Character.valueOf(((char[]) array)[index]); + case 'D': + return Double.valueOf(((double[]) array)[index]); + case 'F': + return Float.valueOf(((float[]) array)[index]); + case 'I': + return Integer.valueOf(((int[]) array)[index]); + case 'J': + return Long.valueOf(((long[]) array)[index]); + case 'S': + return Short.valueOf(((short[]) array)[index]); + case 'Z': + return Boolean.valueOf(((boolean[]) array)[index]); + case 'L': + case '[': + return ((Object[]) array)[index]; + + default: + throw new Error(); + } + } + + public static void set(Object array, int index, Object value) { + String className = array.getClass().getName(); + if (! className.startsWith("[")) { + throw new IllegalArgumentException(); + } + + switch (className.charAt(1)) { + case 'B': + ((byte[]) array)[index] = (Byte) value; + break; + case 'C': + ((char[]) array)[index] = (Character) value; + break; + case 'D': + ((double[]) array)[index] = (Double) value; + break; + case 'F': + ((float[]) array)[index] = (Float) value; + break; + case 'I': + ((int[]) array)[index] = (Integer) value; + break; + case 'J': + ((long[]) array)[index] = (Long) value; + break; + case 'S': + ((short[]) array)[index] = (Short) value; + break; + case 'Z': + ((boolean[]) array)[index] = (Boolean) value; + break; + case 'L': + case '[': + if (value == null + || array.getClass().getComponentType().isInstance(value)) + { + ((Object[]) array)[index] = value; + } else { + throw new IllegalArgumentException + ("need " + array.getClass().getComponentType() + + ", got " + value.getClass().getName()); + } + break; + + default: + throw new Error(); + } + } + + public static native int getLength(Object array); + + private static native Object makeObjectArray(Class elementType, int length); + + public static Object newInstance(Class elementType, int length) { + if (length < 0) { + throw new NegativeArraySizeException(); + } + + if (elementType.isPrimitive()) { + if (elementType.equals(boolean.class)) { + return new boolean[length]; + } else if (elementType.equals(byte.class)) { + return new byte[length]; + } else if (elementType.equals(char.class)) { + return new char[length]; + } else if (elementType.equals(short.class)) { + return new short[length]; + } else if (elementType.equals(int.class)) { + return new int[length]; + } else if (elementType.equals(long.class)) { + return new long[length]; + } else if (elementType.equals(float.class)) { + return new float[length]; + } else if (elementType.equals(double.class)) { + return new double[length]; + } else { + throw new IllegalArgumentException(); + } + } else { + return makeObjectArray(elementType, length); + } + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/reflect/Constructor.java b/sgx-jvm/avian/classpath/java/lang/reflect/Constructor.java new file mode 100644 index 0000000000..ec7d306bbd --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/reflect/Constructor.java @@ -0,0 +1,81 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.reflect; + +import java.lang.annotation.Annotation; + +public class Constructor extends AccessibleObject implements Member { + private Method method; + + public Constructor(Method method) { + this.method = method; + } + + public boolean equals(Object o) { + return o instanceof Constructor + && ((Constructor) o).method.equals(method); + } + + public boolean isAccessible() { + return method.isAccessible(); + } + + public void setAccessible(boolean v) { + method.setAccessible(v); + } + + public Class getDeclaringClass() { + return method.getDeclaringClass(); + } + + public Class[] getParameterTypes() { + return method.getParameterTypes(); + } + + public int getModifiers() { + return method.getModifiers(); + } + + public boolean isSynthetic() { + return method.isSynthetic(); + } + + public String getName() { + return method.getName(); + } + + public T getAnnotation(Class class_) { + return method.getAnnotation(class_); + } + + public Annotation[] getAnnotations() { + return method.getAnnotations(); + } + + public Annotation[] getDeclaredAnnotations() { + return method.getDeclaredAnnotations(); + } + + private static native Object make(avian.VMClass c); + + public T newInstance(Object ... arguments) + throws InvocationTargetException, InstantiationException, + IllegalAccessException + { + T v = (T) make(method.getDeclaringClass().vmClass); + method.invoke(v, arguments); + return v; + } + + public Class[] getExceptionTypes() { + throw new UnsupportedOperationException("not yet implemented"); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/reflect/Field.java b/sgx-jvm/avian/classpath/java/lang/reflect/Field.java new file mode 100644 index 0000000000..91d3b68f5c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/reflect/Field.java @@ -0,0 +1,401 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.reflect; + +import avian.VMField; +import avian.AnnotationInvocationHandler; +import avian.SystemClassLoader; +import avian.Classes; + +import java.lang.annotation.Annotation; + +public class Field extends AccessibleObject implements Member { + private static final int VoidField = 0; + private static final int ByteField = 1; + private static final int CharField = 2; + private static final int DoubleField = 3; + private static final int FloatField = 4; + private static final int IntField = 5; + private static final int LongField = 6; + private static final int ShortField = 7; + private static final int BooleanField = 8; + private static final int ObjectField = 9; + + private final VMField vmField; + private boolean accessible = true; + + public Field(VMField vmField) { + this.vmField = vmField; + } + + public boolean isAccessible() { + return accessible; + } + + public void setAccessible(boolean v) { + accessible = v; + } + + public Class getDeclaringClass() { + return SystemClassLoader.getClass(vmField.class_); + } + + public int getModifiers() { + return vmField.flags; + } + + public boolean isSynthetic() { + return (vmField.flags & ACC_SYNTHETIC) != 0; + } + + public String getName() { + return getName(vmField); + } + + public static String getName(VMField vmField) { + return Classes.makeString(vmField.name, 0, vmField.name.length - 1); + } + + public Class getType() { + return Classes.forCanonicalName + (vmField.class_.loader, + Classes.makeString(vmField.spec, 0, vmField.spec.length - 1)); + } + + public Type getGenericType() { + if (vmField.addendum == null || vmField.addendum.signature == null) { + return getType(); + } + String signature = Classes.toString((byte[]) vmField.addendum.signature); + return SignatureParser.parse(vmField.class_.loader, signature, getDeclaringClass()); + } + + public Object get(Object instance) throws IllegalAccessException { + Object target; + if ((vmField.flags & Modifier.STATIC) != 0) { + target = vmField.class_.staticTable; + } else if (Class.isInstance(vmField.class_, instance)) { + target = instance; + } else { + throw new IllegalArgumentException(); + } + + Classes.initialize(vmField.class_); + + switch (vmField.code) { + case ByteField: + return Byte.valueOf + ((byte) getPrimitive(target, vmField.code, vmField.offset)); + + case BooleanField: + return Boolean.valueOf + (getPrimitive(target, vmField.code, vmField.offset) != 0); + + case CharField: + return Character.valueOf + ((char) getPrimitive(target, vmField.code, vmField.offset)); + + case ShortField: + return Short.valueOf + ((short) getPrimitive(target, vmField.code, vmField.offset)); + + case IntField: + return Integer.valueOf + ((int) getPrimitive(target, vmField.code, vmField.offset)); + + case LongField: + return Long.valueOf + (getPrimitive(target, vmField.code, vmField.offset)); + + case FloatField: + return Float.valueOf + (Float.intBitsToFloat + ((int) getPrimitive(target, vmField.code, vmField.offset))); + + case DoubleField: + return Double.valueOf + (Double.longBitsToDouble + (getPrimitive(target, vmField.code, vmField.offset))); + + case ObjectField: + return getObject(target, vmField.offset); + + default: + throw new Error(); + } + } + + public boolean getBoolean(Object instance) throws IllegalAccessException { + return ((Boolean) get(instance)).booleanValue(); + } + + public byte getByte(Object instance) throws IllegalAccessException { + return ((Byte) get(instance)).byteValue(); + } + + public short getShort(Object instance) throws IllegalAccessException { + return ((Short) get(instance)).shortValue(); + } + + public char getChar(Object instance) throws IllegalAccessException { + return ((Character) get(instance)).charValue(); + } + + public int getInt(Object instance) throws IllegalAccessException { + return ((Integer) get(instance)).intValue(); + } + + public float getFloat(Object instance) throws IllegalAccessException { + return ((Float) get(instance)).floatValue(); + } + + public long getLong(Object instance) throws IllegalAccessException { + return ((Long) get(instance)).longValue(); + } + + public double getDouble(Object instance) throws IllegalAccessException { + return ((Double) get(instance)).doubleValue(); + } + + private boolean matchType(Object value) { + switch (vmField.code) { + case ByteField: + return value instanceof Byte; + + case BooleanField: + return value instanceof Boolean; + + case CharField: + return value instanceof Character; + + case ShortField: + return value instanceof Short; + + case IntField: + return value instanceof Integer; + + case LongField: + return value instanceof Long; + + case FloatField: + return value instanceof Float; + + case DoubleField: + return value instanceof Double; + + case ObjectField: + return value == null || getType().isInstance(value); + + default: + throw new Error(); + } + } + + public void set(Object instance, Object value) + throws IllegalAccessException + { + Object target; + if ((vmField.flags & Modifier.STATIC) != 0) { + target = vmField.class_.staticTable; + } else if (Class.isInstance(vmField.class_, instance)) { + target = instance; + } else { + throw new IllegalArgumentException(); + } + + if (! matchType(value)) { + throw new IllegalArgumentException(); + } + + Classes.initialize(vmField.class_); + + switch (vmField.code) { + case ByteField: + setPrimitive(target, vmField.code, vmField.offset, (Byte) value); + break; + + case BooleanField: + setPrimitive + (target, vmField.code, vmField.offset, ((Boolean) value) ? 1 : 0); + break; + + case CharField: + setPrimitive(target, vmField.code, vmField.offset, (Character) value); + break; + + case ShortField: + setPrimitive(target, vmField.code, vmField.offset, (Short) value); + break; + + case IntField: + setPrimitive(target, vmField.code, vmField.offset, (Integer) value); + break; + + case LongField: + setPrimitive(target, vmField.code, vmField.offset, (Long) value); + break; + + case FloatField: + setPrimitive(target, vmField.code, vmField.offset, + Float.floatToRawIntBits((Float) value)); + break; + + case DoubleField: + setPrimitive(target, vmField.code, vmField.offset, + Double.doubleToRawLongBits((Double) value)); + break; + + case ObjectField: + setObject(target, vmField.offset, value); + break; + + default: + throw new Error(); + } + } + + private void set(Object instance, long value) + throws IllegalAccessException + { + Object target; + if ((vmField.flags & Modifier.STATIC) != 0) { + target = vmField.class_.staticTable; + } else if (Class.isInstance(vmField.class_, instance)) { + target = instance; + } else { + throw new IllegalArgumentException(); + } + + Classes.initialize(vmField.class_); + + switch (vmField.code) { + case ByteField: + case BooleanField: + case CharField: + case ShortField: + case IntField: + case LongField: + case FloatField: + case DoubleField: + setPrimitive(target, vmField.code, vmField.offset, value); + break; + + default: + throw new IllegalArgumentException + ("needed " + getType() + ", got primitive type when setting " + + Class.getName(vmField.class_) + "." + getName()); + } + } + + public void setByte(Object instance, byte value) + throws IllegalAccessException + { + set(instance, value & 0xff); + } + + public void setBoolean(Object instance, boolean value) + throws IllegalAccessException + { + set(instance, value ? 1 : 0); + } + + public void setChar(Object instance, char value) + throws IllegalAccessException + { + set(instance, value & 0xffff); + } + + public void setShort(Object instance, short value) + throws IllegalAccessException + { + set(instance, value & 0xffff); + } + + public void setInt(Object instance, int value) + throws IllegalAccessException + { + set(instance, value & 0xffffffffl); + } + + public void setLong(Object instance, long value) + throws IllegalAccessException + { + set(instance, value); + } + + public void setFloat(Object instance, float value) + throws IllegalAccessException + { + set(instance, Float.floatToIntBits(value)); + } + + public void setDouble(Object instance, double value) + throws IllegalAccessException + { + set(instance, Double.doubleToLongBits(value)); + } + + private Annotation getAnnotation(Object[] a) { + if (a[0] == null) { + a[0] = Proxy.newProxyInstance + (vmField.class_.loader, new Class[] { (Class) a[1] }, + new AnnotationInvocationHandler(a)); + } + return (Annotation) a[0]; + } + + private boolean hasAnnotations() { + return vmField.addendum != null + && vmField.addendum.annotationTable != null; + } + + public T getAnnotation(Class class_) { + if (hasAnnotations()) { + Object[] table = (Object[]) vmField.addendum.annotationTable; + for (int i = 0; i < table.length; ++i) { + Object[] a = (Object[]) table[i]; + if (a[1] == class_) { + return (T) getAnnotation(a); + } + } + } + return null; + } + + public Annotation[] getAnnotations() { + if (hasAnnotations()) { + Object[] table = (Object[]) vmField.addendum.annotationTable; + Annotation[] array = new Annotation[table.length]; + for (int i = 0; i < table.length; ++i) { + array[i] = getAnnotation((Object[]) table[i]); + } + return array; + } else { + return new Annotation[0]; + } + } + + public Annotation[] getDeclaredAnnotations() { + return getAnnotations(); + } + + private static native long getPrimitive + (Object instance, int code, int offset); + + private static native Object getObject + (Object instance, int offset); + + private static native void setPrimitive + (Object instance, int code, int offset, long value); + + private static native void setObject + (Object instance, int offset, Object value); +} diff --git a/sgx-jvm/avian/classpath/java/lang/reflect/GenericDeclaration.java b/sgx-jvm/avian/classpath/java/lang/reflect/GenericDeclaration.java new file mode 100644 index 0000000000..d167aeb549 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/reflect/GenericDeclaration.java @@ -0,0 +1,5 @@ +package java.lang.reflect; + +public interface GenericDeclaration { + TypeVariable[] getTypeParameters(); +} diff --git a/sgx-jvm/avian/classpath/java/lang/reflect/InvocationHandler.java b/sgx-jvm/avian/classpath/java/lang/reflect/InvocationHandler.java new file mode 100644 index 0000000000..02bd57f97c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/reflect/InvocationHandler.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.reflect; + +public interface InvocationHandler { + public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable; +} diff --git a/sgx-jvm/avian/classpath/java/lang/reflect/InvocationTargetException.java b/sgx-jvm/avian/classpath/java/lang/reflect/InvocationTargetException.java new file mode 100644 index 0000000000..da6d11956f --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/reflect/InvocationTargetException.java @@ -0,0 +1,31 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.reflect; + +public class InvocationTargetException extends Exception { + private Throwable target; // for compatibility with OpenJDK + + public InvocationTargetException(Throwable targetException, String message) { + super(message, targetException); + } + + public InvocationTargetException(Throwable targetException) { + this(targetException, null); + } + + public InvocationTargetException() { + this(null, null); + } + + public Throwable getTargetException() { + return getCause(); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/reflect/Member.java b/sgx-jvm/avian/classpath/java/lang/reflect/Member.java new file mode 100644 index 0000000000..4081b12b35 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/reflect/Member.java @@ -0,0 +1,24 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.reflect; + +public interface Member { + public static final int PUBLIC = 0; + public static final int DECLARED = 1; + + public Class getDeclaringClass(); + + public int getModifiers(); + + public String getName(); + + public boolean isSynthetic(); +} diff --git a/sgx-jvm/avian/classpath/java/lang/reflect/Method.java b/sgx-jvm/avian/classpath/java/lang/reflect/Method.java new file mode 100644 index 0000000000..705fc8ec16 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/reflect/Method.java @@ -0,0 +1,164 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.reflect; + +import avian.VMMethod; +import avian.AnnotationInvocationHandler; +import avian.SystemClassLoader; +import avian.Classes; + +import java.lang.annotation.Annotation; + +public class Method extends AccessibleObject implements Member { + public final VMMethod vmMethod; + private boolean accessible; + + public Method(VMMethod vmMethod) { + this.vmMethod = vmMethod; + } + + public boolean equals(Object o) { + return o instanceof Method && ((Method) o).vmMethod == vmMethod; + } + + public boolean isAccessible() { + return accessible; + } + + public void setAccessible(boolean v) { + accessible = v; + } + + public static native VMMethod getCaller(); + + public Class getDeclaringClass() { + return SystemClassLoader.getClass(vmMethod.class_); + } + + public int getModifiers() { + return vmMethod.flags; + } + + public String getName() { + return getName(vmMethod); + } + + public static String getName(VMMethod vmMethod) { + return Classes.makeString(vmMethod.name, 0, vmMethod.name.length - 1); + } + + private String getSpec() { + return getSpec(vmMethod); + } + + public static String getSpec(VMMethod vmMethod) { + return Classes.makeString(vmMethod.spec, 0, vmMethod.spec.length - 1); + } + + public Class[] getParameterTypes() { + return Classes.getParameterTypes(vmMethod); + } + + public Object invoke(Object instance, Object ... arguments) + throws InvocationTargetException, IllegalAccessException + { + if ((vmMethod.flags & Modifier.STATIC) != 0 + || Class.isInstance(vmMethod.class_, instance)) + { + if ((vmMethod.flags & Modifier.STATIC) != 0) { + instance = null; + } + + if (arguments == null) { + if (vmMethod.parameterCount > 0) { + throw new NullPointerException(); + } + arguments = new Object[0]; + } + + if (arguments.length == vmMethod.parameterCount) { + Classes.initialize(vmMethod.class_); + + return invoke(vmMethod, instance, arguments); + } else { + throw new ArrayIndexOutOfBoundsException(); + } + } else { +// System.out.println +// (getDeclaringClass() + "." + getName() + " flags: " + vmMethod.flags + " vm flags: " + vmMethod.vmFlags + " return code: " + vmMethod.returnCode); + throw new IllegalArgumentException(); + } + } + + private static native Object invoke(VMMethod method, Object instance, + Object ... arguments) + throws InvocationTargetException, IllegalAccessException; + + public Class getReturnType() { + for (int i = 0; i < vmMethod.spec.length - 1; ++i) { + if (vmMethod.spec[i] == ')') { + return Classes.forCanonicalName + (vmMethod.class_.loader, + Classes.makeString + (vmMethod.spec, i + 1, vmMethod.spec.length - i - 2)); + } + } + throw new RuntimeException(); + } + + public T getAnnotation(Class class_) { + if (vmMethod.hasAnnotations()) { + Object[] table = (Object[]) vmMethod.addendum.annotationTable; + for (int i = 0; i < table.length; ++i) { + Object[] a = (Object[]) table[i]; + if (a[1] == class_) { + return (T) Classes.getAnnotation(vmMethod.class_.loader, a); + } + } + } + return null; + } + + public Annotation[] getAnnotations() { + if (vmMethod.hasAnnotations()) { + Object[] table = (Object[]) vmMethod.addendum.annotationTable; + Annotation[] array = new Annotation[table.length]; + for (int i = 0; i < table.length; ++i) { + array[i] = Classes.getAnnotation + (vmMethod.class_.loader, (Object[]) table[i]); + } + return array; + } else { + return new Annotation[0]; + } + } + + public Annotation[] getDeclaredAnnotations() { + return getAnnotations(); + } + + public boolean isVarArgs() { + return (getModifiers() & ACC_VARARGS) != 0; + } + + public boolean isSynthetic() { + return (getModifiers() & ACC_SYNTHETIC) != 0; + } + + public Object getDefaultValue() { + ClassLoader loader = getDeclaringClass().getClassLoader(); + return Classes.getAnnotationDefaultValue(loader, vmMethod.addendum); + } + + public Class[] getExceptionTypes() { + throw new UnsupportedOperationException("not yet implemented"); + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/reflect/Modifier.java b/sgx-jvm/avian/classpath/java/lang/reflect/Modifier.java new file mode 100644 index 0000000000..1b32f6a1f6 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/reflect/Modifier.java @@ -0,0 +1,40 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.reflect; + +public final class Modifier { + public static final int PUBLIC = 1 << 0; + public static final int PRIVATE = 1 << 1; + public static final int PROTECTED = 1 << 2; + public static final int STATIC = 1 << 3; + public static final int FINAL = 1 << 4; + public static final int SUPER = 1 << 5; + public static final int SYNCHRONIZED = SUPER; + public static final int VOLATILE = 1 << 6; + public static final int TRANSIENT = 1 << 7; + public static final int NATIVE = 1 << 8; + public static final int INTERFACE = 1 << 9; + public static final int ABSTRACT = 1 << 10; + public static final int STRICT = 1 << 11; + + private Modifier() { } + + public static boolean isPublic (int v) { return (v & PUBLIC) != 0; } + public static boolean isPrivate (int v) { return (v & PRIVATE) != 0; } + public static boolean isProtected(int v) { return (v & PROTECTED) != 0; } + public static boolean isStatic (int v) { return (v & STATIC) != 0; } + public static boolean isFinal (int v) { return (v & FINAL) != 0; } + public static boolean isTransient(int v) { return (v & TRANSIENT) != 0; } + public static boolean isSuper (int v) { return (v & SUPER) != 0; } + public static boolean isNative (int v) { return (v & NATIVE) != 0; } + public static boolean isAbstract (int v) { return (v & ABSTRACT) != 0; } + public static boolean isInterface(int v) { return (v & INTERFACE) != 0; } +} diff --git a/sgx-jvm/avian/classpath/java/lang/reflect/ParameterizedType.java b/sgx-jvm/avian/classpath/java/lang/reflect/ParameterizedType.java new file mode 100644 index 0000000000..8a570f0c76 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/reflect/ParameterizedType.java @@ -0,0 +1,17 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.reflect; + +public interface ParameterizedType extends Type { + Type[] getActualTypeArguments(); + Type getOwnerType(); + Type getRawType(); +} diff --git a/sgx-jvm/avian/classpath/java/lang/reflect/Proxy.java b/sgx-jvm/avian/classpath/java/lang/reflect/Proxy.java new file mode 100644 index 0000000000..60bbedc7ec --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/reflect/Proxy.java @@ -0,0 +1,430 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.reflect; + +import static avian.Stream.write1; +import static avian.Stream.write2; +import static avian.Stream.write4; +import static avian.Stream.set4; +import static avian.Assembler.*; + +import avian.SystemClassLoader; +import avian.Classes; + +import avian.ConstantPool; +import avian.ConstantPool.PoolEntry; + +import avian.Assembler; +import avian.Assembler.FieldData; +import avian.Assembler.MethodData; + +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; +import java.util.Set; +import java.util.HashSet; +import java.io.OutputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class Proxy { + private static int nextNumber; + + protected InvocationHandler h; + protected final static Map methodRefsMap = + new HashMap(); + protected final Method[] methodRefs; + + public Proxy() { + methodRefs = methodRefsMap.get(getClass()); + } + + public static Class getProxyClass(ClassLoader loader, + Class ... interfaces) + { + for (Class c: interfaces) { + if (! c.isInterface()) { + throw new IllegalArgumentException(); + } + } + + int number; + synchronized (Proxy.class) { + number = nextNumber++; + } + + try { + return makeClass(loader, interfaces, "Proxy-" + number); + } catch (IOException e) { + AssertionError error = new AssertionError(); + error.initCause(e); + throw error; + } + } + + public static boolean isProxyClass(Class c) { + return c.getName().startsWith("Proxy-"); + } + + public static InvocationHandler getInvocationHandler(Object proxy) { + return ((Proxy) proxy).h; + } + + private static byte[] makeInvokeCode(List pool, + String className, + byte[] spec, + int parameterCount, + int parameterFootprint, + int index) + throws IOException + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + write2(out, 8); // max stack + write2(out, parameterFootprint); // max locals + write4(out, 0); // length (we'll set the real value later) + + write1(out, aload_0); + write1(out, getfield); + write2(out, ConstantPool.addFieldRef + (pool, "java/lang/reflect/Proxy", + "h", "Ljava/lang/reflect/InvocationHandler;") + 1); + + write1(out, aload_0); + + write1(out, aload_0); + write1(out, getfield); + write2(out, ConstantPool.addFieldRef + (pool, className, + "methodRefs", "[Ljava/lang/reflect/Method;") + 1); + write1(out, ldc_w); + write2(out, ConstantPool.addInteger(pool, index) + 1); + write1(out, aaload); + + write1(out, ldc_w); + write2(out, ConstantPool.addInteger(pool, parameterCount) + 1); + write1(out, anewarray); + write2(out, ConstantPool.addClass(pool, "java/lang/Object") + 1); + + int ai = 0; + int si; + for (si = 1; spec[si] != ')'; ++si) { + write1(out, dup); + + write1(out, ldc_w); + write2(out, ConstantPool.addInteger(pool, ai) + 1); + + switch (spec[si]) { + case 'L': + ++ si; + while (spec[si] != ';') ++si; + + write1(out, aload); + write1(out, ai + 1); + break; + + case '[': + ++ si; + while (spec[si] == '[') ++si; + switch (spec[si]) { + case 'L': + ++ si; + while (spec[si] != ';') ++si; + break; + + default: + break; + } + + write1(out, aload); + write1(out, ai + 1); + break; + + case 'Z': + write1(out, iload); + write1(out, ai + 1); + + write1(out, invokestatic); + write2(out, ConstantPool.addMethodRef + (pool, "java/lang/Boolean", + "valueOf", "(Z)Ljava/lang/Boolean;") + 1); + break; + + case 'B': + write1(out, iload); + write1(out, ai + 1); + + write1(out, invokestatic); + write2(out, ConstantPool.addMethodRef + (pool, "java/lang/Byte", + "valueOf", "(B)Ljava/lang/Byte;") + 1); + break; + + case 'S': + write1(out, iload); + write1(out, ai + 1); + + write1(out, invokestatic); + write2(out, ConstantPool.addMethodRef + (pool, "java/lang/Short", + "valueOf", "(S)Ljava/lang/Short;") + 1); + break; + + case 'C': + write1(out, iload); + write1(out, ai + 1); + + write1(out, invokestatic); + write2(out, ConstantPool.addMethodRef + (pool, "java/lang/Character", + "valueOf", "(C)Ljava/lang/Character;") + 1); + break; + + case 'I': + write1(out, iload); + write1(out, ai + 1); + + write1(out, invokestatic); + write2(out, ConstantPool.addMethodRef + (pool, "java/lang/Integer", + "valueOf", "(I)Ljava/lang/Integer;") + 1); + break; + + case 'F': + write1(out, fload); + write1(out, ai + 1); + + write1(out, invokestatic); + write2(out, ConstantPool.addMethodRef + (pool, "java/lang/Float", + "valueOf", "(F)Ljava/lang/Float;") + 1); + break; + + case 'J': + write1(out, lload); + write1(out, ai + 1); + + write1(out, invokestatic); + write2(out, ConstantPool.addMethodRef + (pool, "java/lang/Long", + "valueOf", "(J)Ljava/lang/Long;") + 1); + ++ ai; + break; + + case 'D': + write1(out, dload); + write1(out, ai + 1); + + write1(out, invokestatic); + write2(out, ConstantPool.addMethodRef + (pool, "java/lang/Double", + "valueOf", "(D)Ljava/lang/Double;") + 1); + ++ ai; + break; + + default: throw new IllegalArgumentException(); + } + + write1(out, aastore); + + ++ ai; + } + + write1(out, invokeinterface); + write2(out, ConstantPool.addMethodRef + (pool, "java/lang/reflect/InvocationHandler", + "invoke", + "(Ljava/lang/Object;" + + "Ljava/lang/reflect/Method;" + + "[Ljava/lang/Object;)" + + "Ljava/lang/Object;") + 1); + write2(out, 0); // this will be ignored by the VM + + switch (spec[si + 1]) { + case 'L': + case '[': + write1(out, areturn); + break; + + case 'Z': + write1(out, invokevirtual); + write2(out, ConstantPool.addMethodRef + (pool, "java/lang/Boolean", "booleanValue", "()Z") + 1); + write1(out, ireturn); + break; + + case 'B': + write1(out, invokevirtual); + write2(out, ConstantPool.addMethodRef + (pool, "java/lang/Byte", "byteValue", "()B") + 1); + write1(out, ireturn); + break; + + case 'C': + write1(out, invokevirtual); + write2(out, ConstantPool.addMethodRef + (pool, "java/lang/Character", "charValue", "()C") + 1); + write1(out, ireturn); + break; + + case 'S': + write1(out, invokevirtual); + write2(out, ConstantPool.addMethodRef + (pool, "java/lang/Short", "shortValue", "()S") + 1); + write1(out, ireturn); + break; + + case 'I': + write1(out, invokevirtual); + write2(out, ConstantPool.addMethodRef + (pool, "java/lang/Integer", "intValue", "()I") + 1); + write1(out, ireturn); + break; + + case 'F': + write1(out, invokevirtual); + write2(out, ConstantPool.addMethodRef + (pool, "java/lang/Float", "floatValue", "()F") + 1); + write1(out, freturn); + break; + + case 'J': + write1(out, invokevirtual); + write2(out, ConstantPool.addMethodRef + (pool, "java/lang/Long", "longValue", "()J") + 1); + write1(out, lreturn); + break; + + case 'D': + write1(out, invokevirtual); + write2(out, ConstantPool.addMethodRef + (pool, "java/lang/Double", "doubleValue", "()D") + 1); + write1(out, dreturn); + break; + + case 'V': + write1(out, pop); + write1(out, return_); + break; + + default: throw new IllegalArgumentException(); + } + + write2(out, 0); // exception handler table length + write2(out, 0); // attribute count + + byte[] result = out.toByteArray(); + set4(result, 4, result.length - 12); + + return result; + } + + private static byte[] makeConstructorCode(List pool) + throws IOException + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + write2(out, 2); // max stack + write2(out, 2); // max locals + write4(out, 10); // length + + write1(out, aload_0); + write1(out, invokespecial); + write2(out, ConstantPool.addMethodRef + (pool, "java/lang/reflect/Proxy", + "", "()V") + 1); + + write1(out, aload_0); + write1(out, aload_1); + write1(out, putfield); + write2(out, ConstantPool.addFieldRef + (pool, "java/lang/reflect/Proxy", + "h", "Ljava/lang/reflect/InvocationHandler;") + 1); + write1(out, return_); + + write2(out, 0); // exception handler table length + write2(out, 0); // attribute count + + return out.toByteArray(); + } + + private static Class makeClass(ClassLoader loader, + Class[] interfaces, + String name) + throws IOException + { + List pool = new ArrayList(); + + int[] interfaceIndexes = new int[interfaces.length]; + for (int i = 0; i < interfaces.length; ++i) { + interfaceIndexes[i] = ConstantPool.addClass + (pool, interfaces[i].getName().replace('.', '/')); + } + + Set specs = new HashSet(); + List methodTable = new ArrayList(); + List refs = new ArrayList(); + for (Class c: interfaces) { + avian.VMMethod[] ivtable = SystemClassLoader.vmClass(c).virtualTable; + if (ivtable != null) { + for (avian.VMMethod m: ivtable) { + String spec = Classes.toString(m.name) + Classes.toString(m.spec); + if (specs.contains(spec)) { + continue; + } + methodTable.add(new MethodData + (Modifier.PUBLIC, + ConstantPool.addUtf8(pool, Classes.toString(m.name)), + ConstantPool.addUtf8(pool, Classes.toString(m.spec)), + makeInvokeCode(pool, name, m.spec, m.parameterCount, + m.parameterFootprint, methodTable.size()))); + refs.add(Classes.makeMethod(m)); + } + } + } + + methodTable.add(new MethodData + (Modifier.PUBLIC, + ConstantPool.addUtf8(pool, ""), + ConstantPool.addUtf8 + (pool, "(Ljava/lang/reflect/InvocationHandler;)V"), + makeConstructorCode(pool))); + + int nameIndex = ConstantPool.addClass(pool, name); + int superIndex = ConstantPool.addClass(pool, "java/lang/reflect/Proxy"); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Assembler.writeClass + (out, pool, nameIndex, superIndex, interfaceIndexes, + new FieldData[0], + methodTable.toArray(new MethodData[methodTable.size()])); + + byte[] classData = out.toByteArray(); + Class result = avian.SystemClassLoader.getClass + (avian.Classes.defineVMClass(loader, classData, 0, classData.length)); + methodRefsMap.put(result, refs.toArray(new Method[refs.size()])); + return result; + } + + public static Object newProxyInstance(ClassLoader loader, + Class[] interfaces, + InvocationHandler handler) + { + try { + return Proxy.getProxyClass(loader, interfaces) + .getConstructor(new Class[] { InvocationHandler.class }) + .newInstance(new Object[] { handler }); + } catch (Exception e) { + AssertionError error = new AssertionError(); + error.initCause(e); + throw error; + } + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/reflect/SignatureParser.java b/sgx-jvm/avian/classpath/java/lang/reflect/SignatureParser.java new file mode 100644 index 0000000000..b32f4fdc24 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/reflect/SignatureParser.java @@ -0,0 +1,258 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.reflect; + +import avian.Classes; + +import java.util.ArrayList; +import java.util.List; +import java.util.LinkedList; +import java.util.Map; +import java.util.HashMap; + +public class SignatureParser { + private final ClassLoader loader; + private final char[] array; + private final String signature; + private int offset; + private final Type type; + private final Map typeVariables; + + public static Type parse(ClassLoader loader, String signature, Class declaringClass) { + return new SignatureParser(loader, signature, collectTypeVariables(declaringClass)).type; + } + + private static Type parse(ClassLoader loader, String signature, Map typeVariables) { + return new SignatureParser(loader, signature, typeVariables).type; + } + + private SignatureParser(ClassLoader loader, String signature, Map typeVariables) { + this.loader = loader; + this.signature = signature; + array = signature.toCharArray(); + this.typeVariables = typeVariables; + type = parseType(); + if (offset != array.length) { + throw new IllegalArgumentException("Extra characters after " + offset + + ": " + signature); + } + } + + private Type parseType() { + char c = array[offset++]; + if (c == 'B') { + return Byte.TYPE; + } else if (c == 'C') { + return Character.TYPE; + } else if (c == 'D') { + return Double.TYPE; + } else if (c == 'F') { + return Float.TYPE; + } else if (c == 'I') { + return Integer.TYPE; + } else if (c == 'J') { + return Long.TYPE; + } else if (c == 'S') { + return Short.TYPE; + } else if (c == 'Z') { + return Boolean.TYPE; + } else if (c == 'T') { + int end = signature.indexOf(';', offset); + if (end < 0) { + throw new RuntimeException("No semicolon found while parsing signature"); + } + Type res = typeVariables.get(new String(array, offset, end - offset)); + offset = end + 1; + return res; + } else if (c != 'L') { + throw new IllegalArgumentException("Unexpected character: " + c + ", signature: " + new String(array, 0, array.length) + ", i = " + offset); + } + StringBuilder builder = new StringBuilder(); + Type ownerType = null; + for (;;) { + for (;;) { + c = array[offset++]; + if (c == ';' || c == '<') { + break; + } + builder.append(c == '/' ? '.' : c); + } + String rawTypeName = builder.toString(); + Class rawType; + try { + rawType = loader.loadClass(rawTypeName); + } catch (ClassNotFoundException e) { + throw new RuntimeException("Could not find class " + rawTypeName); + } + + int lastDollar = rawTypeName.lastIndexOf('$'); + if (lastDollar != -1 && ownerType == null) { + String ownerName = rawTypeName.substring(0, lastDollar); + try { + ownerType = loader.loadClass(ownerName); + } catch (ClassNotFoundException e) { + throw new RuntimeException("Could not find class " + ownerName); + } + } + + if (c == ';') { + return rawType; + } + List args = new ArrayList(); + while (array[offset] != '>') { + args.add(parseType()); + } + ++offset; + c = array[offset++]; + ParameterizedType type = makeType(args.toArray(new Type[args.size()]), ownerType, rawType); + if (c == ';') { + return type; + } + if (c != '.') { + throw new RuntimeException("TODO"); + } + ownerType = type; + builder.append("$"); + } + } + + private static String typeName(Type type) { + if (type instanceof Class) { + Class clazz = (Class) type; + return clazz.getName(); + } + return type.toString(); + } + + private static ParameterizedType makeType(final Type[] args, final Type owner, final Type raw) { + return new ParameterizedType() { + @Override + public Type getRawType() { + return raw; + } + + @Override + public Type getOwnerType() { + return owner; + } + + @Override + public Type[] getActualTypeArguments() { + return args; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(typeName(raw)); + builder.append('<'); + String sep = ""; + for (Type t : args) { + builder.append(sep).append(typeName(t)); + sep = ", "; + } + builder.append('>'); + return builder.toString(); + } + }; + } + + private static Map collectTypeVariables(Class clz) { + Map varsMap = new HashMap(); + LinkedList classList = new LinkedList(); + for (Class c = clz; c != null; c = c.getDeclaringClass()) { + classList.addFirst(c); + } + + for (Class cur : classList) { + final LinkedList varsList = new LinkedList(); + if (cur.vmClass.addendum != null && cur.vmClass.addendum.signature != null) { + String signature = Classes.toString((byte[]) cur.vmClass.addendum.signature); + final char[] signChars = signature.toCharArray(); + try { + int i = 0; + if (signChars[i] == '<') { + i++; + do { + final int colon = signature.indexOf(':', i); + if (colon < 0 || colon + 1 == signChars.length) { + throw new RuntimeException("Can't find ':' in the signature " + signature + " starting from " + i); + } + String typeVarName = new String(signChars, i, colon - i); + i = colon + 1; + + int start = i; + int angles = 0; + while (angles > 0 || signChars[i] != ';') { + if (signChars[i] == '<') angles ++; + else if (signChars[i] == '>') angles --; + i++; + } + String typeName = new String(signChars, start, i - start + 1); + final Type baseType = SignatureParser.parse(cur.vmClass.loader, typeName, varsMap); + + TypeVariableImpl tv = new TypeVariableImpl(typeVarName, baseType); + varsList.add(tv); + + i++; + } while (signChars[i] != '>'); + + } + } catch (IndexOutOfBoundsException e) { + throw new RuntimeException("Signature of " + cur + " is broken (" + signature + ") and can't be parsed", e); + } + } + for (TypeVariableImpl tv : varsList) { + tv.setVars(varsList); + varsMap.put(tv.getName(), tv); + } + cur = cur.getDeclaringClass(); + }; + return varsMap; + } + + private static class TypeVariableImpl implements TypeVariable { + private String name; + private Type baseType; + private TypeVariableImpl[] vars; + + public Type[] getBounds() { + return new Type[] { baseType }; + } + + public GenericDeclaration getGenericDeclaration() { + return new GenericDeclaration() { + public TypeVariable[] getTypeParameters() { + return vars; + } + }; + } + + public String getName() { + return name; + } + + TypeVariableImpl(String name, Type baseType) { + this.name = name; + this.baseType = baseType; + } + + void setVars(List vars) { + this.vars = new TypeVariableImpl[vars.size()]; + vars.toArray(this.vars); + } + + @Override + public String toString() { + return name; + } + } +} diff --git a/sgx-jvm/avian/classpath/java/lang/reflect/Type.java b/sgx-jvm/avian/classpath/java/lang/reflect/Type.java new file mode 100644 index 0000000000..c4deabf16e --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/reflect/Type.java @@ -0,0 +1,13 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.reflect; + +public interface Type { } diff --git a/sgx-jvm/avian/classpath/java/lang/reflect/TypeVariable.java b/sgx-jvm/avian/classpath/java/lang/reflect/TypeVariable.java new file mode 100644 index 0000000000..5d01787ada --- /dev/null +++ b/sgx-jvm/avian/classpath/java/lang/reflect/TypeVariable.java @@ -0,0 +1,7 @@ +package java.lang.reflect; + +public interface TypeVariable extends Type { + Type[] getBounds(); + D getGenericDeclaration(); + String getName(); +} diff --git a/sgx-jvm/avian/classpath/java/math/BigInteger.java b/sgx-jvm/avian/classpath/java/math/BigInteger.java new file mode 100644 index 0000000000..54df95b9e8 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/math/BigInteger.java @@ -0,0 +1,44 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.math; + +import java.io.Serializable; + +public class BigInteger implements Serializable { + + private int sign; + private int[] value; + + private BigInteger(int sign, long value) { + this.sign = sign; + int upperBits = (int) (value >>> 32); + if (upperBits == 0) + // Array with one element + this.value = new int[] { (int) value }; + else + // Array with two elements + this.value = new int[] { (int) value, upperBits }; + } + + public static final BigInteger ZERO = new BigInteger(0, 0); + public static final BigInteger ONE = new BigInteger(1, 1); + public static final BigInteger TEN = new BigInteger(1, 10); + + public static BigInteger valueOf(long num) { + int signum = Long.signum(num); + if (signum == 0) + return BigInteger.ZERO; + else if (signum > 0) + return new BigInteger(signum, num); + else + return new BigInteger(signum, -num); + } +} diff --git a/sgx-jvm/avian/classpath/java/math/MathContext.java b/sgx-jvm/avian/classpath/java/math/MathContext.java new file mode 100644 index 0000000000..8a8fe79607 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/math/MathContext.java @@ -0,0 +1,71 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.math; + +import java.io.Serializable; + +public final class MathContext implements Serializable { + + public static final MathContext DECIMAL32 = new MathContext( 7, RoundingMode.HALF_EVEN); + public static final MathContext DECIMAL64 = new MathContext(16, RoundingMode.HALF_EVEN); + public static final MathContext DECIMAL128 = new MathContext(34, RoundingMode.HALF_EVEN); + public static final MathContext UNLIMITED = new MathContext(0, RoundingMode.HALF_UP); + + private int precision; + private RoundingMode roundingMode; + + public MathContext(int precision, RoundingMode roundingMode) { + if (precision < 0) + throw new IllegalArgumentException(); + if (roundingMode == null) + throw new NullPointerException(); + this.precision = precision; + this.roundingMode = roundingMode; + } + + public MathContext(int precision) { + this(precision, RoundingMode.HALF_UP); + } + + public int getPrecision() { + return precision; + } + + public RoundingMode getRoundingMode() { + return roundingMode; + } + + @Override + public boolean equals(Object that) { + return + (that instanceof MathContext) && + (precision == ((MathContext) that).getPrecision()) && + (roundingMode == ((MathContext) that).getRoundingMode()); + } + + @Override + public int hashCode() { + return + roundingMode.ordinal() | + (precision << 4); + } + + private final static String precisionString = "precision="; + private final static String roundingModeString = " roundingMode="; + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(48); + sb.append(precisionString).append(precision); + sb.append(roundingModeString).append(roundingMode); + return sb.toString(); + } +} diff --git a/sgx-jvm/avian/classpath/java/math/RoundingMode.java b/sgx-jvm/avian/classpath/java/math/RoundingMode.java new file mode 100644 index 0000000000..4ea32d8e30 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/math/RoundingMode.java @@ -0,0 +1,36 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.math; + +public enum RoundingMode { + + UP (0), + DOWN (1), + CEILING (2), + FLOOR (3), + HALF_UP (4), + HALF_DOWN (5), + HALF_EVEN (6), + UNNECESSARY(7); + + RoundingMode(int rm) { + roundingMode = rm; + } + + private final int roundingMode; + + public static RoundingMode valueOf(int roundingMode) { + final RoundingMode[] values = values(); + if (roundingMode < 0 || roundingMode >= values.length) + throw new IllegalArgumentException(); + return values[roundingMode]; + } +} diff --git a/sgx-jvm/avian/classpath/java/net/BindException.java b/sgx-jvm/avian/classpath/java/net/BindException.java new file mode 100644 index 0000000000..18c6e08473 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/net/BindException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.net; + +public class BindException extends SocketException { + public BindException(String message) { + super(message); + } + + public BindException() { + this(null); + } +} diff --git a/sgx-jvm/avian/classpath/java/net/DatagramSocket.java b/sgx-jvm/avian/classpath/java/net/DatagramSocket.java new file mode 100644 index 0000000000..911550a8aa --- /dev/null +++ b/sgx-jvm/avian/classpath/java/net/DatagramSocket.java @@ -0,0 +1,19 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.net; + +import java.io.IOException; + +public abstract class DatagramSocket { + public abstract SocketAddress getRemoteSocketAddress(); + + public abstract void bind(SocketAddress address) throws SocketException; +} diff --git a/sgx-jvm/avian/classpath/java/net/InetAddress.java b/sgx-jvm/avian/classpath/java/net/InetAddress.java new file mode 100644 index 0000000000..8ddecac67c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/net/InetAddress.java @@ -0,0 +1,78 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.net; + +import java.io.IOException; + +public class InetAddress { + private final String name; + private final int ip; + + private InetAddress(String name) throws UnknownHostException { + this.name = name; + this.ip = ipv4AddressForName(name); + } + + public String getHostName() { + return name; + } + + public String getHostAddress() { + try { + return new InetAddress(name).toString(); + } catch (UnknownHostException e) { + return null; // Strange case + } + } + + public static InetAddress getByName(String name) throws UnknownHostException { + try { + Socket.init(); + return new InetAddress(name); + } catch (IOException e) { + UnknownHostException uhe = new UnknownHostException(name); + uhe.initCause(e); + throw uhe; + } + } + + public byte[] getAddress() { + byte[] res = new byte[4]; + res[0] = (byte) ( ip >>> 24); + res[1] = (byte) ((ip >>> 16) & 0xFF); + res[2] = (byte) ((ip >>> 8 ) & 0xFF); + res[3] = (byte) ((ip ) & 0xFF); + return res; + } + + @Override + public String toString() { + byte[] addr = getAddress(); + return (int)((addr[0] + 256) % 256) + "." + + (int)((addr[1] + 256) % 256) + "." + + (int)((addr[2] + 256) % 256) + "." + + (int)((addr[3] + 256) % 256); + } + + public int getRawAddress() { + return ip; + } + + static native int ipv4AddressForName(String name) throws UnknownHostException; + + public boolean equals(Object o) { + return o instanceof InetAddress && ((InetAddress) o).ip == ip; + } + + public int hashCode() { + return ip; + } +} diff --git a/sgx-jvm/avian/classpath/java/net/InetSocketAddress.java b/sgx-jvm/avian/classpath/java/net/InetSocketAddress.java new file mode 100644 index 0000000000..01d34a8734 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/net/InetSocketAddress.java @@ -0,0 +1,65 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.net; + +public class InetSocketAddress extends SocketAddress { + private final String host; + private final InetAddress address; + private final int port; + + public InetSocketAddress(String host, int port) { + InetAddress address; + try { + address = InetAddress.getByName(host); + host = address.getHostName(); + } catch (UnknownHostException e) { + address = null; + } + this.host = host; + this.address = address; + this.port = port; + } + + public InetSocketAddress(InetAddress address, int port) { + this.host = address.getHostName(); + this.address = address; + this.port = port; + } + + public InetAddress getAddress() { + return address; + } + + public String getHostName() { + return host; + } + + public int getPort() { + return port; + } + + public boolean equals(Object o) { + if (o instanceof InetSocketAddress) { + InetSocketAddress a = (InetSocketAddress) o; + return a.address.equals(address) && a.port == port; + } else { + return false; + } + } + + public int hashCode() { + return port ^ address.hashCode(); + } + + public String toString() { + return getHostName() + ":" + port; + } +} diff --git a/sgx-jvm/avian/classpath/java/net/JarURLConnection.java b/sgx-jvm/avian/classpath/java/net/JarURLConnection.java new file mode 100644 index 0000000000..bdfa5834fb --- /dev/null +++ b/sgx-jvm/avian/classpath/java/net/JarURLConnection.java @@ -0,0 +1,22 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.net; + +import java.io.IOException; +import java.util.jar.JarFile; + +public abstract class JarURLConnection extends URLConnection { + public JarURLConnection(URL url) { + super(url); + } + + public abstract JarFile getJarFile() throws IOException; +} diff --git a/sgx-jvm/avian/classpath/java/net/MalformedURLException.java b/sgx-jvm/avian/classpath/java/net/MalformedURLException.java new file mode 100644 index 0000000000..a635c7d457 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/net/MalformedURLException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.net; + +public class MalformedURLException extends Exception { + public MalformedURLException(String message) { + super(message); + } + + public MalformedURLException() { + this(null); + } +} diff --git a/sgx-jvm/avian/classpath/java/net/ProtocolFamily.java b/sgx-jvm/avian/classpath/java/net/ProtocolFamily.java new file mode 100644 index 0000000000..7cbaaad567 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/net/ProtocolFamily.java @@ -0,0 +1,13 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.net; + +public interface ProtocolFamily { } diff --git a/sgx-jvm/avian/classpath/java/net/ServerSocket.java b/sgx-jvm/avian/classpath/java/net/ServerSocket.java new file mode 100644 index 0000000000..21836741ae --- /dev/null +++ b/sgx-jvm/avian/classpath/java/net/ServerSocket.java @@ -0,0 +1,17 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.net; + +import java.io.IOException; + +public abstract class ServerSocket { + public abstract void bind(SocketAddress address) throws IOException; +} diff --git a/sgx-jvm/avian/classpath/java/net/Socket.java b/sgx-jvm/avian/classpath/java/net/Socket.java new file mode 100644 index 0000000000..9a32b88f7c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/net/Socket.java @@ -0,0 +1,201 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.net; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class Socket implements Closeable, AutoCloseable { + + private static final int SD_RECEIVE = 0x00; + private static final int SD_SEND = 0x01; + private static final int SD_BOTH = 0x02; + + private static final int BUFFER_SIZE = 65535; + + /** + * This method is called from all routines that depend on winsock in windows, + * so it has public visibility + * @throws IOException + */ + public static native void init() throws IOException; + + /** + * Creates the native socket object + * @return Handle to the native object + * @throws IOException + */ + private static native /* SOCKET */long create() throws IOException; + + /** + * Connects the native socket object to an address:port + * @param sock Native socket handler + * @param addr Address to connect to + * @param port Port to connect to + * @throws IOException + */ + private static native void connect(/* SOCKET */long sock, long addr, short port) throws IOException; + private static native void bind(/* SOCKET */long sock, long addr, short port) throws IOException; + + private static native void send(/* SOCKET */long sock, byte[] buffer, int start_pos, int count) throws IOException; + private static native int recv(/* SOCKET */long sock, byte[] buffer, int start_pos, int count) throws IOException; + + private static native void abort(/* SOCKET */long sock); + private static native void close(/* SOCKET */long sock); + private static native void closeOutput(/* SOCKET */long sock); + private static native void closeInput(/* SOCKET */long sock); + + private class SocketInputStream extends InputStream { + + private boolean closed = false; + + @Override + public void close() throws IOException { + if (!closed) { + closeInput(sock); + closed = true; + } + super.close(); + } + + @Override + protected void finalize() throws Throwable { + close(); + super.finalize(); + } + + @Override + public int read() throws IOException { + byte[] buffer = new byte[1]; + int size = recv(sock, buffer, 0, 1); + if (size == 0) { + return -1; + } + return buffer[0]; + } + + @Override + public int read(byte[] buffer) throws IOException { + if(buffer.length == 0) return 0; //spec says return 0 if buffer length is zero. + int fullSize = buffer.length; + int size; + size = recv(sock, buffer, 0, Math.min(fullSize, Socket.BUFFER_SIZE)); + fullSize -= size; + //removed loop, because otherwise interactive protocols will not work. + if(size < 0) throw new IOException("Error while reading stream"); //as the manpage of recv says, a value below zero indicates an error. + if(size == 0) return -1; // if the stream is closed (size == 0), then return -1 to indicate end of stream. + return size; + } + } + + private class SocketOutputStream extends OutputStream { + + private boolean closed = false; + + @Override + public void close() throws IOException { + if (!closed) { + closeOutput(sock); + closed = true; + } + super.close(); + } + + @Override + protected void finalize() throws Throwable { + close(); + super.finalize(); + } + + @Override + public void write(int c) throws IOException { + byte[] res = new byte[1]; + res[0] = (byte)c; + send(sock, res, 0, 1); + } + + @Override + public void write(byte[] buffer) throws IOException { + int fullSize = buffer.length; + int index = 0; + int size; + do { + size = Math.min(fullSize, Socket.BUFFER_SIZE); + send(sock, buffer, index, size); + fullSize -= size; + index += size; + } while (fullSize != 0 && size != 0); + } + + } + + private long sock; + private SocketInputStream inputStream; + private SocketOutputStream outputStream; + + public Socket() throws IOException { + Socket.init(); + sock = create(); + inputStream = new SocketInputStream(); + outputStream = new SocketOutputStream(); + } + + public SocketInputStream getInputStream() { + return inputStream; + } + + public SocketOutputStream getOutputStream() { + return outputStream; + } + + public Socket(InetAddress address, int port) throws IOException { + this(); + connect(sock, address.getRawAddress(), (short)port); + } + + public Socket(String host, int port) throws UnknownHostException, IOException { + this(InetAddress.getByName(host), port); + } + + public void bind(SocketAddress bindpoint) throws IOException { + if (bindpoint instanceof InetSocketAddress) { + InetSocketAddress inetBindpoint = (InetSocketAddress)bindpoint; + bind(sock, inetBindpoint.getAddress().getRawAddress(), (short) inetBindpoint.getPort()); + } + } + + public void setTcpNoDelay(boolean on) throws SocketException {} + + @Override + public void close() throws IOException { + close(sock); + } + + public void shutdownInput() throws IOException { + inputStream.close(); + } + + public void shutdownOutput() throws IOException { + outputStream.close(); + } + + public SocketAddress getRemoteSocketAddress() { + throw new UnsupportedOperationException(); + } + + @Override + protected void finalize() throws Throwable { + close(); + super.finalize(); + } +} diff --git a/sgx-jvm/avian/classpath/java/net/SocketAddress.java b/sgx-jvm/avian/classpath/java/net/SocketAddress.java new file mode 100644 index 0000000000..c0d6cafe42 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/net/SocketAddress.java @@ -0,0 +1,13 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.net; + +public abstract class SocketAddress { } diff --git a/sgx-jvm/avian/classpath/java/net/SocketException.java b/sgx-jvm/avian/classpath/java/net/SocketException.java new file mode 100644 index 0000000000..6d3da998ea --- /dev/null +++ b/sgx-jvm/avian/classpath/java/net/SocketException.java @@ -0,0 +1,23 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.net; + +import java.io.IOException; + +public class SocketException extends IOException { + public SocketException(String message) { + super(message); + } + + public SocketException() { + this(null); + } +} diff --git a/sgx-jvm/avian/classpath/java/net/StandardProtocolFamily.java b/sgx-jvm/avian/classpath/java/net/StandardProtocolFamily.java new file mode 100644 index 0000000000..4a7f1e8a4e --- /dev/null +++ b/sgx-jvm/avian/classpath/java/net/StandardProtocolFamily.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.net; + +public enum StandardProtocolFamily implements ProtocolFamily { + INET; +} diff --git a/sgx-jvm/avian/classpath/java/net/URL.java b/sgx-jvm/avian/classpath/java/net/URL.java new file mode 100644 index 0000000000..86f244786f --- /dev/null +++ b/sgx-jvm/avian/classpath/java/net/URL.java @@ -0,0 +1,114 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.net; + +import java.io.IOException; +import java.io.InputStream; + +public final class URL { + private final URLStreamHandler handler; + private String protocol; + private String host; + private int port; + private String file; + private String path; + private String query; + private String ref; + + public URL(String s) throws MalformedURLException { + int colon = s.indexOf(':'); + int slash = s.indexOf('/'); + if (colon > 0 && (slash < 0 || colon < slash)) { + handler = findHandler(s.substring(0, colon)); + handler.parseURL(this, s, colon + 1, s.length()); + } else { + throw new MalformedURLException(s); + } + } + + public String toString() { + return handler.toExternalForm(this); + } + + public String getProtocol() { + return protocol; + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + public String getFile() { + return file; + } + + public String getRef() { + return ref; + } + + public String getPath() { + return path; + } + + public String getQuery() { + return query; + } + + public URLConnection openConnection() throws IOException { + return handler.openConnection(this); + } + + public InputStream openStream() throws IOException { + return openConnection().getInputStream(); + } + + public Object getContent() throws IOException { + return openStream(); + } + + private static URLStreamHandler findHandler(String protocol) + throws MalformedURLException + { + if ("http".equals(protocol) || "https".equals(protocol)) { + return new avian.http.Handler(); + } else if ("avianvmresource".equals(protocol)) { + return new avian.avianvmresource.Handler(); + } else if ("file".equals(protocol)) { + return new avian.file.Handler(); + } else if ("jar".equals(protocol)) { + return new avian.jar.Handler(); + } else { + throw new MalformedURLException("unknown protocol: " + protocol); + } + } + + public void set(String protocol, String host, int port, String file, + String ref) + { + this.protocol = protocol; + this.host = host; + this.port = port; + this.file = file; + this.ref = ref; + + int q = file == null ? -1 : file.lastIndexOf('?'); + if (q != -1) { + this.query = file.substring(q + 1); + this.path = file.substring(0, q); + } else { + this.path = file; + } + } +} diff --git a/sgx-jvm/avian/classpath/java/net/URLClassLoader.java b/sgx-jvm/avian/classpath/java/net/URLClassLoader.java new file mode 100644 index 0000000000..2d210f13af --- /dev/null +++ b/sgx-jvm/avian/classpath/java/net/URLClassLoader.java @@ -0,0 +1,67 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.net; + +import java.io.File; + +import java.io.InputStream; +import java.io.IOException; +import java.io.ByteArrayOutputStream; + +public class URLClassLoader extends ClassLoader { + + private final File jarFile; + + public URLClassLoader(URL[] urls, ClassLoader parent) { + super(parent); + if(urls.length != 1) { + throw new UnsupportedOperationException(); + } + if(!urls[0].getProtocol().equals("file")) { + throw new UnsupportedOperationException(urls[0].getProtocol()); + } + this.jarFile = new File(urls[0].getFile()); + } + + + protected Class findClass(String name) throws ClassNotFoundException { + try { + InputStream stream = getResourceAsStream(name.replace(".", "/") + ".class"); + if(stream == null) { + throw new ClassNotFoundException("couldn't find class " + name); + } + byte[] buf = new byte[2048]; + ByteArrayOutputStream mem = new ByteArrayOutputStream(); + try { + int size; + while((size = stream.read(buf, 0, buf.length)) > 0) { + mem.write(buf, 0, size); + } + byte[] data = mem.toByteArray(); + return defineClass(name, data, 0, data.length); + } finally { + stream.close(); + } + } catch(IOException e) { + throw new ClassNotFoundException("couldn't find class " + name, e); + } + } + + public URL getResource(String path) { + try { + return new URL("jar:file:" + jarFile.getAbsolutePath() + "!/" + path); + } catch(MalformedURLException e) { + throw new RuntimeException(e); + } + } + + +} \ No newline at end of file diff --git a/sgx-jvm/avian/classpath/java/net/URLConnection.java b/sgx-jvm/avian/classpath/java/net/URLConnection.java new file mode 100644 index 0000000000..0a34889e42 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/net/URLConnection.java @@ -0,0 +1,64 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.net; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public abstract class URLConnection { + protected final URL url; + protected boolean doInput = true; + protected boolean doOutput = false; + protected boolean useCaches = true; + + protected URLConnection(URL url) { + this.url = url; + } + + public Object getContent() throws IOException { + return getInputStream(); + } + + public int getContentLength() { + return -1; + } + + public abstract void connect() throws IOException; + + public InputStream getInputStream() throws IOException { + throw new UnknownServiceException(); + } + + public OutputStream getOutputStream() throws IOException { + throw new UnknownServiceException(); + } + + public boolean getDoInput() { + return doInput; + } + + public boolean getDoOutput() { + return doOutput; + } + + public void setDoInput(boolean v) { + doInput = v; + } + + public void setDoOutput(boolean v) { + doInput = v; + } + + public void setUseCaches(boolean v) { + useCaches = v; + } +} diff --git a/sgx-jvm/avian/classpath/java/net/URLStreamHandler.java b/sgx-jvm/avian/classpath/java/net/URLStreamHandler.java new file mode 100644 index 0000000000..3699c73d18 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/net/URLStreamHandler.java @@ -0,0 +1,81 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.net; + +import java.io.IOException; + +public abstract class URLStreamHandler { + protected void parseURL(URL url, String s, int start, int end) + throws MalformedURLException + { + String protocol = s.substring(0, start - 1); + s = s.substring(start, end); + + String host = null; + int port = -1; + if (s.startsWith("//")) { + s = s.substring(2); + int colon = s.indexOf(':'); + int slash = s.indexOf('/'); + if (slash < 0) { + if (colon < 0) { + host = s; + } else { + host = s.substring(0, colon); + port = Integer.parseInt(s.substring(colon + 1)); + } + s = ""; + } else { + if (colon < 0 || colon > slash) { + host = s.substring(0, slash); + } else { + host = s.substring(0, colon); + port = Integer.parseInt(s.substring(colon + 1, slash)); + } + s = s.substring(slash); + } + } + + String file = null; + if (s.length() > 0) { + file = s; + } + + url.set(protocol, host, port, file, null); + } + + private static boolean equals(String a, String b) { + return (a == null && b == null) || (a != null && a.equals(b)); + } + + protected boolean equals(URL a, URL b) { + return equals(a.getHost(), b.getHost()) + && (a.getPort() == b.getPort()) + && equals(a.getFile(), b.getFile()); + } + + protected String toExternalForm(URL url) { + StringBuilder sb = new StringBuilder(); + sb.append(url.getProtocol()).append(":"); + if (url.getHost() != null) { + sb.append("//").append(url.getHost()); + if (url.getPort() >= 0) { + sb.append(":").append(url.getPort()); + } + } + if (url.getFile() != null) { + sb.append(url.getFile()); + } + return sb.toString(); + } + + protected abstract URLConnection openConnection(URL url) throws IOException; +} diff --git a/sgx-jvm/avian/classpath/java/net/UnknownHostException.java b/sgx-jvm/avian/classpath/java/net/UnknownHostException.java new file mode 100644 index 0000000000..4e4acd1ef3 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/net/UnknownHostException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.net; + +import java.io.IOException; + +public class UnknownHostException extends IOException { + public UnknownHostException(String host) { + super(host); + } + + public UnknownHostException() { } +} diff --git a/sgx-jvm/avian/classpath/java/net/UnknownServiceException.java b/sgx-jvm/avian/classpath/java/net/UnknownServiceException.java new file mode 100644 index 0000000000..1c415de16e --- /dev/null +++ b/sgx-jvm/avian/classpath/java/net/UnknownServiceException.java @@ -0,0 +1,23 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.net; + +import java.io.IOException; + +public class UnknownServiceException extends IOException { + public UnknownServiceException(String message) { + super(message); + } + + public UnknownServiceException() { + this(null); + } +} diff --git a/sgx-jvm/avian/classpath/java/nio/ArrayByteBuffer.java b/sgx-jvm/avian/classpath/java/nio/ArrayByteBuffer.java new file mode 100644 index 0000000000..d0e41a2e0c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/ArrayByteBuffer.java @@ -0,0 +1,100 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio; + +class ArrayByteBuffer extends ByteBuffer { + private final byte[] array; + private final int arrayOffset; + + ArrayByteBuffer(byte[] array, int offset, int length, boolean readOnly) { + super(readOnly); + + this.array = array; + this.arrayOffset = offset; + this.capacity = length; + this.limit = length; + this.position = 0; + } + + public ByteBuffer asReadOnlyBuffer() { + ByteBuffer b = new ArrayByteBuffer(array, arrayOffset, capacity, true); + b.position(position()); + b.limit(limit()); + return b; + } + + public boolean hasArray() { + return true; + } + + public byte[] array() { + return array; + } + + public ByteBuffer slice() { + return new ArrayByteBuffer + (array, arrayOffset + position, remaining(), true); + } + + public int arrayOffset() { + return arrayOffset; + } + + protected void doPut(int position, byte val) { + array[arrayOffset + position] = val; + } + + public ByteBuffer put(ByteBuffer src) { + int length = src.remaining(); + checkPut(position, length, false); + src.get(array, arrayOffset + position, length); + position += length; + return this; + } + + public ByteBuffer put(byte[] src, int offset, int length) { + checkPut(position, length, false); + + System.arraycopy(src, offset, array, arrayOffset + position, length); + position += length; + + return this; + } + + public ByteBuffer get(byte[] dst, int offset, int length) { + checkGet(position, length, false); + + System.arraycopy(array, arrayOffset + position, dst, offset, length); + position += length; + + return this; + } + + protected byte doGet(int position) { + return array[arrayOffset+position]; + } + + public String toString() { + return "(ArrayByteBuffer with array: " + array + + " arrayOffset: " + arrayOffset + + " position: " + position + + " limit: " + limit + + " capacity: " + capacity + ")"; + } + + @Override + public ByteBuffer duplicate() { + ByteBuffer b = new ArrayByteBuffer(array, arrayOffset, capacity, isReadOnly()); + b.limit(this.limit()); + b.position(this.position()); + return b; + } +} diff --git a/sgx-jvm/avian/classpath/java/nio/ArrayCharBuffer.java b/sgx-jvm/avian/classpath/java/nio/ArrayCharBuffer.java new file mode 100644 index 0000000000..a4e93aa191 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/ArrayCharBuffer.java @@ -0,0 +1,92 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio; + +class ArrayCharBuffer extends CharBuffer { + private final char[] array; + private final int arrayOffset; + + ArrayCharBuffer(char[] array, int offset, int length, boolean readOnly) { + super(readOnly); + + this.array = array; + this.arrayOffset = offset; + this.capacity = length; + this.limit = length; + this.position = 0; + } + + public CharBuffer asReadOnlyBuffer() { + CharBuffer b = new ArrayCharBuffer(array, arrayOffset, capacity, true); + b.position(position()); + b.limit(limit()); + return b; + } + + public boolean hasArray() { + return true; + } + + public char[] array() { + return array; + } + + public CharBuffer slice() { + return new ArrayCharBuffer + (array, arrayOffset + position, remaining(), true); + } + + public int arrayOffset() { + return arrayOffset; + } + + protected void doPut(int position, char val) { + array[arrayOffset + position] = val; + } + + public CharBuffer put(CharBuffer src) { + int length = src.remaining(); + checkPut(position, length, false); + src.get(array, arrayOffset + position, length); + position += length; + return this; + } + + public CharBuffer put(char[] src, int offset, int length) { + checkPut(position, length, false); + + System.arraycopy(src, offset, array, arrayOffset + position, length); + position += length; + + return this; + } + + public CharBuffer get(char[] dst, int offset, int length) { + checkGet(position, length, false); + + System.arraycopy(array, arrayOffset + position, dst, offset, length); + position += length; + + return this; + } + + protected char doGet(int position) { + return array[arrayOffset+position]; + } + + public String toString() { + return "(ArrayCharBuffer with array: " + array + + " arrayOffset: " + arrayOffset + + " position: " + position + + " limit: " + limit + + " capacity: " + capacity + ")"; + } +} diff --git a/sgx-jvm/avian/classpath/java/nio/Buffer.java b/sgx-jvm/avian/classpath/java/nio/Buffer.java new file mode 100644 index 0000000000..f03e178a2b --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/Buffer.java @@ -0,0 +1,69 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio; + +public abstract class Buffer { + protected int capacity; + protected int position; + protected int limit; + protected boolean readonly; + + public final int limit() { + return limit; + } + + public final int remaining() { + return limit-position; + } + + public final int position() { + return position; + } + + public final int capacity() { + return capacity; + } + + public final Buffer limit(int newLimit) { + limit = newLimit; + return this; + } + + public final Buffer position(int newPosition) { + position = newPosition; + return this; + } + + public final boolean hasRemaining() { + return remaining() > 0; + } + + public final Buffer clear() { + position = 0; + limit = capacity; + return this; + } + + public final Buffer flip() { + limit = position; + position = 0; + return this; + } + + public final Buffer rewind() { + position = 0; + return this; + } + + public boolean isReadOnly() { + return readonly; + } +} diff --git a/sgx-jvm/avian/classpath/java/nio/BufferOverflowException.java b/sgx-jvm/avian/classpath/java/nio/BufferOverflowException.java new file mode 100644 index 0000000000..b1cb402ab2 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/BufferOverflowException.java @@ -0,0 +1,14 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio; + +public class BufferOverflowException extends RuntimeException { +} diff --git a/sgx-jvm/avian/classpath/java/nio/BufferUnderflowException.java b/sgx-jvm/avian/classpath/java/nio/BufferUnderflowException.java new file mode 100644 index 0000000000..ec6afd7e75 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/BufferUnderflowException.java @@ -0,0 +1,14 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio; + +public class BufferUnderflowException extends RuntimeException { +} diff --git a/sgx-jvm/avian/classpath/java/nio/ByteBuffer.java b/sgx-jvm/avian/classpath/java/nio/ByteBuffer.java new file mode 100644 index 0000000000..fddb06481b --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/ByteBuffer.java @@ -0,0 +1,337 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio; + +public abstract class ByteBuffer + extends Buffer + implements Comparable +{ + + protected ByteBuffer(boolean readOnly) { + this.readonly = readOnly; + } + + public static ByteBuffer allocate(int capacity) { + return new ArrayByteBuffer(new byte[capacity], 0, capacity, false); + } + + public static ByteBuffer allocateDirect(int capacity) { + return FixedArrayByteBuffer.make(capacity); + } + + public static ByteBuffer wrap(byte[] array) { + return wrap(array, 0, array.length); + } + + public static ByteBuffer wrap(byte[] array, int offset, int length) { + return new ArrayByteBuffer(array, offset, length, false); + } + + public abstract ByteBuffer asReadOnlyBuffer(); + + public abstract ByteBuffer slice(); + + public abstract ByteBuffer duplicate(); + + protected abstract void doPut(int offset, byte val); + + public abstract ByteBuffer put(byte[] arr, int offset, int len); + + protected abstract byte doGet(int offset); + + public abstract ByteBuffer get(byte[] dst, int offset, int length); + + public boolean hasArray() { + return false; + } + + public ByteBuffer compact() { + int remaining = remaining(); + + if (position != 0) { + ByteBuffer b = slice(); + position = 0; + put(b); + } + + position = remaining; + limit(capacity()); + + return this; + } + + public ByteBuffer put(ByteBuffer src) { + if (src.hasArray()) { + checkPut(position, src.remaining(), false); + + put(src.array(), src.arrayOffset() + src.position, src.remaining()); + src.position(src.position() + src.remaining()); + + return this; + } else { + byte[] buffer = new byte[src.remaining()]; + src.get(buffer); + return put(buffer); + } + } + + public int compareTo(ByteBuffer o) { + int end = (remaining() < o.remaining() ? remaining() : o.remaining()); + + for (int i = 0; i < end; ++i) { + int d = get(position + i) - o.get(o.position + i); + if (d != 0) { + return d; + } + } + return remaining() - o.remaining(); + } + + public boolean equals(Object o) { + return o instanceof ByteBuffer && compareTo((ByteBuffer) o) == 0; + } + + public byte[] array() { + throw new UnsupportedOperationException(); + } + + public int arrayOffset() { + throw new UnsupportedOperationException(); + } + + public ByteBuffer put(int offset, byte val) { + checkPut(offset, 1, true); + doPut(offset, val); + return this; + } + + public ByteBuffer put(byte val) { + checkPut(position, 1, false); + doPut(position, val); + ++ position; + return this; + } + + public ByteBuffer put(byte[] arr) { + return put(arr, 0, arr.length); + } + + private void rawPutLong(int position, long val) { + doPut(position , (byte) ((val >> 56) & 0xff)); + doPut(position + 1, (byte) ((val >> 48) & 0xff)); + doPut(position + 2, (byte) ((val >> 40) & 0xff)); + doPut(position + 3, (byte) ((val >> 32) & 0xff)); + doPut(position + 4, (byte) ((val >> 24) & 0xff)); + doPut(position + 5, (byte) ((val >> 16) & 0xff)); + doPut(position + 6, (byte) ((val >> 8) & 0xff)); + doPut(position + 7, (byte) ((val ) & 0xff)); + } + + private void rawPutInt(int position, int val) { + doPut(position , (byte) ((val >> 24) & 0xff)); + doPut(position + 1, (byte) ((val >> 16) & 0xff)); + doPut(position + 2, (byte) ((val >> 8) & 0xff)); + doPut(position + 3, (byte) ((val ) & 0xff)); + } + + private void rawPutShort(int position, short val) { + doPut(position , (byte) ((val >> 8) & 0xff)); + doPut(position + 1, (byte) ((val ) & 0xff)); + } + + public ByteBuffer putDouble(int position, double val) { + return putLong(position, Double.doubleToRawLongBits(val)); + } + + public ByteBuffer putFloat(int position, float val) { + return putInt(position, Float.floatToRawIntBits(val)); + } + + public ByteBuffer putLong(int position, long val) { + checkPut(position, 8, true); + + rawPutLong(position, val); + + return this; + } + + public ByteBuffer putInt(int position, int val) { + checkPut(position, 4, true); + + rawPutInt(position, val); + + return this; + } + + public ByteBuffer putShort(int position, short val) { + checkPut(position, 2, true); + + rawPutShort(position, val); + + return this; + } + + public ByteBuffer putDouble(double val) { + return putLong(Double.doubleToRawLongBits(val)); + } + + public ByteBuffer putFloat(float val) { + return putInt(Float.floatToRawIntBits(val)); + } + + public ByteBuffer putLong(long val) { + checkPut(position, 8, false); + + rawPutLong(position, val); + position += 8; + return this; + } + + public ByteBuffer putInt(int val) { + checkPut(position, 4, false); + + rawPutInt(position, val); + position += 4; + return this; + } + + public ByteBuffer putShort(short val) { + checkPut(position, 2, false); + + rawPutShort(position, val); + position += 2; + return this; + } + + public byte get() { + checkGet(position, 1, false); + return doGet(position++); + } + + public byte get(int position) { + checkGet(position, 1, true); + return doGet(position); + } + + public ByteBuffer get(byte[] dst) { + return get(dst, 0, dst.length); + } + + public double getDouble(int position) { + return Double.longBitsToDouble(getLong(position)); + } + + public float getFloat(int position) { + return Float.intBitsToFloat(getInt(position)); + } + + public long getLong(int position) { + checkGet(position, 8, true); + + return rawGetLong(position); + } + + public int getInt(int position) { + checkGet(position, 4, true); + + return rawGetInt(position); + } + + public short getShort(int position) { + checkGet(position, 2, true); + + return rawGetShort(position); + } + + private long rawGetLong(int position) { + return (((long) (doGet(position ) & 0xFF)) << 56) + | (((long) (doGet(position + 1) & 0xFF)) << 48) + | (((long) (doGet(position + 2) & 0xFF)) << 40) + | (((long) (doGet(position + 3) & 0xFF)) << 32) + | (((long) (doGet(position + 4) & 0xFF)) << 24) + | (((long) (doGet(position + 5) & 0xFF)) << 16) + | (((long) (doGet(position + 6) & 0xFF)) << 8) + | (((long) (doGet(position + 7) & 0xFF)) ); + } + + private int rawGetInt(int position) { + return (((int) (doGet(position ) & 0xFF)) << 24) + | (((int) (doGet(position + 1) & 0xFF)) << 16) + | (((int) (doGet(position + 2) & 0xFF)) << 8) + | (((int) (doGet(position + 3) & 0xFF)) ); + } + + private short rawGetShort(int position) { + return (short) (( ((int) (doGet(position ) & 0xFF)) << 8) + | (((int) (doGet(position + 1) & 0xFF)) )); + } + + public double getDouble() { + return Double.longBitsToDouble(getLong()); + } + + public float getFloat() { + return Float.intBitsToFloat(getInt()); + } + + public long getLong() { + checkGet(position, 8, false); + + long r = rawGetLong(position); + position += 8; + return r; + } + + public int getInt() { + checkGet(position, 4, false); + + int r = rawGetInt(position); + position += 4; + return r; + } + + public short getShort() { + checkGet(position, 2, false); + + short r = rawGetShort(position); + position += 2; + return r; + } + + protected void checkPut(int position, int amount, boolean absolute) { + if (isReadOnly()) { + throw new ReadOnlyBufferException(); + } + + if (position < 0 || position+amount > limit) { + throw absolute + ? new IndexOutOfBoundsException() + : new BufferOverflowException(); + } + } + + protected void checkGet(int position, int amount, boolean absolute) { + if (amount > limit-position) { + throw absolute + ? new IndexOutOfBoundsException() + : new BufferUnderflowException(); + } + } + + public ByteBuffer order(ByteOrder order) { + if (order != ByteOrder.BIG_ENDIAN) throw new UnsupportedOperationException(); + return this; + } + + public ByteOrder order() { + return ByteOrder.BIG_ENDIAN; + } +} diff --git a/sgx-jvm/avian/classpath/java/nio/ByteOrder.java b/sgx-jvm/avian/classpath/java/nio/ByteOrder.java new file mode 100644 index 0000000000..6592bcd873 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/ByteOrder.java @@ -0,0 +1,40 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio; + +public final class ByteOrder { + public static final ByteOrder BIG_ENDIAN = new ByteOrder("BIG_ENDIAN"); + public static final ByteOrder LITTLE_ENDIAN = new ByteOrder("LITTLE_ENDIAN"); + + private static final ByteOrder NATIVE; + private static native boolean isNativeBigEndian(); + + static { + if (isNativeBigEndian()) + NATIVE = BIG_ENDIAN; + else + NATIVE = LITTLE_ENDIAN; + } + + private String name; + + private ByteOrder(String name) { + this.name = name; + } + + public String toString() { + return name; + } + + public static ByteOrder nativeOrder() { + return NATIVE; + } +} diff --git a/sgx-jvm/avian/classpath/java/nio/CharBuffer.java b/sgx-jvm/avian/classpath/java/nio/CharBuffer.java new file mode 100644 index 0000000000..1d92de5980 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/CharBuffer.java @@ -0,0 +1,154 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio; + +public abstract class CharBuffer + extends Buffer + implements Comparable +{ + private final boolean readOnly; + + protected CharBuffer(boolean readOnly) { + this.readOnly = readOnly; + } + + public static CharBuffer allocate(int capacity) { + return new ArrayCharBuffer(new char[capacity], 0, capacity, false); + } + + public static CharBuffer wrap(char[] array) { + return wrap(array, 0, array.length); + } + + public static CharBuffer wrap(char[] array, int offset, int length) { + return new ArrayCharBuffer(array, offset, length, false); + } + + public abstract CharBuffer asReadOnlyBuffer(); + + public abstract CharBuffer slice(); + + protected abstract void doPut(int offset, char value); + + public abstract CharBuffer put(char[] src, int offset, int length); + + protected abstract char doGet(int offset); + + public abstract CharBuffer get(char[] dst, int offset, int length); + + public boolean hasArray() { + return false; + } + + public CharBuffer compact() { + int remaining = remaining(); + + if (position != 0) { + CharBuffer b = slice(); + position = 0; + put(b); + } + + position = remaining; + limit(capacity()); + + return this; + } + + public CharBuffer put(CharBuffer src) { + if (src.hasArray()) { + checkPut(position, src.remaining(), false); + + put(src.array(), src.arrayOffset() + src.position, src.remaining()); + src.position(src.position() + src.remaining()); + + return this; + } else { + char[] buffer = new char[src.remaining()]; + src.get(buffer); + return put(buffer); + } + } + + public int compareTo(CharBuffer o) { + int end = (remaining() < o.remaining() ? remaining() : o.remaining()); + + for (int i = 0; i < end; ++i) { + int d = get(position + i) - o.get(o.position + i); + if (d != 0) { + return d; + } + } + return remaining() - o.remaining(); + } + + public boolean equals(Object o) { + return o instanceof CharBuffer && compareTo((CharBuffer) o) == 0; + } + + public char[] array() { + throw new UnsupportedOperationException(); + } + + public int arrayOffset() { + throw new UnsupportedOperationException(); + } + + public CharBuffer put(int offset, char val) { + checkPut(offset, 1, true); + doPut(offset, val); + return this; + } + + public CharBuffer put(char val) { + put(position, val); + ++ position; + return this; + } + + public CharBuffer put(char[] arr) { + return put(arr, 0, arr.length); + } + + public char get() { + checkGet(position, 1, false); + return doGet(position++); + } + + public char get(int position) { + checkGet(position, 1, true); + return doGet(position); + } + + public CharBuffer get(char[] dst) { + return get(dst, 0, dst.length); + } + + protected void checkPut(int position, int amount, boolean absolute) { + if (readOnly) { + throw new ReadOnlyBufferException(); + } + + if (position < 0 || position+amount > limit) { + throw absolute + ? new IndexOutOfBoundsException() + : new BufferOverflowException(); + } + } + + protected void checkGet(int position, int amount, boolean absolute) { + if (amount > limit-position) { + throw absolute + ? new IndexOutOfBoundsException() + : new BufferUnderflowException(); + } + } +} diff --git a/sgx-jvm/avian/classpath/java/nio/DirectByteBuffer.java b/sgx-jvm/avian/classpath/java/nio/DirectByteBuffer.java new file mode 100644 index 0000000000..ede7074a5b --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/DirectByteBuffer.java @@ -0,0 +1,113 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio; + +import sun.misc.Unsafe; + +class DirectByteBuffer extends ByteBuffer { + private static final Unsafe unsafe = Unsafe.getUnsafe(); + private static final int baseOffset = unsafe.arrayBaseOffset(byte[].class); + + protected final long address; + + protected DirectByteBuffer(long address, int capacity, boolean readOnly) { + super(readOnly); + + this.address = address; + this.capacity = capacity; + this.limit = capacity; + this.position = 0; + } + + protected DirectByteBuffer(long address, int capacity) { + this(address, capacity, false); + } + + public ByteBuffer asReadOnlyBuffer() { + ByteBuffer b = new DirectByteBuffer(address, capacity, true); + b.position(position()); + b.limit(limit()); + return b; + } + + public ByteBuffer slice() { + return new DirectByteBuffer(address + position, remaining(), true); + } + + protected void doPut(int position, byte val) { + unsafe.putByte(address + position, val); + } + + public ByteBuffer put(ByteBuffer src) { + if (src instanceof DirectByteBuffer) { + checkPut(position, src.remaining(), false); + + DirectByteBuffer b = (DirectByteBuffer) src; + + unsafe.copyMemory + (b.address + b.position, address + position, b.remaining()); + + position += b.remaining(); + b.position += b.remaining(); + + return this; + } else { + return super.put(src); + } + } + + public ByteBuffer put(byte[] src, int offset, int length) { + if (offset < 0 || offset + length > src.length) { + throw new ArrayIndexOutOfBoundsException(); + } + + checkPut(position, length, false); + + unsafe.copyMemory + (src, baseOffset + offset, null, address + position, length); + + position += length; + + return this; + } + + public ByteBuffer get(byte[] dst, int offset, int length) { + if (offset < 0 || offset + length > dst.length) { + throw new ArrayIndexOutOfBoundsException(); + } + + checkGet(position, length, false); + + unsafe.copyMemory + (null, address + position, dst, baseOffset + offset, length); + + return this; + } + + protected byte doGet(int position) { + return unsafe.getByte(address + position); + } + + public String toString() { + return "(DirectByteBuffer with address: " + address + + " position: " + position + + " limit: " + limit + + " capacity: " + capacity + ")"; + } + + @Override + public ByteBuffer duplicate() { + ByteBuffer b = new DirectByteBuffer(address, capacity, isReadOnly()); + b.limit(this.limit()); + b.position(this.position()); + return b; + } +} diff --git a/sgx-jvm/avian/classpath/java/nio/FixedArrayByteBuffer.java b/sgx-jvm/avian/classpath/java/nio/FixedArrayByteBuffer.java new file mode 100644 index 0000000000..d433e321af --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/FixedArrayByteBuffer.java @@ -0,0 +1,66 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio; + +class FixedArrayByteBuffer extends DirectByteBuffer { + private final byte[] array; + private final int arrayOffset; + + private FixedArrayByteBuffer(long address, + byte[] array, + int offset, + int capacity, + boolean readOnly) + { + super(address, capacity, readOnly); + + this.array = array; + this.arrayOffset = offset; + } + + public static FixedArrayByteBuffer make(int capacity) { + long[] address = new long[1]; + byte[] array = allocateFixed(capacity, address); + return new FixedArrayByteBuffer(address[0], array, 0, capacity, false); + } + + private static native byte[] allocateFixed(int capacity, long[] address); + + public ByteBuffer asReadOnlyBuffer() { + ByteBuffer b = new FixedArrayByteBuffer + (address, array, arrayOffset, capacity, true); + b.position(position()); + b.limit(limit()); + return b; + } + + public ByteBuffer slice() { + return new FixedArrayByteBuffer + (address + position, array, arrayOffset + position, remaining(), true); + } + + @Override + public ByteBuffer duplicate() { + ByteBuffer b = new FixedArrayByteBuffer(address, array, arrayOffset, capacity, isReadOnly()); + b.limit(this.limit()); + b.position(this.position()); + return b; + } + + public String toString() { + return "(FixedArrayByteBuffer with address: " + address + + " array: " + array + + " arrayOffset: " + arrayOffset + + " position: " + position + + " limit: " + limit + + " capacity: " + capacity + ")"; + } +} diff --git a/sgx-jvm/avian/classpath/java/nio/ReadOnlyBufferException.java b/sgx-jvm/avian/classpath/java/nio/ReadOnlyBufferException.java new file mode 100644 index 0000000000..62bcdd8ebb --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/ReadOnlyBufferException.java @@ -0,0 +1,14 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio; + +public class ReadOnlyBufferException extends UnsupportedOperationException { +} diff --git a/sgx-jvm/avian/classpath/java/nio/channels/Channel.java b/sgx-jvm/avian/classpath/java/nio/channels/Channel.java new file mode 100644 index 0000000000..172e1d69e8 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/channels/Channel.java @@ -0,0 +1,18 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio.channels; + +import java.io.IOException; + +public interface Channel { + public void close() throws IOException; + public boolean isOpen(); +} diff --git a/sgx-jvm/avian/classpath/java/nio/channels/Channels.java b/sgx-jvm/avian/classpath/java/nio/channels/Channels.java new file mode 100644 index 0000000000..9d0e377ea3 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/channels/Channels.java @@ -0,0 +1,142 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio.channels; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; + +public class Channels { + public static InputStream newInputStream(ReadableByteChannel channel) { + return new MyInputStream(channel); + } + + public static OutputStream newOutputStream(WritableByteChannel channel) { + return new MyOutputStream(channel); + } + + public static ReadableByteChannel newChannel(InputStream stream) { + return new InputStreamChannel(stream); + } + + public static WritableByteChannel newChannel(OutputStream stream) { + return new OutputStreamChannel(stream); + } + + private static class MyInputStream extends InputStream { + private final ReadableByteChannel channel; + + public MyInputStream(ReadableByteChannel channel) { + this.channel = channel; + } + + public int read() throws IOException { + byte[] buffer = new byte[1]; + int r = read(buffer); + if (r == -1) { + return -1; + } else { + return buffer[0] & 0xFF; + } + } + + public int read(byte[] buffer, int offset, int length) throws IOException { + return channel.read(ByteBuffer.wrap(buffer, offset, length)); + } + + public void close() throws IOException { + channel.close(); + } + } + + private static class MyOutputStream extends OutputStream { + private final WritableByteChannel channel; + + public MyOutputStream(WritableByteChannel channel) { + this.channel = channel; + } + + public void write(int v) throws IOException { + byte[] buffer = new byte[] { (byte) (v & 0xFF) }; + write(buffer); + } + + public void write(byte[] buffer, int offset, int length) + throws IOException + { + channel.write(ByteBuffer.wrap(buffer, offset, length)); + } + + public void close() throws IOException { + channel.close(); + } + } + + private static class InputStreamChannel implements ReadableByteChannel { + private InputStream stream; + + public InputStreamChannel(InputStream stream) { + this.stream = stream; + } + + public void close() throws IOException { + if (stream != null) { + stream.close(); + stream = null; + } + } + + public boolean isOpen() { + return stream != null; + } + + public int read(ByteBuffer b) throws IOException { + int c = stream.read + (b.array(), b.arrayOffset() + b.position(), b.remaining()); + + if (c > 0) { + b.position(b.position() + c); + } + + return c; + } + } + + private static class OutputStreamChannel implements WritableByteChannel { + private OutputStream stream; + + public OutputStreamChannel(OutputStream stream) { + this.stream = stream; + } + + public void close() throws IOException { + if (stream != null) { + stream.close(); + stream = null; + } + } + + public boolean isOpen() { + return stream != null; + } + + public int write(ByteBuffer b) throws IOException { + stream.write(b.array(), b.arrayOffset() + b.position(), b.remaining()); + + int c = b.remaining(); + + b.position(b.limit()); + + return c; + } + } +} diff --git a/sgx-jvm/avian/classpath/java/nio/channels/ClosedSelectorException.java b/sgx-jvm/avian/classpath/java/nio/channels/ClosedSelectorException.java new file mode 100644 index 0000000000..df85114f18 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/channels/ClosedSelectorException.java @@ -0,0 +1,13 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio.channels; + +public class ClosedSelectorException extends IllegalStateException { } diff --git a/sgx-jvm/avian/classpath/java/nio/channels/DatagramChannel.java b/sgx-jvm/avian/classpath/java/nio/channels/DatagramChannel.java new file mode 100644 index 0000000000..f0a2b39a66 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/channels/DatagramChannel.java @@ -0,0 +1,239 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio.channels; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.net.SocketAddress; +import java.net.InetSocketAddress; +import java.net.ProtocolFamily; +import java.net.Socket; +import java.net.SocketException; +import java.net.DatagramSocket; +import java.net.StandardProtocolFamily; + +// TODO: This class is both divergent from the Java standard and incomplete. +public class DatagramChannel extends SelectableChannel + implements ReadableByteChannel, WritableByteChannel +{ + public static final int InvalidSocket = -1; + + private int socket = makeSocket(); + private boolean blocking = true; + private boolean connected = false; + + public SelectableChannel configureBlocking(boolean v) throws IOException { + blocking = v; + configureBlocking(); + return this; + } + + private void configureBlocking() throws IOException { + if (socket != InvalidSocket) { + configureBlocking(socket, blocking); + } + } + + int socketFD() { + return socket; + } + + void handleReadyOps(int ops) { + // ignore + } + + public static DatagramChannel open(ProtocolFamily family) + throws IOException + { + if (family.equals(StandardProtocolFamily.INET)) { + Socket.init(); + + return new DatagramChannel(); + } else { + throw new UnsupportedOperationException(); + } + } + + public static DatagramChannel open() + throws IOException + { + return open(StandardProtocolFamily.INET); + } + + public DatagramSocket socket() { + return new Handle(); + } + + public DatagramChannel bind(SocketAddress address) throws IOException { + InetSocketAddress inetAddress; + try { + inetAddress = (InetSocketAddress) address; + } catch (ClassCastException e) { + throw new UnsupportedAddressTypeException(); + } + + if (inetAddress == null) { + bind(socket, 0, 0); + } else { + bind(socket, inetAddress.getAddress().getRawAddress(), + inetAddress.getPort()); + } + + return this; + } + + public DatagramChannel connect(SocketAddress address) throws IOException { + InetSocketAddress inetAddress; + try { + inetAddress = (InetSocketAddress) address; + } catch (ClassCastException e) { + throw new UnsupportedAddressTypeException(); + } + + connected = connect(socket, inetAddress.getAddress().getRawAddress(), + inetAddress.getPort()); + + return this; + } + + public int write(ByteBuffer b) throws IOException { + if (b.remaining() == 0) return 0; + + byte[] array = b.array(); + if (array == null) throw new NullPointerException(); + + int c = write + (socket, array, b.arrayOffset() + b.position(), b.remaining(), blocking); + + if (c > 0) { + b.position(b.position() + c); + } + + return c; + } + + public int read(ByteBuffer b) throws IOException { + int p = b.position(); + receive(b); + return b.position() - p; + } + + public SocketAddress receive(ByteBuffer b) throws IOException { + if (b.remaining() == 0) return null; + + byte[] array = b.array(); + if (array == null) throw new NullPointerException(); + + int[] address = new int[2]; + + int c = receive + (socket, array, b.arrayOffset() + b.position(), b.remaining(), blocking, + address); + + if (c > 0) { + b.position(b.position() + c); + + return new InetSocketAddress(ipv4ToString(address[0]), address[1]); + } else { + return null; + } + } + + public int send(ByteBuffer b, SocketAddress address) throws IOException { + if (b.remaining() == 0) return 0; + + InetSocketAddress inetAddress; + try { + inetAddress = (InetSocketAddress) address; + } catch (ClassCastException e) { + throw new UnsupportedAddressTypeException(); + } + + byte[] array = b.array(); + if (array == null) throw new NullPointerException(); + + int c = send + (socket, inetAddress.getAddress().getRawAddress(), + inetAddress.getPort(), array, b.arrayOffset() + b.position(), + b.remaining(), blocking); + + if (c > 0) { + b.position(b.position() + c); + } + + return c; + } + + private static String ipv4ToString(int address) { + StringBuilder sb = new StringBuilder(); + + sb.append( address >> 24 ).append('.') + .append((address >> 16) & 0xFF).append('.') + .append((address >> 8) & 0xFF).append('.') + .append( address & 0xFF); + + return sb.toString(); + } + + public class Handle extends DatagramSocket { + public SocketAddress getRemoteSocketAddress() { + throw new UnsupportedOperationException(); + } + + public void bind(SocketAddress address) throws SocketException { + try { + DatagramChannel.this.bind(address); + } catch (SocketException e) { + throw e; + } catch (IOException e) { + SocketException se = new SocketException(); + se.initCause(e); + throw se; + } + } + } + + public boolean isConnected() { return connected; } + + /** TODO: This is probably incomplete. */ + public DatagramChannel disconnect() throws IOException { + connect(socket, 0, 0); + connected = false; + return this; + } + + public void close() throws IOException { + if (isOpen()) { + super.close(); + close(socket); + } + } + + private static native int makeSocket(); + private static native void configureBlocking(int socket, boolean blocking) + throws IOException; + private static native void bind(int socket, int host, int port) + throws IOException; + private static native boolean connect(int socket, int host, int port) + throws IOException; + private static native int write(int socket, byte[] array, int offset, + int length, boolean blocking) + throws IOException; + private static native int send(int socket, int host, int port, + byte[] array, int offset, + int length, boolean blocking) + throws IOException; + private static native int receive(int socket, byte[] array, int offset, + int length, boolean blocking, + int[] address) + throws IOException; + private static native void close(int socket); +} diff --git a/sgx-jvm/avian/classpath/java/nio/channels/FileChannel.java b/sgx-jvm/avian/classpath/java/nio/channels/FileChannel.java new file mode 100644 index 0000000000..af8102317b --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/channels/FileChannel.java @@ -0,0 +1,35 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio.channels; + +import java.io.IOException; +import java.nio.ByteBuffer; + +public abstract class FileChannel implements Channel { + + public static enum MapMode { + PRIVATE, READ_ONLY, READ_WRITE + }; + + public abstract int read(ByteBuffer dst) throws IOException; + + public abstract int read(ByteBuffer dst, long position) throws IOException; + + public abstract int write(ByteBuffer dst) throws IOException; + + public abstract int write(ByteBuffer dst, long position) throws IOException; + + public abstract long position() throws IOException; + + public abstract FileChannel position(long position) throws IOException; + + public abstract long size() throws IOException; +} diff --git a/sgx-jvm/avian/classpath/java/nio/channels/GatheringByteChannel.java b/sgx-jvm/avian/classpath/java/nio/channels/GatheringByteChannel.java new file mode 100644 index 0000000000..da461fcefe --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/channels/GatheringByteChannel.java @@ -0,0 +1,20 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio.channels; + +import java.io.IOException; +import java.nio.ByteBuffer; + +public interface GatheringByteChannel extends WritableByteChannel { + public long write(ByteBuffer[] srcs) throws IOException; + public long write(ByteBuffer[] srcs, int offset, int length) + throws IOException; +} diff --git a/sgx-jvm/avian/classpath/java/nio/channels/ReadableByteChannel.java b/sgx-jvm/avian/classpath/java/nio/channels/ReadableByteChannel.java new file mode 100644 index 0000000000..d3891eabeb --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/channels/ReadableByteChannel.java @@ -0,0 +1,18 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio.channels; + +import java.io.IOException; +import java.nio.ByteBuffer; + +public interface ReadableByteChannel extends Channel { + public int read(ByteBuffer b) throws IOException; +} diff --git a/sgx-jvm/avian/classpath/java/nio/channels/SelectableChannel.java b/sgx-jvm/avian/classpath/java/nio/channels/SelectableChannel.java new file mode 100644 index 0000000000..3e544f937e --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/channels/SelectableChannel.java @@ -0,0 +1,43 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio.channels; + +import java.io.IOException; +import java.nio.ByteBuffer; + +public abstract class SelectableChannel implements Channel { + private SelectionKey key; + private boolean open = true; + + abstract int socketFD(); + + abstract void handleReadyOps(int ops); + + public abstract SelectableChannel configureBlocking(boolean v) + throws IOException; + + public SelectionKey register(Selector selector, int interestOps, + Object attachment) + { + key = new SelectionKey(this, selector, interestOps, attachment); + selector.add(key); + return key; + } + + public boolean isOpen() { + return open; + } + + public void close() throws IOException { + open = false; + key = null; + } +} diff --git a/sgx-jvm/avian/classpath/java/nio/channels/SelectionKey.java b/sgx-jvm/avian/classpath/java/nio/channels/SelectionKey.java new file mode 100644 index 0000000000..ca41b74d3a --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/channels/SelectionKey.java @@ -0,0 +1,83 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio.channels; + +public class SelectionKey { + public static final int OP_READ = 1 << 0; + public static final int OP_WRITE = 1 << 2; + public static final int OP_CONNECT = 1 << 3; + public static final int OP_ACCEPT = 1 << 4; + + private final SelectableChannel channel; + private final Selector selector; + private int interestOps; + private int readyOps; + private final Object attachment; + + public SelectionKey(SelectableChannel channel, Selector selector, + int interestOps, Object attachment) + { + this.channel = channel; + this.selector = selector; + this.interestOps = interestOps; + this.attachment = attachment; + this.readyOps = 0; + } + + public int interestOps() { + return interestOps; + } + + public SelectionKey interestOps(int v) { + this.interestOps = v; + return this; + } + + public int readyOps() { + return readyOps; + } + + public void readyOps(int v) { + this.readyOps = v; + } + + public boolean isReadable() { + return (readyOps & OP_READ) != 0; + } + + public boolean isWritable() { + return (readyOps & OP_WRITE) != 0; + } + + public boolean isConnectable() { + return (readyOps & OP_CONNECT) != 0; + } + + public boolean isAcceptable() { + return (readyOps & OP_ACCEPT) != 0; + } + + public boolean isValid() { + return channel.isOpen() && selector.isOpen(); + } + + public SelectableChannel channel() { + return channel; + } + + public Selector selector() { + return selector; + } + + public Object attachment() { + return attachment; + } +} diff --git a/sgx-jvm/avian/classpath/java/nio/channels/Selector.java b/sgx-jvm/avian/classpath/java/nio/channels/Selector.java new file mode 100644 index 0000000000..ceb45031eb --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/channels/Selector.java @@ -0,0 +1,52 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio.channels; + +import java.io.IOException; +import java.util.Set; +import java.util.HashSet; + +public abstract class Selector { + protected final Set keys = new HashSet(); + protected final Set selectedKeys = new HashSet(); + + public static Selector open() throws IOException { + return new SocketSelector(); + } + + public void add(SelectionKey key) { + keys.add(key); + } + + public void remove(SelectionKey key) { + keys.remove(key); + } + + public Set keys() { + return keys; + } + + public Set selectedKeys() { + return selectedKeys; + } + + public abstract boolean isOpen(); + + public abstract Selector wakeup(); + + public abstract int selectNow() throws IOException; + + public abstract int select(long interval) throws IOException; + + public abstract int select() throws IOException; + + public abstract void close(); +} diff --git a/sgx-jvm/avian/classpath/java/nio/channels/ServerSocketChannel.java b/sgx-jvm/avian/classpath/java/nio/channels/ServerSocketChannel.java new file mode 100644 index 0000000000..6f0d601a8b --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/channels/ServerSocketChannel.java @@ -0,0 +1,93 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio.channels; + +import java.io.IOException; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.ServerSocket; +import java.net.Socket; + +public class ServerSocketChannel extends SelectableChannel { + private final SocketChannel channel; + + private ServerSocketChannel() throws IOException { + channel = new SocketChannel(); + } + + public static ServerSocketChannel open() throws IOException { + Socket.init(); + + return new ServerSocketChannel(); + } + + public int socketFD() { + return channel.socketFD(); + } + + public void handleReadyOps(int ops) { + channel.handleReadyOps(ops); + } + + public SelectableChannel configureBlocking(boolean v) throws IOException { + return channel.configureBlocking(v); + } + + public void close() throws IOException { + channel.close(); + } + + public SocketChannel accept() throws IOException { + SocketChannel c = new SocketChannel(); + c.socket = doAccept(); + c.connected = true; + return c; + } + + public ServerSocket socket() { + return new Handle(); + } + + private int doAccept() throws IOException { + while (true) { + int s = natDoAccept(channel.socket); + if (s != -1) { + return s; + } + // todo: throw ClosedByInterruptException if this thread was + // interrupted during the accept call + } + } + + private void doListen(int socket, int host, int port) throws IOException { + Socket.init(); + + natDoListen(socket, host, port); + } + + public class Handle extends ServerSocket { + public void bind(SocketAddress address) + throws IOException + { + InetSocketAddress a; + try { + a = (InetSocketAddress) address; + } catch (ClassCastException e) { + throw new IllegalArgumentException(); + } + doListen(channel.socket, a.getAddress().getRawAddress(), a.getPort()); + } + } + + private static native int natDoAccept(int socket) throws IOException; + private static native void natDoListen(int socket, int host, int port) throws IOException; +} diff --git a/sgx-jvm/avian/classpath/java/nio/channels/SocketChannel.java b/sgx-jvm/avian/classpath/java/nio/channels/SocketChannel.java new file mode 100644 index 0000000000..f551eb1dc3 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/channels/SocketChannel.java @@ -0,0 +1,214 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio.channels; + +import java.io.IOException; +import java.net.SocketException; +import java.net.SocketAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.nio.ByteBuffer; + +public class SocketChannel extends SelectableChannel + implements ReadableByteChannel, GatheringByteChannel +{ + public static final int InvalidSocket = -1; + + int socket = makeSocket(); + boolean connected = false; + boolean readyToConnect = false; + boolean blocking = true; + + public static SocketChannel open() throws IOException { + Socket.init(); + + return new SocketChannel(); + } + + public SelectableChannel configureBlocking(boolean v) throws IOException { + blocking = v; + if (socket != InvalidSocket) { + configureBlocking(socket, v); + } + return this; + } + + public boolean isBlocking() { + return blocking; + } + + public boolean isConnected() { + return connected; + } + + public Socket socket() { + try { + return new Handle(); + } catch (IOException e) { + return null; + } + } + + public boolean connect(SocketAddress address) throws IOException { + InetSocketAddress a; + try { + a = (InetSocketAddress) address; + } catch (ClassCastException e) { + throw new UnsupportedAddressTypeException(); + } + doConnect(socket, a.getAddress().getRawAddress(), a.getPort()); + configureBlocking(blocking); + return connected; + } + + public boolean finishConnect() throws IOException { + if (! connected) { + while (! readyToConnect) { + Selector selector = Selector.open(); + SelectionKey key = register(selector, SelectionKey.OP_CONNECT, null); + + if (blocking) { + selector.select(); + } else { + selector.selectNow(); + break; + } + } + + natFinishConnect(socket); + + connected = readyToConnect; + } + + return connected; + } + + public void close() throws IOException { + if (isOpen()) { + super.close(); + closeSocket(); + } + } + + private void doConnect(int socket, int host, int port) + throws IOException + { + connected = natDoConnect(socket, host, port); + } + + public int read(ByteBuffer b) throws IOException { + if (! isOpen()) return -1; + if (b.remaining() == 0) return 0; + + byte[] array = b.array(); + if (array == null) throw new NullPointerException(); + + int r = natRead(socket, array, b.arrayOffset() + b.position(), b.remaining(), blocking); + if (r > 0) { + b.position(b.position() + r); + } + return r; + } + + public int write(ByteBuffer b) throws IOException { + if (! connected) { + natThrowWriteError(socket); + } + if (b.remaining() == 0) return 0; + + byte[] array = b.array(); + if (array == null) throw new NullPointerException(); + + int w = natWrite(socket, array, b.arrayOffset() + b.position(), b.remaining(), blocking); + if (w > 0) { + b.position(b.position() + w); + } + return w; + } + + public long write(ByteBuffer[] srcs) throws IOException { + return write(srcs, 0, srcs.length); + } + + public long write(ByteBuffer[] srcs, int offset, int length) + throws IOException + { + long total = 0; + for (int i = offset; i < offset + length; ++i) { + total += write(srcs[i]); + if (srcs[i].hasRemaining()) { + return total; + } + } + return total; + } + + private void closeSocket() { + natCloseSocket(socket); + } + + int socketFD() { + return socket; + } + + void handleReadyOps(int ops) { + if ((ops & SelectionKey.OP_CONNECT) != 0) { + readyToConnect = true; + } + } + + public class Handle extends Socket { + public Handle() throws IOException { + super(); + } + + public void setTcpNoDelay(boolean on) throws SocketException { + natSetTcpNoDelay(socket, on); + } + + public void bind(SocketAddress address) + throws IOException + { + InetSocketAddress a; + try { + a = (InetSocketAddress) address; + } catch (ClassCastException e) { + throw new IllegalArgumentException(); + } + + if (a == null) { + SocketChannel.bind(socket, 0, 0); + } else { + SocketChannel.bind + (socket, a.getAddress().getRawAddress(), a.getPort()); + } + } + } + + private static native int makeSocket(); + private static native void configureBlocking(int socket, boolean blocking) + throws IOException; + + private static native void natSetTcpNoDelay(int socket, boolean on) + throws SocketException; + private static native void bind(int socket, int host, int port) + throws IOException; + private static native boolean natDoConnect(int socket, int host, int port) + throws IOException; + private static native void natFinishConnect(int socket) + throws IOException; + private static native int natRead(int socket, byte[] buffer, int offset, int length, boolean blocking) + throws IOException; + private static native int natWrite(int socket, byte[] buffer, int offset, int length, boolean blocking) + throws IOException; + private static native void natThrowWriteError(int socket) throws IOException; + private static native void natCloseSocket(int socket); +} diff --git a/sgx-jvm/avian/classpath/java/nio/channels/SocketSelector.java b/sgx-jvm/avian/classpath/java/nio/channels/SocketSelector.java new file mode 100644 index 0000000000..c17d2afbf2 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/channels/SocketSelector.java @@ -0,0 +1,133 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio.channels; + +import java.io.IOException; +import java.util.Iterator; +import java.net.Socket; + +class SocketSelector extends Selector { + protected volatile long state; + protected final Object lock = new Object(); + protected boolean woken = false; + + public SocketSelector() throws IOException { + Socket.init(); + + state = natInit(); + } + + public boolean isOpen() { + return state != 0; + } + + public Selector wakeup() { + synchronized (lock) { + if (isOpen() && (! woken)) { + woken = true; + + natWakeup(state); + } + } + return this; + } + + private boolean clearWoken() { + synchronized (lock) { + if (woken) { + woken = false; + return true; + } else { + return false; + } + } + } + + public synchronized int selectNow() throws IOException { + return doSelect(-1); + } + + public synchronized int select() throws IOException { + return doSelect(0); + } + + public synchronized int select(long interval) throws IOException { + if (interval < 0) throw new IllegalArgumentException(); + + return doSelect(interval); + } + + public int doSelect(long interval) throws IOException { + if (! isOpen()) { + throw new ClosedSelectorException(); + } + + selectedKeys.clear(); + + if (clearWoken()) interval = -1; + + int max=0; + for (Iterator it = keys.iterator(); + it.hasNext();) + { + SelectionKey key = it.next(); + SelectableChannel c = key.channel(); + int socket = c.socketFD(); + if (c.isOpen()) { + key.readyOps(0); + max = natSelectUpdateInterestSet + (socket, key.interestOps(), state, max); + } else { + natSelectClearAll(socket, state); + it.remove(); + } + } + + int r = natDoSocketSelect(state, max, interval); + + if (r > 0) { + for (SelectionKey key : keys) { + SelectableChannel c = key.channel(); + int socket = c.socketFD(); + int ready = natUpdateReadySet(socket, key.interestOps(), state); + key.readyOps(ready); + if (ready != 0) { + c.handleReadyOps(ready); + selectedKeys.add(key); + } + } + } + clearWoken(); + + return selectedKeys.size(); + } + + public synchronized void close() { + synchronized (lock) { + if (isOpen()) { + natClose(state); + state = 0; + } + } + } + + private static native long natInit(); + private static native void natWakeup(long state); + private static native void natClose(long state); + private static native void natSelectClearAll(int socket, long state); + private static native int natSelectUpdateInterestSet(int socket, + int interest, + long state, + int max); + private static native int natDoSocketSelect(long state, int max, long interval) + throws IOException; + private static native int natUpdateReadySet(int socket, int interest, long state); +} diff --git a/sgx-jvm/avian/classpath/java/nio/channels/UnsupportedAddressTypeException.java b/sgx-jvm/avian/classpath/java/nio/channels/UnsupportedAddressTypeException.java new file mode 100644 index 0000000000..01362bcf6c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/channels/UnsupportedAddressTypeException.java @@ -0,0 +1,17 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio.channels; + +public class UnsupportedAddressTypeException extends IllegalArgumentException { + public UnsupportedAddressTypeException() { + super(null, null); + } +} diff --git a/sgx-jvm/avian/classpath/java/nio/channels/WritableByteChannel.java b/sgx-jvm/avian/classpath/java/nio/channels/WritableByteChannel.java new file mode 100644 index 0000000000..842f5dedad --- /dev/null +++ b/sgx-jvm/avian/classpath/java/nio/channels/WritableByteChannel.java @@ -0,0 +1,18 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio.channels; + +import java.io.IOException; +import java.nio.ByteBuffer; + +public interface WritableByteChannel extends Channel { + public int write(ByteBuffer b) throws IOException; +} diff --git a/sgx-jvm/avian/classpath/java/security/AccessControlException.java b/sgx-jvm/avian/classpath/java/security/AccessControlException.java new file mode 100644 index 0000000000..a0a93028a2 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/security/AccessControlException.java @@ -0,0 +1,28 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.security; + +public class AccessControlException extends SecurityException { + private final Permission permission; + + public AccessControlException(String message) { + this(message, null); + } + + public AccessControlException(String message, Permission permission) { + super(message); + this.permission = permission; + } + + public Permission getPermission() { + return permission; + } +} diff --git a/sgx-jvm/avian/classpath/java/security/AccessController.java b/sgx-jvm/avian/classpath/java/security/AccessController.java new file mode 100644 index 0000000000..e0fba259f8 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/security/AccessController.java @@ -0,0 +1,29 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.security; + +/** + * No real access control is implemented here. + * + * @author zsombor + * + */ +public class AccessController { + + public static T doPrivileged (PrivilegedAction action) { + return action.run(); + } + + public static void checkPermission(Permission perm) throws AccessControlException { + + } + +} diff --git a/sgx-jvm/avian/classpath/java/security/AllPermission.java b/sgx-jvm/avian/classpath/java/security/AllPermission.java new file mode 100644 index 0000000000..c39089f22c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/security/AllPermission.java @@ -0,0 +1,19 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.security; + +public class AllPermission extends Permission { + public AllPermission() { + super(""); + } + + +} diff --git a/sgx-jvm/avian/classpath/java/security/BasicPermission.java b/sgx-jvm/avian/classpath/java/security/BasicPermission.java new file mode 100644 index 0000000000..74e734eb5c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/security/BasicPermission.java @@ -0,0 +1,19 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.security; + +public class BasicPermission extends Permission { + + public BasicPermission(String name) { + super(name); + } + +} diff --git a/sgx-jvm/avian/classpath/java/security/CodeSource.java b/sgx-jvm/avian/classpath/java/security/CodeSource.java new file mode 100644 index 0000000000..b32723caf0 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/security/CodeSource.java @@ -0,0 +1,26 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.security; + +import java.net.URL; +import java.security.cert.Certificate; + +public class CodeSource { + private final URL url; + + public CodeSource(URL url, Certificate[] certificates) { + this.url = url; + } + + public URL getLocation() { + return url; + } +} diff --git a/sgx-jvm/avian/classpath/java/security/Permission.java b/sgx-jvm/avian/classpath/java/security/Permission.java new file mode 100644 index 0000000000..9f2ddeddef --- /dev/null +++ b/sgx-jvm/avian/classpath/java/security/Permission.java @@ -0,0 +1,33 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.security; + +public abstract class Permission { + + protected String name; + + public Permission(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + '['+name+']'; + } + + public PermissionCollection newPermissionCollection() { + return null; + } +} diff --git a/sgx-jvm/avian/classpath/java/security/PermissionCollection.java b/sgx-jvm/avian/classpath/java/security/PermissionCollection.java new file mode 100644 index 0000000000..efdfc56a66 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/security/PermissionCollection.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.security; + +public abstract class PermissionCollection { + public abstract void add(Permission p); +} diff --git a/sgx-jvm/avian/classpath/java/security/Permissions.java b/sgx-jvm/avian/classpath/java/security/Permissions.java new file mode 100644 index 0000000000..919185cb7e --- /dev/null +++ b/sgx-jvm/avian/classpath/java/security/Permissions.java @@ -0,0 +1,41 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.security; + +import java.util.Map; +import java.util.HashMap; +import java.util.Set; +import java.util.HashSet; + +public class Permissions extends PermissionCollection { + private final Map collections = new HashMap(); + + public void add(Permission p) { + Class c = p.getClass(); + PermissionCollection pc = collections.get(c); + if (pc == null) { + pc = p.newPermissionCollection(); + if (pc == null) { + pc = new MyPermissionCollection(); + } + collections.put(c, pc); + } + pc.add(p); + } + + private static class MyPermissionCollection extends PermissionCollection { + private final Set permissions = new HashSet(); + + public void add(Permission p) { + permissions.add(p); + } + } +} diff --git a/sgx-jvm/avian/classpath/java/security/PrivilegedAction.java b/sgx-jvm/avian/classpath/java/security/PrivilegedAction.java new file mode 100644 index 0000000000..6e1a1f757b --- /dev/null +++ b/sgx-jvm/avian/classpath/java/security/PrivilegedAction.java @@ -0,0 +1,17 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.security; + +public interface PrivilegedAction { + + T run(); + +} diff --git a/sgx-jvm/avian/classpath/java/security/ProtectionDomain.java b/sgx-jvm/avian/classpath/java/security/ProtectionDomain.java new file mode 100644 index 0000000000..8186405853 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/security/ProtectionDomain.java @@ -0,0 +1,27 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.security; + +public class ProtectionDomain { + private final CodeSource codeSource; + private final PermissionCollection permissions; + + public ProtectionDomain(CodeSource codeSource, + PermissionCollection permissions) + { + this.codeSource = codeSource; + this.permissions = permissions; + } + + public CodeSource getCodeSource() { + return codeSource; + } +} diff --git a/sgx-jvm/avian/classpath/java/security/SecurityPermission.java b/sgx-jvm/avian/classpath/java/security/SecurityPermission.java new file mode 100644 index 0000000000..22a356a731 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/security/SecurityPermission.java @@ -0,0 +1,19 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.security; + +public class SecurityPermission extends BasicPermission { + + public SecurityPermission(String name) { + super(name); + } + +} diff --git a/sgx-jvm/avian/classpath/java/security/cert/Certificate.java b/sgx-jvm/avian/classpath/java/security/cert/Certificate.java new file mode 100644 index 0000000000..956aadc592 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/security/cert/Certificate.java @@ -0,0 +1,13 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.security.cert; + +public abstract class Certificate { } diff --git a/sgx-jvm/avian/classpath/java/text/DateFormatSymbols.java b/sgx-jvm/avian/classpath/java/text/DateFormatSymbols.java new file mode 100644 index 0000000000..a03becffe8 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/text/DateFormatSymbols.java @@ -0,0 +1,36 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.text; + +public class DateFormatSymbols { + private String[] ampm = new String[] { "AM", "PM" }; + private String[] shortWeekdays = new String[] { "Sun", "Mon", "Tue", + "Wed", "Thu", "Fri", "Sat" }; + private String[] shortMonths = new String[] { "Jan", "Feb", "Mar", "Apr", + "May", "Jun", "Jul", "Aug", + "Sep", "Oct", "Nov", "Dec" }; + + public String[] getAmPmStrings() { + return ampm; + } + + public void setAmPmStrings(String[] v) { + ampm = v; + } + + public String[] getShortWeekdays() { + return shortWeekdays; + } + + public String[] getShortMonths() { + return shortMonths; + } +} diff --git a/sgx-jvm/avian/classpath/java/text/FieldPosition.java b/sgx-jvm/avian/classpath/java/text/FieldPosition.java new file mode 100644 index 0000000000..d9ec4ebd7a --- /dev/null +++ b/sgx-jvm/avian/classpath/java/text/FieldPosition.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.text; + +public class FieldPosition { + public FieldPosition(int field) { } +} diff --git a/sgx-jvm/avian/classpath/java/text/Format.java b/sgx-jvm/avian/classpath/java/text/Format.java new file mode 100644 index 0000000000..b99649540c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/text/Format.java @@ -0,0 +1,20 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.text; + +public abstract class Format { + public final String format(Object o) { + return format(o, new StringBuffer(), new FieldPosition(0)).toString(); + } + + public abstract StringBuffer format(Object o, StringBuffer target, + FieldPosition p); +} diff --git a/sgx-jvm/avian/classpath/java/text/MessageFormat.java b/sgx-jvm/avian/classpath/java/text/MessageFormat.java new file mode 100644 index 0000000000..4ff224546d --- /dev/null +++ b/sgx-jvm/avian/classpath/java/text/MessageFormat.java @@ -0,0 +1,103 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.text; + +import java.util.Locale; + +/** + * A minimalist Java message formatter. + * The string is a sequence of letters, copied verbatim unless the letter + * is "{" or "'". If the letter is a "{", this begins a parameter, which + * is a base-10 number. {0} will be replaced by the first argument to + * format, {1} is the second argument, and so on. + * If the letter is a single tick ("'"), then all characters + * until the mathcing tick are outputted verbatim(this is useful for escaping + * the { character). + *

Examples

+ * + * + * + * + * + * + *
formatArgsResult
There are {0} grapessixThere are six grapes
{2} + {1} = {0}5 2 33 + 2 = 5
{0} and {0} and {0}againagain and again and again
Joe''s age is {0}, not '{0}'30Joe's age is 30, not {0}
+ */ +public class MessageFormat extends Format { + private String pattern; + private final Locale locale; + + public MessageFormat(String pattern, Locale locale) { + this.pattern = pattern; + this.locale = locale; + } + + public MessageFormat(String pattern) { + this(pattern, Locale.getDefault()); + } + + public StringBuffer format(Object args[], StringBuffer target, FieldPosition pos) { + int i=0; + int len=pattern.length(); + + while (i < len) { + char ch = pattern.charAt(i); + if (ch == '{') { + // Param should be a number + int num=0; + while (i < (len-1)) { + i++; + ch = pattern.charAt(i); + if ((ch >= '0') && (ch <= '9')) { + num = num * 10 + (ch - '0'); + } else if (ch == '}') { + target.append((args[num] == null) ? "null" : args[num].toString()); + break; + } else { + throw new IllegalArgumentException("Character within {} isn't digit: " + ch); + } + } + } else if (ch == '\'') { + // Char is a literal string + i++; + ch = pattern.charAt(i); + if (ch == '\'') { + target.append('\''); + } else { + while (ch != '\'') { + target.append(ch); + i++; + ch = pattern.charAt(i); + } + } + } else { + target.append(ch); + } + i++; + } + return target; + } + + public static String format(String message, Object... args) { + return new MessageFormat(message).format(args, new StringBuffer(), new FieldPosition(0)).toString(); + } + + public StringBuffer format(Object args, StringBuffer target, FieldPosition p) { + return format((Object[]) args, target, p); + } + + public void applyPattern(String pattern) { + this.pattern = pattern; + } + + public String toPattern() { + return pattern; + } +} diff --git a/sgx-jvm/avian/classpath/java/text/ParseException.java b/sgx-jvm/avian/classpath/java/text/ParseException.java new file mode 100644 index 0000000000..ba9d74a801 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/text/ParseException.java @@ -0,0 +1,24 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.text; + +public class ParseException extends Exception { + private int errorOffset; + + public ParseException(String message, int errorOffset) { + super(message); + this.errorOffset = errorOffset; + } + + public int getErrorOffset() { + return errorOffset; + } +} diff --git a/sgx-jvm/avian/classpath/java/text/ParsePosition.java b/sgx-jvm/avian/classpath/java/text/ParsePosition.java new file mode 100644 index 0000000000..93a730abcd --- /dev/null +++ b/sgx-jvm/avian/classpath/java/text/ParsePosition.java @@ -0,0 +1,39 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.text; + +public class ParsePosition { + private int index, errorIndex = -1; + + public ParsePosition(int index) { + this.index = index; + } + + public int getErrorIndex() { + return errorIndex; + } + + public int getIndex() { + return index; + } + + public void setErrorIndex(int i) { + errorIndex = i; + } + + public void setIndex(int i) { + index = i; + } + + public String toString() { + return "index: " + index + "(error index: " + errorIndex + ")"; + } +} diff --git a/sgx-jvm/avian/classpath/java/text/SimpleDateFormat.java b/sgx-jvm/avian/classpath/java/text/SimpleDateFormat.java new file mode 100644 index 0000000000..483ac707e9 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/text/SimpleDateFormat.java @@ -0,0 +1,105 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.text; + +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + +public class SimpleDateFormat { + private String pattern; + + public SimpleDateFormat(String pattern) { + this.pattern = pattern; + if (! "yyyy-MM-dd'T'HH:mm:ss".equals(pattern)) { + throw new UnsupportedOperationException("Unsupported pattern: " + pattern); + } + } + + public void setTimeZone(TimeZone tz) { + if(!tz.getDisplayName().equals("GMT")) { + throw new UnsupportedOperationException(); + } + } + + public StringBuffer format(Date date, StringBuffer buffer, FieldPosition position) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + pad(buffer, calendar.get(Calendar.YEAR), 4); + buffer.append('-'); + pad(buffer, calendar.get(Calendar.MONTH) + 1, 2); + buffer.append('-'); + pad(buffer, calendar.get(Calendar.DAY_OF_MONTH), 2); + buffer.append("T"); + pad(buffer, calendar.get(Calendar.HOUR_OF_DAY), 2); + buffer.append(':'); + pad(buffer, calendar.get(Calendar.MINUTE), 2); + buffer.append(':'); + pad(buffer, calendar.get(Calendar.SECOND), 2); + return buffer; + } + + public Date parse(String text) { + return parse(text, new ParsePosition(0)); + } + + public Date parse(String text, ParsePosition position) { + int index = position.getIndex(); + try { + Calendar calendar = Calendar.getInstance(); + index = parseField(text, index, 4, calendar, Calendar.YEAR, 0); + index = expectPrefix(text, index, "-"); + index = parseField(text, index, 2, calendar, Calendar.MONTH, -1); + index = expectPrefix(text, index, "-"); + index = parseField(text, index, 2, calendar, Calendar.DAY_OF_MONTH, 0); + index = expectPrefix(text, index, "T"); + index = parseField(text, index, 2, calendar, Calendar.HOUR_OF_DAY, 0); + index = expectPrefix(text, index, ":"); + index = parseField(text, index, 2, calendar, Calendar.MINUTE, 0); + index = expectPrefix(text, index, ":"); + index = parseField(text, index, 2, calendar, Calendar.SECOND, 0); + position.setIndex(index); + return calendar.getTime(); + } catch (ParseException e) { + position.setErrorIndex(index); + return null; + } + } + + private static void pad(StringBuffer buffer, int value, int digits) { + int i = value == 0 ? 1 : value; + while (i > 0) { + i /= 10; + --digits; + } + while (digits-- > 0) { + buffer.append('0'); + } + buffer.append(value); + } + + private static int parseField(String text, int offset, int length, Calendar calendar, int field, int adjustment) throws ParseException { + if (text.length() < offset + length) throw new ParseException("Short date: " + text, offset); + try { + int value = Integer.parseInt(text.substring(offset, offset + length), 10); + calendar.set(field, value + adjustment); + } catch (NumberFormatException e) { + throw new ParseException("Not a number: " + text, offset); + } + return offset + length; + } + + private static int expectPrefix(String text, int offset, String prefix) throws ParseException { + if (text.length() <= offset) throw new ParseException("Short date: " + text, offset); + if (! text.substring(offset).startsWith(prefix)) throw new ParseException("Parse error: " + text, offset); + return offset + prefix.length(); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/AbstractCollection.java b/sgx-jvm/avian/classpath/java/util/AbstractCollection.java new file mode 100644 index 0000000000..d6b6dda81f --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/AbstractCollection.java @@ -0,0 +1,108 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +/** + * @author zsombor + * + */ +public abstract class AbstractCollection implements Collection { + public boolean add(T element) { + throw new UnsupportedOperationException("adding to " + + this.getClass().getName()); + } + + public boolean addAll(Collection collection) { + boolean result = false; + for (T obj : collection) { + result |= add(obj); + } + return result; + } + + public void clear() { + throw new UnsupportedOperationException("clear() in " + + this.getClass().getName()); + } + + public boolean contains(Object element) { + if (element != null) { + for (Iterator iter = iterator(); iter.hasNext();) { + if (element.equals(iter.next())) { + return true; + } + } + } else { + for (Iterator iter = iterator(); iter.hasNext();) { + if (iter.next()==null) { + return true; + } + } + + } + return false; + } + + public boolean containsAll(Collection c) { + if (c == null) { + throw new NullPointerException("Collection is null"); + } + + Iterator it = c.iterator(); + while (it.hasNext()) { + if (! contains(it.next())) { + return false; + } + } + + return true; + } + + public boolean isEmpty() { + return size() == 0; + } + + public boolean remove(Object element) { + throw new UnsupportedOperationException("remove(T) in " + + this.getClass().getName()); + } + + public boolean removeAll(Collection c) { + if (c == null) { + throw new NullPointerException("Collection is null"); + } + + boolean changed = false; + + Iterator it = c.iterator(); + while (it.hasNext()) { + changed = remove(it.next()) || changed; + } + + return changed; + } + + public abstract int size(); + + public Object[] toArray() { + return toArray(new Object[size()]); + } + + public S[] toArray(S[] array) { + return avian.Data.toArray(this, array); + } + + public abstract Iterator iterator(); + + public String toString() { + return avian.Data.toString(this); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/AbstractDeque.java b/sgx-jvm/avian/classpath/java/util/AbstractDeque.java new file mode 100644 index 0000000000..f88858bb2e --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/AbstractDeque.java @@ -0,0 +1,90 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public abstract class AbstractDeque extends AbstractQueue + implements Deque { + + @Override + public void push(T e) { + addFirst(e); + } + + @Override + public void addLast(T element) { + if (! offerLast(element)) { + throw new IllegalStateException(); + } + } + + @Override + public void addFirst(T element) { + if (! offerFirst(element)) { + throw new IllegalStateException(); + } + } + + @Override + public boolean offer(T element) { + return offerLast(element); + } + + @Override + public T poll() { + return pollFirst(); + } + + @Override + public T pop() { + return removeFirst(); + } + + @Override + public T removeFirst() { + return remove(); + } + + @Override + public boolean remove(Object element) { + return removeFirstOccurrence(element); + } + + @Override + public T removeLast() { + T result = pollLast(); + if (result == null) { + throw new NoSuchElementException(); + } else { + return result; + } + } + + @Override + public T getFirst() { + return element(); + } + + @Override + public T getLast() { + T result = peekLast(); + + if (result == null) { + throw new NoSuchElementException(); + } + + return result; + } + + @Override + public T peek() { + return peekFirst(); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/AbstractList.java b/sgx-jvm/avian/classpath/java/util/AbstractList.java new file mode 100644 index 0000000000..5ad69eaabc --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/AbstractList.java @@ -0,0 +1,67 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public abstract class AbstractList extends AbstractCollection + implements List +{ + protected int modCount; + + public boolean add(T o) { + add(size(), o); + return true; + } + + public boolean addAll(Collection c) { + return addAll(size(), c); + } + + public boolean addAll(int startIndex, Collection c) { + if (c == null) { + throw new NullPointerException("Collection is null"); + } + + int index = startIndex; + boolean changed = false; + + Iterator it = c.iterator(); + while (it.hasNext()) { + add(index++, it.next()); + changed = true; + } + + return changed; + } + + public Iterator iterator() { + return listIterator(); + } + + public ListIterator listIterator() { + return new Collections.ArrayListIterator(this); + } + + public int indexOf(Object o) { + int i = 0; + for (T v: this) { + if (o == null) { + if (v == null) { + return i; + } + } else if (o.equals(v)) { + return i; + } + + ++ i; + } + return -1; + } +} diff --git a/sgx-jvm/avian/classpath/java/util/AbstractMap.java b/sgx-jvm/avian/classpath/java/util/AbstractMap.java new file mode 100644 index 0000000000..88cd22636d --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/AbstractMap.java @@ -0,0 +1,13 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public abstract class AbstractMap extends Object implements Map { } diff --git a/sgx-jvm/avian/classpath/java/util/AbstractQueue.java b/sgx-jvm/avian/classpath/java/util/AbstractQueue.java new file mode 100644 index 0000000000..e407672c76 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/AbstractQueue.java @@ -0,0 +1,67 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public abstract class AbstractQueue extends AbstractCollection implements Queue { + + protected AbstractQueue() { + super(); + } + + @Override + public boolean add(T element) { + if (offer(element)) { + return true; + } else { + throw new IllegalStateException(); + } + } + + @Override + public boolean addAll(Collection collection) { + if (collection == null) { + throw new NullPointerException(); + } + + for (T element : collection) { + add(element); + } + + return true; + } + + @Override + public void clear() { + while (size() > 0) { + poll(); + } + } + + @Override + public T element() { + T result = peek(); + if (result == null) { + throw new NoSuchElementException(); + } else { + return result; + } + } + + @Override + public T remove() { + T result = poll(); + if (result == null) { + throw new NoSuchElementException(); + } else { + return result; + } + } +} diff --git a/sgx-jvm/avian/classpath/java/util/AbstractSequentialList.java b/sgx-jvm/avian/classpath/java/util/AbstractSequentialList.java new file mode 100644 index 0000000000..3f359ec502 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/AbstractSequentialList.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public abstract class AbstractSequentialList extends AbstractList + implements List +{ } diff --git a/sgx-jvm/avian/classpath/java/util/AbstractSet.java b/sgx-jvm/avian/classpath/java/util/AbstractSet.java new file mode 100644 index 0000000000..29b3a95a82 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/AbstractSet.java @@ -0,0 +1,14 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public abstract class AbstractSet extends AbstractCollection implements Set { +} diff --git a/sgx-jvm/avian/classpath/java/util/ArrayDeque.java b/sgx-jvm/avian/classpath/java/util/ArrayDeque.java new file mode 100644 index 0000000000..8801806064 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/ArrayDeque.java @@ -0,0 +1,364 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public class ArrayDeque extends AbstractDeque + implements Deque { + private Object[] dataArray; + // both indexes are inclusive, except when size == 0 + private int startIndex; + private int endIndex; + private int size; + private int modCount; + + public ArrayDeque() { + this(16); + } + + public ArrayDeque(int intialSize) { + dataArray = new Object[intialSize]; + modCount = 0; + clear(); + } + + public ArrayDeque(Collection c) { + this(c.size()); + + addAll(c); + } + + private void copyInto(Object[] array) { + if (startIndex <= endIndex) { + // only one copy needed + System.arraycopy(dataArray, startIndex, array, 0, size); + } else { + int firstCopyCount = dataArray.length - startIndex; + System.arraycopy(dataArray, startIndex, array, 0, firstCopyCount); + System.arraycopy(dataArray, 0, array, firstCopyCount, endIndex + 1); + } + } + + private void ensureCapacity(int newSize) { + if (dataArray.length < newSize) { + Object[] newArray = new Object[dataArray.length * 2]; + copyInto(newArray); + + dataArray = newArray; + startIndex = 0; + endIndex = size - 1; + } + } + + @Override + public boolean offerFirst(T e) { + ensureCapacity(size() + 1); + modCount++; + + if (size > 0) { + // we don't need to move the head index for the first one + startIndex--; + if (startIndex < 0) { // wrapping to the end of the array + startIndex = dataArray.length - 1; + } + } + size++; + dataArray[startIndex] = e; + + return true; + } + + @Override + public boolean offerLast(T e) { + ensureCapacity(size() + 1); + modCount++; + + if (size > 0) { + // we don't need to move the tail index for the first one + endIndex = (endIndex + 1) % dataArray.length; + } + size++; + dataArray[endIndex] = e; + + return true; + } + + @Override + public T pollFirst() { + modCount++; + + if (size == 0) { + return null; + } + + @SuppressWarnings("unchecked") + T result = (T)dataArray[startIndex]; + size--; + if (size == 0) { + startIndex = endIndex = 0; + } else { + startIndex = (startIndex + 1) % dataArray.length; + } + + return result; + } + + @Override + public T pollLast() { + modCount++; + + if (size == 0) { + return null; + } + + @SuppressWarnings("unchecked") + T result = (T)dataArray[endIndex]; + size--; + if (size == 0) { + startIndex = endIndex = 0; + } else { + endIndex--; + if (endIndex < 0) { + endIndex = dataArray.length - 1; + } + } + + return result; + } + + @Override + public T peekFirst() { + if (size == 0) { + return null; + } else { + @SuppressWarnings("unchecked") + T result = (T)dataArray[startIndex]; + return result; + } + } + + @Override + public T peekLast() { + if (size == 0) { + return null; + } else { + @SuppressWarnings("unchecked") + T result = (T)dataArray[endIndex]; + return result; + } + } + + @Override + public boolean addAll(Collection c) { + if (c == null || c.isEmpty()) { + return false; + } + + ensureCapacity(size() + c.size()); + + Iterator it = c.iterator(); + while (it.hasNext()) { + add(it.next()); + } + + return true; + } + + @Override + public boolean removeAll(Collection c) { + boolean removed = false; + Iterator it = c.iterator(); + while (it.hasNext()) { + removed = remove(it.next()) || removed; + } + + return removed; + } + + private boolean remove(Object o, boolean first) { + modCount++; + + Iterator it; + if (first) { + it = iterator(); + } else { + it = descendingIterator(); + } + while (it.hasNext()) { + T next = it.next(); + if (next == null) { + if (o == null) { + it.remove(); + return true; + } + } else if (next.equals(o)) { + it.remove(); + return true; + } + } + + return false; + } + + @Override + public boolean removeFirstOccurrence(Object o) { + return remove(o, true); + } + + @Override + public boolean removeLastOccurrence(Object o) { + return remove(o, false); + } + + @Override + public void clear() { + size = 0; + startIndex = endIndex = 0; + modCount++; + } + + @Override + public int size() { + return size; + } + + @Override + public boolean contains(Object element) { + Iterator it = iterator(); + while (it.hasNext()) { + T next = it.next(); + if (next == null) { + if (element == null) { + return true; + } + } else if (next.equals(element)) { + return true; + } + } + + return false; + } + + @Override + public boolean containsAll(Collection c) { + Iterator it = c.iterator(); + while (it.hasNext()) { + if (! contains(it.next())) { + return false; + } + } + + return true; + } + + @Override + public Object[] toArray() { + Object[] result = new Object[size]; + copyInto(result); + + return result; + } + + @Override + public S[] toArray(S[] array) { + return avian.Data.toArray(this, array); + } + + public Iterator iterator() { + return new GenericIterator() { + @Override + protected int getArrayIndex() { + int result = (currentIndex + startIndex) % dataArray.length; + return result; + } + }; + } + + public Iterator descendingIterator() { + return new GenericIterator() { + @Override + protected int getArrayIndex() { + int result = (endIndex - currentIndex) % dataArray.length; + return result; + } + }; + } + + private abstract class GenericIterator implements Iterator { + protected int expectedModCount = modCount; + protected int currentIndex = 0; + + protected abstract int getArrayIndex(); + + @Override + public T next() { + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } else if (currentIndex == size) { + throw new NoSuchElementException(); + } + + @SuppressWarnings("unchecked") + T result = (T)dataArray[getArrayIndex()]; + currentIndex++; + + return result; + } + + @Override + public boolean hasNext() { + return currentIndex < size; + } + + @Override + public void remove() { + currentIndex--; + + int removalIndex = getArrayIndex(); + if (removalIndex == startIndex) { + // avoid array copy + pollFirst(); + expectedModCount = modCount; + } else if (removalIndex == endIndex) { + // avoid array copy + pollLast(); + expectedModCount = modCount; + } else { // array must be copied + Object[] newArray = new Object[dataArray.length]; + if (startIndex <= endIndex) { + int firstCopyCount = removalIndex - startIndex; + System.arraycopy(dataArray, startIndex, + newArray, 0, firstCopyCount); + System.arraycopy(dataArray, removalIndex + 1, + newArray, firstCopyCount, size - firstCopyCount); + } else if (removalIndex > startIndex) { + int firstCopyCount = removalIndex - startIndex; + System.arraycopy(dataArray, startIndex, + newArray, 0, firstCopyCount); + System.arraycopy(dataArray, startIndex + firstCopyCount + 1, + newArray, firstCopyCount, dataArray.length - removalIndex - 1); + System.arraycopy(dataArray, 0, newArray, size - removalIndex, endIndex + 1); + } else { + int firstCopyCount = dataArray.length - startIndex; + System.arraycopy(dataArray, startIndex, + newArray, 0, firstCopyCount); + System.arraycopy(dataArray, 0, + newArray, firstCopyCount, removalIndex); + System.arraycopy(dataArray, removalIndex + 1, + newArray, firstCopyCount + removalIndex, endIndex - removalIndex); + } + + dataArray = newArray; + startIndex = 0; + endIndex = --size - 1; + } + } + } +} diff --git a/sgx-jvm/avian/classpath/java/util/ArrayList.java b/sgx-jvm/avian/classpath/java/util/ArrayList.java new file mode 100644 index 0000000000..325fa1591f --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/ArrayList.java @@ -0,0 +1,209 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +public class ArrayList extends AbstractList implements java.io.Serializable, RandomAccess { + private static final int MinimumCapacity = 16; + + private Object[] array; + private int size; + + public ArrayList(int capacity) { + resize(capacity); + } + + public ArrayList() { + this(0); + } + + public ArrayList(Collection source) { + this(source.size()); + addAll(source); + } + + private void grow(int newSize) { + if (array == null || newSize > array.length) { + resize(Math.max(newSize, array == null + ? MinimumCapacity : array.length * 2)); + } + } + + private void shrink(int newSize) { + if (array.length / 2 >= MinimumCapacity && newSize <= array.length / 3) { + resize(array.length / 2); + } + } + + private void resize(int capacity) { + Object[] newArray = null; + if (capacity != 0) { + if (array != null && array.length == capacity) { + return; + } + + newArray = new Object[capacity]; + if (array != null) { + System.arraycopy(array, 0, newArray, 0, size); + } + } + array = newArray; + } + + private static boolean equal(Object a, Object b) { + return (a == null && b == null) || (a != null && a.equals(b)); + } + + public int size() { + return size; + } + + public void ensureCapacity(int capacity) { + grow(capacity); + } + + public boolean contains(Object element) { + for (int i = 0; i < size; ++i) { + if (equal(element, array[i])) { + return true; + } + } + return false; + } + + public void add(int index, T element) { + int newSize = Math.max(size+1, index+1); + grow(newSize); + size = newSize; + System.arraycopy(array, index, array, index+1, size-index-1); + array[index] = element; + } + + public boolean add(T element) { + add(size, element); + return true; + } + + public boolean addAll(Collection collection) { + for (T t: collection) add(t); + return true; + } + + public int indexOf(Object element) { + for (int i = 0; i < size; ++i) { + if (equal(element, array[i])) { + return i; + } + } + return -1; + } + + public int lastIndexOf(Object element) { + for (int i = size - 1; i >= 0; --i) { + if (equal(element, array[i])) { + return i; + } + } + return -1; + } + + public T get(int index) { + if (index >= 0 && index < size) { + return (T) array[index]; + } else { + throw new IndexOutOfBoundsException(index + " not in [0, " + size + ")"); + } + } + + public T set(int index, T element) { + if (index >= 0 && index < size) { + Object oldValue = array[index]; + array[index] = element; + return (T) oldValue; + } else { + throw new IndexOutOfBoundsException(index + " not in [0, " + size + ")"); + } + } + + public T remove(int index) { + T v = get(index); + + int newSize = size - 1; + + if (index == newSize) { + array[index] = null; + } else { + System.arraycopy(array, index + 1, array, index, newSize - index); + } + + shrink(newSize); + size = newSize; + + return v; + } + + public boolean remove(Object element) { + for (int i = 0; i < size; ++i) { + if (equal(element, array[i])) { + remove(i); + return true; + } + } + return false; + } + + public boolean isEmpty() { + return size() == 0; + } + + public void clear() { + array = null; + size = 0; + } + + public Iterator iterator() { + return listIterator(); + } + + public ListIterator listIterator(int index) { + return new Collections.ArrayListIterator(this, index); + } + + public ListIterator listIterator() { + return listIterator(0); + } + + public String toString() { + return avian.Data.toString(this); + } + + private void writeObject(ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + out.writeInt(array.length); + for (T o : this) { + out.writeObject(o); + } + } + + private void readObject(ObjectInputStream in) + throws ClassNotFoundException, IOException + { + in.defaultReadObject(); + int capacity = in.readInt(); + grow(capacity); + for (int i = 0; i < size; i++) { + array[i] = in.readObject(); + } + } +} diff --git a/sgx-jvm/avian/classpath/java/util/Arrays.java b/sgx-jvm/avian/classpath/java/util/Arrays.java new file mode 100644 index 0000000000..0c2de7dba2 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/Arrays.java @@ -0,0 +1,689 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +import java.lang.reflect.Array; + +public class Arrays { + private Arrays() { } + + public static String toString(Object[] a) { + return asList(a).toString(); + } + + public static String toString(boolean[] a) { + if (a == null) { + return "null"; + } else { + StringBuilder sb = new StringBuilder(); + sb.append("["); + for (int i = 0; i < a.length; ++i) { + sb.append(String.valueOf(a[i])); + if (i + 1 != a.length) { + sb.append(", "); + } + } + sb.append("]"); + return sb.toString(); + } + } + + public static String toString(byte[] a) { + if (a == null) { + return "null"; + } else { + StringBuilder sb = new StringBuilder(); + sb.append("["); + for (int i = 0; i < a.length; ++i) { + sb.append(String.valueOf(a[i])); + if (i + 1 != a.length) { + sb.append(", "); + } + } + sb.append("]"); + return sb.toString(); + } + } + + public static String toString(short[] a) { + if (a == null) { + return "null"; + } else { + StringBuilder sb = new StringBuilder(); + sb.append("["); + for (int i = 0; i < a.length; ++i) { + sb.append(String.valueOf(a[i])); + if (i + 1 != a.length) { + sb.append(", "); + } + } + sb.append("]"); + return sb.toString(); + } + } + + public static String toString(int[] a) { + if (a == null) { + return "null"; + } else { + StringBuilder sb = new StringBuilder(); + sb.append("["); + for (int i = 0; i < a.length; ++i) { + sb.append(String.valueOf(a[i])); + if (i + 1 != a.length) { + sb.append(", "); + } + } + sb.append("]"); + return sb.toString(); + } + } + + public static String toString(long[] a) { + if (a == null) { + return "null"; + } else { + StringBuilder sb = new StringBuilder(); + sb.append("["); + for (int i = 0; i < a.length; ++i) { + sb.append(String.valueOf(a[i])); + if (i + 1 != a.length) { + sb.append(", "); + } + } + sb.append("]"); + return sb.toString(); + } + } + + public static String toString(float[] a) { + if (a == null) { + return "null"; + } else { + StringBuilder sb = new StringBuilder(); + sb.append("["); + for (int i = 0; i < a.length; ++i) { + sb.append(String.valueOf(a[i])); + if (i + 1 != a.length) { + sb.append(", "); + } + } + sb.append("]"); + return sb.toString(); + } + } + + public static String toString(double[] a) { + if (a == null) { + return "null"; + } else { + StringBuilder sb = new StringBuilder(); + sb.append("["); + for (int i = 0; i < a.length; ++i) { + sb.append(String.valueOf(a[i])); + if (i + 1 != a.length) { + sb.append(", "); + } + } + sb.append("]"); + return sb.toString(); + } + } + + private static boolean equal(Object a, Object b) { + return (a == null && b == null) || (a != null && a.equals(b)); + } + + public static void sort(Object[] array) { + sort(array, new Comparator() { + @Override + public int compare(Object a, Object b) { + return ((Comparable) a).compareTo(b); + } + }); + } + + private final static int SORT_SIZE_THRESHOLD = 16; + + public static void sort(T[] array, Comparator comparator) { + introSort(array, comparator, 0, array.length, array.length); + insertionSort(array, comparator); + } + + private static void introSort(T[] array, + Comparator comparator, int begin, int end, int limit) + { + while (end - begin > SORT_SIZE_THRESHOLD) { + if (limit == 0) { + heapSort(array, comparator, begin, end); + return; + } + limit >>= 1; + + // median of three + T a = array[begin]; + T b = array[begin + (end - begin) / 2 + 1]; + T c = array[end - 1]; + T median; + if (comparator.compare(a, b) < 0) { + median = comparator.compare(b, c) < 0 ? + b : (comparator.compare(a, c) < 0 ? c : a); + } else { + median = comparator.compare(b, c) > 0 ? + b : (comparator.compare(a, c) > 0 ? c : a); + } + + // partition + int pivot, i = begin, j = end; + for (;;) { + while (comparator.compare(array[i], median) < 0) { + ++i; + } + --j; + while (comparator.compare(median, array[j]) < 0) { + --j; + } + if (i >= j) { + pivot = i; + break; + } + T swap = array[i]; + array[i] = array[j]; + array[j] = swap; + ++i; + } + + introSort(array, comparator, pivot, end, limit); + end = pivot; + } + } + + private static void heapSort(T[] array, Comparator comparator, + int begin, int end) + { + int count = end - begin; + for (int i = count / 2 - 1; i >= 0; --i) { + siftDown(array, comparator, i, count, begin); + } + for (int i = count - 1; i > 0; --i) { + // swap begin and begin + i + T swap = array[begin + i]; + array[begin + i] = array[begin]; + array[begin] = swap; + + siftDown(array, comparator, 0, i, begin); + } + } + + private static void siftDown(T[] array, Comparator comparator, + int i, int count, int offset) + { + T value = array[offset + i]; + while (i < count / 2) { + int child = 2 * i + 1; + if (child + 1 < count && + comparator.compare(array[child], array[child + 1]) < 0) { + ++child; + } + if (comparator.compare(value, array[child]) >= 0) { + break; + } + array[offset + i] = array[offset + child]; + i = child; + } + array[offset + i] = value; + } + + private static void insertionSort(T[] array, + Comparator comparator) + { + for (int j = 1; j < array.length; ++j) { + T t = array[j]; + int i = j - 1; + while (i >= 0 && comparator.compare(array[i], t) > 0) { + array[i + 1] = array[i]; + i = i - 1; + } + array[i + 1] = t; + } + } + + public static int hashCode(Object[] array) { + if(array == null) { + return 9023; + } + + int hc = 823347; + for(Object o : array) { + hc += o != null ? o.hashCode() : 54267; + hc *= 3; + } + return hc; + } + + public static boolean equals(Object[] a, Object[] b) { + if(a == b) { + return true; + } + if(a == null || b == null) { + return false; + } + if(a.length != b.length) { + return false; + } + for(int i = 0; i < a.length; i++) { + if(!equal(a[i], b[i])) { + return false; + } + } + return true; + } + + public static boolean equals(byte[] a, byte[] b) { + if(a == b) { + return true; + } + if(a == null || b == null) { + return false; + } + if(a.length != b.length) { + return false; + } + for(int i = 0; i < a.length; i++) { + if(a[i] != b[i]) { + return false; + } + } + return true; + } + + public static boolean equals(int[] a, int[] b) { + if(a == b) { + return true; + } + if(a == null || b == null) { + return false; + } + if(a.length != b.length) { + return false; + } + for(int i = 0; i < a.length; i++) { + if(a[i] != b[i]) { + return false; + } + } + return true; + } + + public static boolean equals(long[] a, long[] b) { + if(a == b) { + return true; + } + if(a == null || b == null) { + return false; + } + if(a.length != b.length) { + return false; + } + for(int i = 0; i < a.length; i++) { + if(a[i] != b[i]) { + return false; + } + } + return true; + } + + public static boolean equals(short[] a, short[] b) { + if(a == b) { + return true; + } + if(a == null || b == null) { + return false; + } + if(a.length != b.length) { + return false; + } + for(int i = 0; i < a.length; i++) { + if(a[i] != b[i]) { + return false; + } + } + return true; + } + + public static boolean equals(char[] a, char[] b) { + if(a == b) { + return true; + } + if(a == null || b == null) { + return false; + } + if(a.length != b.length) { + return false; + } + for(int i = 0; i < a.length; i++) { + if(a[i] != b[i]) { + return false; + } + } + return true; + } + + public static boolean equals(float[] a, float[] b) { + if(a == b) { + return true; + } + if(a == null || b == null) { + return false; + } + if(a.length != b.length) { + return false; + } + for(int i = 0; i < a.length; i++) { + if(a[i] != b[i]) { + return false; + } + } + return true; + } + + public static boolean equals(double[] a, double[] b) { + if(a == b) { + return true; + } + if(a == null || b == null) { + return false; + } + if(a.length != b.length) { + return false; + } + for(int i = 0; i < a.length; i++) { + if(a[i] != b[i]) { + return false; + } + } + return true; + } + + public static boolean deepEquals(Object[] a, Object[] b) { + if(a == b) { + return true; + } + if(a == null || b == null) { + return false; + } + if(a.length != b.length) { + return false; + } + for(int i = 0; i < a.length; i++) { + if(!Objects.deepEquals(a[i], b[i])) { + return false; + } + } + return true; + } + + public static List asList(final T ... array) { + return new AbstractList() { + @Override + public int size() { + return array.length; + } + + @Override + public void add(int index, T element) { + throw new UnsupportedOperationException(); + } + + @Override + public int indexOf(Object element) { + for (int i = 0; i < array.length; ++i) { + if (equal(element, array[i])) { + return i; + } + } + return -1; + } + + @Override + public int lastIndexOf(Object element) { + for (int i = array.length - 1; i >= 0; --i) { + if (equal(element, array[i])) { + return i; + } + } + return -1; + } + + @Override + public T get(int index) { + return array[index]; + } + + @Override + public T set(int index, T value) { + throw new UnsupportedOperationException(); + } + + @Override + public T remove(int index) { + throw new UnsupportedOperationException(); + } + + @Override + public ListIterator listIterator(int index) { + return new Collections.ArrayListIterator(this, index); + } + }; + } + + private static void checkRange(int len, int start, int stop) { + if (start < 0) { + throw new ArrayIndexOutOfBoundsException(start); + } + if (stop > len) { + throw new ArrayIndexOutOfBoundsException(stop); + } + if (start > stop) { + throw new IllegalArgumentException("start(" + start + ") > stop(" + stop + ")"); + } + } + + public static void fill(int[] array, int value) { + for (int i=0;i void fill(T[] array, T value) { + for (int i=0;i void fill(T[] array, int start, int stop, T value) { + checkRange(array.length, start, stop); + for (int i=start;i newLength ? newLength : array.length; + System.arraycopy(array, 0, result, 0, length); + return result; + } + + public static byte[] copyOf(byte[] array, int newLength) { + byte[] result = new byte[newLength]; + int length = array.length > newLength ? newLength : array.length; + System.arraycopy(array, 0, result, 0, length); + return result; + } + + public static char[] copyOf(char[] array, int newLength) { + char[] result = new char[newLength]; + int length = array.length > newLength ? newLength : array.length; + System.arraycopy(array, 0, result, 0, length); + return result; + } + + public static double[] copyOf(double[] array, int newLength) { + double[] result = new double[newLength]; + int length = array.length > newLength ? newLength : array.length; + System.arraycopy(array, 0, result, 0, length); + return result; + } + + public static float[] copyOf(float[] array, int newLength) { + float[] result = new float[newLength]; + int length = array.length > newLength ? newLength : array.length; + System.arraycopy(array, 0, result, 0, length); + return result; + } + + public static int[] copyOf(int[] array, int newLength) { + int[] result = new int[newLength]; + int length = array.length > newLength ? newLength : array.length; + System.arraycopy(array, 0, result, 0, length); + return result; + } + + public static long[] copyOf(long[] array, int newLength) { + long[] result = new long[newLength]; + int length = array.length > newLength ? newLength : array.length; + System.arraycopy(array, 0, result, 0, length); + return result; + } + + public static short[] copyOf(short[] array, int newLength) { + short[] result = new short[newLength]; + int length = array.length > newLength ? newLength : array.length; + System.arraycopy(array, 0, result, 0, length); + return result; + } + + public static T[] copyOf(T[] array, int newLength) { + Class clazz = array.getClass().getComponentType(); + T[] result = (T[])Array.newInstance(clazz, newLength); + int length = array.length > newLength ? newLength : array.length; + System.arraycopy(array, 0, result, 0, length); + return result; + } + + public static T[] copyOf(U[] array, int newLength, + Class newType) + { + T[] result = (T[])Array.newInstance(newType.getComponentType(), newLength); + int length = array.length > newLength ? newLength : array.length; + System.arraycopy(array, 0, result, 0, length); + return result; + } +} diff --git a/sgx-jvm/avian/classpath/java/util/BitSet.java b/sgx-jvm/avian/classpath/java/util/BitSet.java new file mode 100644 index 0000000000..c76016a5f0 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/BitSet.java @@ -0,0 +1,267 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +import java.io.Serializable; + +/** + * @author zsombor + * + */ +public class BitSet implements Serializable, Cloneable { + + final static int BITS_PER_LONG = 64; + final static int BITS_PER_LONG_SHIFT = 6; + final static long MASK = 0xFFFFFFFFFFFFFFFFL; + + private long[] bits; + + private static int longPosition(int index) { + return index >> BITS_PER_LONG_SHIFT; + } + + private static long bitPosition(int index) { + return 1L << (index % BITS_PER_LONG); + } + + private static long getTrueMask(int fromIndex, int toIndex) { + int currentRange = toIndex - fromIndex; + return (MASK >>> (BITS_PER_LONG - currentRange)) << (fromIndex % BITS_PER_LONG); + } + + public BitSet(int bitLength) { + if (bitLength % BITS_PER_LONG == 0) { + enlarge(longPosition(bitLength)); + } else { + enlarge(longPosition(bitLength) + 1); + } + } + + public BitSet() { + enlarge(1); + } + + public void and(BitSet otherBits) { + int min = Math.min(bits.length, otherBits.bits.length); + for (int i = 0; i < min; i++) { + bits[i] &= otherBits.bits[i]; + } + for (int i = min; i < bits.length; i++) { + bits[i] = 0; + } + } + + public void andNot(BitSet otherBits) { + int max = Math.max(bits.length, otherBits.bits.length); + enlarge(max); + int min = Math.min(bits.length, otherBits.bits.length); + for (int i = 0; i < min; i++) { + bits[i] &= ~otherBits.bits[i]; + } + } + + public void or(BitSet otherBits) { + int max = Math.max(bits.length, otherBits.bits.length); + enlarge(max); + int min = Math.min(bits.length, otherBits.bits.length); + for (int i = 0; i < min; i++) { + bits[i] |= otherBits.bits[i]; + } + } + + public void xor(BitSet otherBits) { + int max = Math.max(bits.length, otherBits.bits.length); + enlarge(max); + int min = Math.min(bits.length, otherBits.bits.length); + for (int i = 0; i < min; i++) { + bits[i] ^= otherBits.bits[i]; + } + } + + private void enlarge(int newPartition) { + if (bits == null || bits.length < (newPartition + 1)) { + long[] newBits = new long[newPartition + 1]; + if (bits != null) { + System.arraycopy(bits, 0, newBits, 0, bits.length); + } + bits = newBits; + } + } + + public boolean get(int index) { + int pos = longPosition(index); + if (pos < bits.length) { + return (bits[pos] & bitPosition(index)) != 0; + } + return false; + } + + public void flip(int index) { + flip(index, index+1); + } + + public void flip(int fromIndex, int toIndex) { + if (fromIndex > toIndex || fromIndex < 0 || toIndex < 0) { + throw new IndexOutOfBoundsException(); + } else if (fromIndex != toIndex) { + MaskInfoIterator iter = new MaskInfoIterator(fromIndex, toIndex); + enlarge(iter.getLastPartition()); + while (iter.hasNext()) { + MaskInfo info = iter.next(); + bits[info.partitionIndex] ^= info.mask; + } + } + } + + public void set(int index) { + int pos = longPosition(index); + enlarge(pos); + bits[pos] |= bitPosition(index); + } + + public void set(int start, int end) { + MaskInfoIterator iter = new MaskInfoIterator(start, end); + enlarge(iter.getLastPartition()); + while (iter.hasNext()) { + MaskInfo info = iter.next(); + bits[info.partitionIndex] |= info.mask; + } + } + + public void clear(int index) { + int pos = longPosition(index); + if (pos < bits.length) { + bits[pos] &= (MASK ^ bitPosition(index)); + } + } + + public void clear(int start, int end) { + MaskInfoIterator iter = new MaskInfoIterator(start, end); + while (iter.hasNext()) { + MaskInfo info = iter.next(); + bits[info.partitionIndex] &= (MASK ^ info.mask); + } + } + + public boolean isEmpty() { + for (int i = 0; i < bits.length; i++) { + if (bits[i] != 0) { + return false; + } + } + return true; + } + + public boolean intersects(BitSet otherBits) { + int max = Math.max(bits.length, otherBits.bits.length); + for (int i = 0; i < max; i++) { + if ((bits[i] & otherBits.bits[i]) != 0) { + return true; + } + } + return false; + } + + public int length() { + return bits.length << BITS_PER_LONG_SHIFT; + } + + public int nextSetBit(int fromIndex) { + return nextBit(fromIndex, false); + } + + private int nextBit(int fromIndex, boolean bitClear) { + int pos = longPosition(fromIndex); + if (pos >= bits.length) { + return -1; + } + int current = fromIndex; + do { + long currValue = bits[pos]; + if (currValue == 0) { + pos++; + current = pos << BITS_PER_LONG_SHIFT; + } else { + do { + long bitPos = bitPosition(current); + if (((currValue & bitPos) != 0) ^ bitClear) { + return current; + } else { + current++; + } + } while (current % BITS_PER_LONG != 0); + } + pos++; + } while (pos < bits.length); + + return -1; + } + + public int nextClearBit(int fromIndex) { + return nextBit(fromIndex, true); + } + + public int cardinality() { + int numSetBits = 0; + for (int i = nextSetBit(0); i >= 0; i = nextSetBit(i+1)) { + ++numSetBits; + } + + return numSetBits; + } + + private static class MaskInfoIterator implements Iterator { + private int basePartition; + private int numPartitionsToTraverse; + private int currentPartitionOffset; + private int toIndex; + private int currentFirstIndex; + + public MaskInfoIterator(int fromIndex, int toIndex) { + this.basePartition = longPosition(fromIndex); + this.numPartitionsToTraverse = longPosition(toIndex - 1) - basePartition + 1; + this.currentPartitionOffset = 0; + this.toIndex = toIndex; + this.currentFirstIndex = fromIndex; + } + + public MaskInfo next() { + int currentToIndex = Math.min(toIndex, (basePartition + currentPartitionOffset + 1) * BITS_PER_LONG); + long mask = getTrueMask(currentFirstIndex, currentToIndex); + MaskInfo info = new MaskInfo(mask, basePartition + currentPartitionOffset); + currentFirstIndex = currentToIndex; + currentPartitionOffset++; + return info; + } + + public boolean hasNext() { + return currentPartitionOffset < numPartitionsToTraverse; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + public int getLastPartition() { + return basePartition + numPartitionsToTraverse - 1; + } + } + + private static class MaskInfo { + public long mask; + public int partitionIndex; + + public MaskInfo(long mask, int partitionIndex) { + this.mask = mask; + this.partitionIndex = partitionIndex; + } + } +} diff --git a/sgx-jvm/avian/classpath/java/util/Calendar.java b/sgx-jvm/avian/classpath/java/util/Calendar.java new file mode 100644 index 0000000000..448f0f1bef --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/Calendar.java @@ -0,0 +1,188 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public abstract class Calendar { + public static final int AM = 0; + public static final int AM_PM = 9; + public static final int DAY_OF_MONTH = 5; + public static final int DAY_OF_WEEK = 7; + public static final int HOUR = 10; + public static final int HOUR_OF_DAY = 11; + public static final int MINUTE = 12; + public static final int MONTH = 2; + public static final int PM = 1; + public static final int SECOND = 13; + public static final int YEAR = 1; + + public static final int FIELD_COUNT = 17; + + protected long time; + protected boolean isTimeSet; + protected int[] fields = new int[FIELD_COUNT]; + protected boolean areFieldsSet; + protected boolean[] isSet = new boolean[FIELD_COUNT]; + + protected Calendar() { } + + public static Calendar getInstance() { + return new MyCalendar(System.currentTimeMillis()); + } + + public int get(int field) { + return fields[field]; + } + + public void set(int field, int value) { + fields[field] = value; + } + + public void set(int year, int month, int date) { + set(YEAR, year); + set(MONTH, month); + set(DAY_OF_MONTH, date); + } + + public void setTime(Date date) { + time = date.getTime(); + } + + public Date getTime() { + return new Date(time); + } + + public abstract void roll(int field, boolean up); + public abstract void add(int field, int amount); + + public void roll(int field, int amount) { + boolean up = amount >= 0; + if (! up) { + amount = - amount; + } + for (int i = 0; i < amount; ++i) { + roll(field, up); + } + } + + public abstract int getMinimum(int field); + + public abstract int getMaximum(int field); + + public abstract int getActualMinimum(int field); + + public abstract int getActualMaximum(int field); + + private static class MyCalendar extends Calendar { + private static final long MILLIS_PER_DAY = 86400000; + private static final int MILLIS_PER_HOUR = 3600000; + private static final int MILLIS_PER_MINUTE = 60000; + private static final int MILLIS_PER_SECOND = 1000; + + private static final int EPOCH_YEAR = 1970; + private static final int EPOCH_LEAP_YEAR = 1968; + private static final int DAYS_TO_EPOCH = 731; + + private static final int[][] DAYS_IN_MONTH = new int[][] { + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } + }; + + public MyCalendar(long time) { + this.time = time; + this.isTimeSet = true; + parseIntoFields(time); + } + + public void setTime(Date date) { + super.setTime(date); + parseIntoFields(this.time); + } + + public Date getTime() { + long days = fields[DAY_OF_MONTH] - 1; + long years = fields[YEAR] - EPOCH_LEAP_YEAR; + days += years * 365 + years / 4 + 1 - DAYS_TO_EPOCH; + for (int month = 0; month < fields[MONTH]; month++) { + days += DAYS_IN_MONTH[0][month]; + } + if (fields[MONTH] < 2 && isLeapYear(fields[YEAR])) { + days--; + } + long time = MILLIS_PER_DAY * days + + MILLIS_PER_HOUR * fields[HOUR_OF_DAY] + + MILLIS_PER_MINUTE * fields[MINUTE] + + MILLIS_PER_SECOND * fields[SECOND]; + return new Date(time); + } + + private static boolean isLeapYear(int year) { + return (year%4 == 0) && (year%100 != 0) || (year%400 == 0); + } + + private void parseIntoFields(long timeInMillis) { + long days = timeInMillis / MILLIS_PER_DAY; + /* convert days since Jan 1, 1970 to days since Jan 1, 1968 */ + days += DAYS_TO_EPOCH; + long years = 4 * days / 1461; /* days/365.25 = 4*days/(4*365.25) */ + int year = (int)(EPOCH_LEAP_YEAR + years); + days -= 365 * years + years / 4; + if (!isLeapYear(year)) days--; + + int month=0; + int leapIndex = isLeapYear(year) ? 1 : 0; + while (days >= DAYS_IN_MONTH[leapIndex][month]) { + days -= DAYS_IN_MONTH[leapIndex][month++]; + } + days++; + + int remainder = (int)(timeInMillis % MILLIS_PER_DAY); + int hour = remainder / MILLIS_PER_HOUR; + remainder = remainder % MILLIS_PER_HOUR; + int minute = remainder / MILLIS_PER_MINUTE; + remainder = remainder % MILLIS_PER_MINUTE; + int second = remainder / MILLIS_PER_SECOND; + fields[YEAR] = year; + fields[MONTH] = month; + fields[DAY_OF_MONTH] = (int)days; + fields[HOUR_OF_DAY] = hour; + fields[MINUTE] = minute; + fields[SECOND] = second; + } + + public void roll(int field, boolean up) { + // todo + } + + public void add(int fild, int amount) { + // todo + } + + public int getMinimum(int field) { + // todo + return 0; + } + + public int getMaximum(int field) { + // todo + return 0; + } + + public int getActualMinimum(int field) { + // todo + return 0; + } + + public int getActualMaximum(int field) { + // todo + return 0; + } + } +} diff --git a/sgx-jvm/avian/classpath/java/util/Collection.java b/sgx-jvm/avian/classpath/java/util/Collection.java new file mode 100644 index 0000000000..9f0b740961 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/Collection.java @@ -0,0 +1,35 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public interface Collection extends Iterable { + public int size(); + + public boolean isEmpty(); + + public boolean contains(Object element); + + public boolean containsAll(Collection c); + + public boolean add(T element); + + public boolean addAll(Collection collection); + + public boolean remove(Object element); + + public boolean removeAll(Collection c); + + public Object[] toArray(); + + public S[] toArray(S[] array); + + public void clear(); +} diff --git a/sgx-jvm/avian/classpath/java/util/Collections.java b/sgx-jvm/avian/classpath/java/util/Collections.java new file mode 100644 index 0000000000..9a6f998503 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/Collections.java @@ -0,0 +1,816 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +import avian.Data; + +public class Collections { + + private Collections() { } + + public static void shuffle(List list, Random random) { + Object[] array = Data.toArray(list, new Object[list.size()]); + for (int i = 0; i < array.length; ++i) { + int j = random.nextInt(array.length); + Object tmp = array[i]; + array[i] = array[j]; + array[j] = tmp; + } + + list.clear(); + for (int i = 0; i < array.length; ++i) { + list.add(array[i]); + } + } + + public static void shuffle(List list) { + shuffle(list, new Random()); + } + + public static void sort(List list) { + sort(list, new Comparator() { + public int compare(Object a, Object b) { + return ((Comparable) a).compareTo(b); + } + }); + } + + private final static int SORT_SIZE_THRESHOLD = 16; + + public static void sort(List list, Comparator comparator) { + int size = list.size(); + introSort(list, comparator, 0, size, size); + insertionSort(list, comparator); + } + + private static void introSort(List list, + Comparator comparator, int begin, int end, int limit) + { + while (end - begin > SORT_SIZE_THRESHOLD) { + if (limit == 0) { + heapSort(list, comparator, begin, end); + return; + } + limit >>= 1; + + // median of three + T a = list.get(begin); + T b = list.get(begin + (end - begin) / 2 + 1); + T c = list.get(end - 1); + T median; + if (comparator.compare(a, b) < 0) { + median = comparator.compare(b, c) < 0 ? + b : (comparator.compare(a, c) < 0 ? c : a); + } else { + median = comparator.compare(b, c) > 0 ? + b : (comparator.compare(a, c) > 0 ? c : a); + } + + // partition + int pivot, i = begin, j = end; + for (;;) { + while (comparator.compare(list.get(i), median) < 0) { + ++i; + } + --j; + while (comparator.compare(median, list.get(j)) < 0) { + --j; + } + if (i >= j) { + pivot = i; + break; + } + T swap = list.get(i); + list.set(i, list.get(j)); + list.set(j, swap); + ++i; + } + + introSort(list, comparator, pivot, end, limit); + end = pivot; + } + } + + private static void heapSort(List list, Comparator comparator, + int begin, int end) + { + int count = end - begin; + for (int i = count / 2 - 1; i >= 0; --i) { + siftDown(list, comparator, i, count, begin); + } + for (int i = count - 1; i > 0; --i) { + // swap begin and begin + i + T swap = list.get(begin + i); + list.set(begin + i, list.get(begin)); + list.set(begin, swap); + + siftDown(list, comparator, 0, i, begin); + } + } + + private static void siftDown(List list, Comparator comparator, + int i, int count, int offset) + { + T value = list.get(offset + i); + while (i < count / 2) { + int child = 2 * i + 1; + if (child + 1 < count && + comparator.compare(list.get(child), list.get(child + 1)) < 0) { + ++child; + } + if (comparator.compare(value, list.get(child)) >= 0) { + break; + } + list.set(offset + i, list.get(offset + child)); + i = child; + } + list.set(offset + i, value); + } + + private static void insertionSort(List list, + Comparator comparator) + { + int size = list.size(); + for (int j = 1; j < size; ++j) { + T t = list.get(j); + int i = j - 1; + while (i >= 0 && comparator.compare(list.get(i), t) > 0) { + list.set(i + 1, list.get(i)); + --i; + } + list.set(i + 1, t); + } + } + + public static int binarySearch(List list, T needle) { + int left = -1, right = list.size(); + while (left + 1 < right) { + int middle = (left + right) >> 1; + int result = ((Comparable)needle).compareTo(list.get(middle)); + if (result < 0) { + right = middle; + } else if (result > 0) { + left = middle; + } else { + return middle; + } + } + return -1 - right; + } + + public static void reverse(List list) { + int ascending = 0, descending = list.size() - 1; + while (ascending < descending) { + T tmp = list.get(ascending); + list.set(ascending++, list.get(descending)); + list.set(descending--, tmp); + } + } + + public static final List EMPTY_LIST + = new UnmodifiableList(new ArrayList(0)); + + public static final List emptyList() { + return EMPTY_LIST; + } + + public static final Map emptyMap() { + return (Map) new UnmodifiableMap( + new HashMap(0)); + } + + public static final Set emptySet() { + return (Set) new UnmodifiableSet( + new HashSet(0)); + } + + public static Enumeration enumeration(Collection c) { + return new IteratorEnumeration (c.iterator()); + } + + public static Comparator reverseOrder(Comparator cmp) { + return new ReverseComparator(cmp); + } + + static class IteratorEnumeration implements Enumeration { + private final Iterator it; + + public IteratorEnumeration(Iterator it) { + this.it = it; + } + + public T nextElement() { + return it.next(); + } + + public boolean hasMoreElements() { + return it.hasNext(); + } + } + + static class SynchronizedCollection implements Collection { + protected final Object lock; + protected final Collection collection; + + public SynchronizedCollection(Object lock, Collection collection) { + this.lock = lock; + this.collection = collection; + } + + public int size() { + synchronized (lock) { return collection.size(); } + } + + public boolean isEmpty() { + return size() == 0; + } + + public boolean contains(Object e) { + synchronized (lock) { return collection.contains(e); } + } + + public boolean add(T e) { + synchronized (lock) { return collection.add(e); } + } + + public boolean addAll(Collection collection) { + synchronized (lock) { return this.collection.addAll(collection); } + } + + public boolean remove(Object e) { + synchronized (lock) { return collection.remove((T)e); } + } + + public Object[] toArray() { + return toArray(new Object[size()]); + } + + public T[] toArray(T[] array) { + synchronized (lock) { return collection.toArray(array); } + } + + public void clear() { + synchronized (lock) { collection.clear(); } + } + + public Iterator iterator() { + return new SynchronizedIterator(lock, collection.iterator()); + } + + public boolean containsAll(Collection c) { + synchronized (lock) { return collection.containsAll(c); } + } + + public boolean removeAll(Collection c) { + synchronized (lock) { return collection.removeAll(c); } + } + } + + static class SynchronizedMap implements Map { + protected final Object lock; + protected final Map map; + + SynchronizedMap(Map map) { + this.map = map; + this.lock = this; + } + + SynchronizedMap(Object lock, Map map) { + this.lock = lock; + this.map = map; + } + + public void clear() { + synchronized (lock) { map.clear(); } + } + public boolean containsKey(Object key) { + synchronized (lock) { return map.containsKey(key); } + } + public boolean containsValue(Object value) { + synchronized (lock) { return map.containsValue(value); } + } + public Set> entrySet() { + synchronized (lock) { return new SynchronizedSet>(lock, map.entrySet()); } + } + public V get(Object key) { + synchronized (lock) { return map.get(key); } + } + public boolean isEmpty() { + synchronized (lock) { return map.isEmpty(); } + } + public Set keySet() { + synchronized (lock) { return new SynchronizedSet(lock, map.keySet()); } + } + public V put(K key, V value) { + synchronized (lock) { return map.put(key, value); } + } + public void putAll(Map elts) { + synchronized (lock) { map.putAll(elts); } + } + public V remove(Object key) { + synchronized (lock) { return map.remove(key); } + } + public int size() { + synchronized (lock) { return map.size(); } + } + public Collection values() { + synchronized (lock) { return new SynchronizedCollection(lock, map.values()); } + } + } + + public static Map synchronizedMap(Map map) { + return new SynchronizedMap (map); + } + + static class SynchronizedSet + extends SynchronizedCollection + implements Set + { + public SynchronizedSet(Object lock, Set set) { + super(lock, set); + } + } + + public static Set synchronizedSet(Set set) { + return new SynchronizedSet (set, set); + } + + static class SynchronizedList + extends SynchronizedCollection + implements List + { + private final List list; + + public SynchronizedList(List list) { + super(list, list); + + this.list = list; + } + + @Override + public T get(int index) { + synchronized (lock) { + return list.get(index); + } + } + + @Override + public T set(int index, T value) { + synchronized (lock) { + return list.set(index, value); + } + } + + @Override + public T remove(int index) { + synchronized (lock) { + return list.remove(index); + } + } + + @Override + public void add(int index, T element) { + synchronized (lock) { + list.add(index, element); + } + } + + @Override + public boolean addAll(int startIndex, Collection c) { + synchronized (lock) { + return list.addAll(startIndex, c); + } + } + + @Override + public int indexOf(Object value) { + synchronized (lock) { + return list.indexOf(value); + } + } + + @Override + public int lastIndexOf(Object value) { + synchronized (lock) { + return list.lastIndexOf(value); + } + } + + @Override + public ListIterator listIterator(int index) { + // as described in the javadocs, user should be synchronized on list before calling + return list.listIterator(index); + } + + @Override + public ListIterator listIterator() { + // as described in the javadocs, user should be synchronized on list before calling + return list.listIterator(); + } + } + + static class RandomAccessSynchronizedList + extends SynchronizedList + implements RandomAccess + { + public RandomAccessSynchronizedList(List list) { + super(list); + } + } + + public static List synchronizedList(List list) { + List result; + if (list instanceof RandomAccess) { + result = new RandomAccessSynchronizedList(list); + } else { + result = new SynchronizedList(list); + } + + return result; + } + + static class SynchronizedIterator implements Iterator { + private final Object lock; + private final Iterator it; + + public SynchronizedIterator(Object lock, Iterator it) { + this.lock = lock; + this.it = it; + } + + public T next() { + synchronized (lock) { return it.next(); } + } + + public boolean hasNext() { + synchronized (lock) { return it.hasNext(); } + } + + public void remove() { + synchronized (lock) { it.remove(); } + } + } + + static class ArrayListIterator implements ListIterator { + private final List list; + private int toRemove = -1; + private int index; + + public ArrayListIterator(List list) { + this(list, 0); + } + + public ArrayListIterator(List list, int index) { + this.list = list; + this.index = index - 1; + } + + public boolean hasPrevious() { + return index >= 0; + } + + public T previous() { + if (hasPrevious()) { + toRemove = index; + return list.get(index--); + } else { + throw new NoSuchElementException(); + } + } + + public T next() { + if (hasNext()) { + toRemove = ++index; + return list.get(index); + } else { + throw new NoSuchElementException(); + } + } + + public boolean hasNext() { + return index + 1 < list.size(); + } + + public void remove() { + if (toRemove != -1) { + list.remove(toRemove); + index = toRemove - 1; + toRemove = -1; + } else { + throw new IllegalStateException(); + } + } + } + + public static List unmodifiableList(List list) { + return new UnmodifiableList(list); + } + + static class UnmodifiableList implements List { + + private List inner; + + UnmodifiableList(List l) { + this.inner = l; + } + + public T get(int index) { + return inner.get(index); + } + + public T set(int index, T value) { + throw new UnsupportedOperationException(); + } + + public T remove(int index) { + throw new UnsupportedOperationException(); + } + + public boolean remove(Object o) { + throw new UnsupportedOperationException(); + } + + public boolean add(T element) { + throw new UnsupportedOperationException(); + } + + public void add(int index, T element) { + throw new UnsupportedOperationException(); + } + + public Iterator iterator() { + return new UnmodifiableIterator(inner.iterator()); + } + + public int indexOf(Object value) { + return inner.indexOf(value); + } + + public int lastIndexOf(Object value) { + return inner.lastIndexOf(value); + } + + public boolean isEmpty() { + return inner.isEmpty(); + } + + public ListIterator listIterator(int index) { + return new UnmodifiableListIterator(inner.listIterator(index)); + } + + public ListIterator listIterator() { + return new UnmodifiableListIterator(inner.listIterator()); + } + + public int size() { + return inner.size(); + } + + public boolean contains(Object element) { + return inner.contains(element); + } + + public boolean addAll(Collection collection) { + throw new UnsupportedOperationException(); + } + + public Object[] toArray() { + return inner.toArray(); + } + + public S[] toArray(S[] array) { + return inner.toArray(array); + } + + public void clear() { + throw new UnsupportedOperationException(); + } + + public boolean removeAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public boolean addAll(int startIndex, Collection c) { + throw new UnsupportedOperationException(); + } + + public boolean containsAll(Collection c) { + return inner.containsAll(c); + } + } + + public static Map unmodifiableMap(Map m) { + return new UnmodifiableMap(m); + } + + static class UnmodifiableMap implements Map { + private Map inner; + + UnmodifiableMap(Map m) { + this.inner = m; + } + + public void clear() { + throw new UnsupportedOperationException(); + } + + public boolean containsKey(Object key) { + return inner.containsKey(key); + } + + public boolean containsValue(Object value) { + return inner.containsValue(value); + } + + public Set> entrySet() { + return unmodifiableSet(inner.entrySet()); + } + + public V get(Object key) { + return inner.get(key); + } + + public boolean isEmpty() { + return inner.isEmpty(); + } + + public Set keySet() { + return unmodifiableSet(inner.keySet()); + } + + public V put(K key, V value) { + throw new UnsupportedOperationException(); + } + + public void putAll(Map t) { + throw new UnsupportedOperationException(); + } + + public V remove(Object key) { + throw new UnsupportedOperationException(); + } + + public int size() { + return inner.size(); + } + + public Collection values() { + return unmodifiableCollection(inner.values()); + } + } + + static class UnmodifiableIterator implements Iterator { + private final Iterator inner; + + UnmodifiableIterator(Iterator inner) { + this.inner = inner; + } + + @Override + public T next() { + return inner.next(); + } + + @Override + public boolean hasNext() { + return inner.hasNext(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + + + static class UnmodifiableListIterator extends UnmodifiableIterator + implements ListIterator { + private final ListIterator innerListIterator; + + UnmodifiableListIterator(ListIterator listIterator) { + super(listIterator); + + this.innerListIterator = listIterator; + } + + @Override + public boolean hasPrevious() { + return innerListIterator.hasPrevious(); + } + + @Override + public T previous() { + return innerListIterator.previous(); + } + } + + static class UnmodifiableCollection implements Collection { + private final Collection inner; + + UnmodifiableCollection(Collection inner) { + this.inner = inner; + } + + @Override + public Iterator iterator() { + return new UnmodifiableIterator(inner.iterator()); + } + + @Override + public int size() { + return inner.size(); + } + + @Override + public boolean isEmpty() { + return inner.isEmpty(); + } + + @Override + public boolean contains(Object element) { + return inner.contains(element); + } + + @Override + public boolean containsAll(Collection c) { + return inner.containsAll(c); + } + + @Override + public boolean add(T element) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(Collection collection) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(Object element) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public Object[] toArray() { + return inner.toArray(); + } + + @Override + public S[] toArray(S[] array) { + return inner.toArray(array); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + } + + public static UnmodifiableCollection unmodifiableCollection(Collection collection) { + return new UnmodifiableCollection(collection); + } + + static class UnmodifiableSet extends UnmodifiableCollection + implements Set { + UnmodifiableSet(Set inner) { + super(inner); + } + } + + public static Set unmodifiableSet(Set hs) { + return new UnmodifiableSet(hs); + } + + private static final class ReverseComparator implements Comparator { + + Comparator cmp; + + public ReverseComparator(Comparator cmp) { + this.cmp = cmp; + } + + public int compare(T o1, T o2) { + return - cmp.compare(o1, o2); + } + } + + public static List singletonList(T o) { + ArrayList list = new ArrayList(1); + list.add(o); + return new UnmodifiableList(list); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/Comparator.java b/sgx-jvm/avian/classpath/java/util/Comparator.java new file mode 100644 index 0000000000..6d861ea393 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/Comparator.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public interface Comparator { + public int compare(T o1, T o2); +} diff --git a/sgx-jvm/avian/classpath/java/util/ConcurrentModificationException.java b/sgx-jvm/avian/classpath/java/util/ConcurrentModificationException.java new file mode 100644 index 0000000000..f828ac169e --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/ConcurrentModificationException.java @@ -0,0 +1,47 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +/** + * @author zsombor + * + */ +public class ConcurrentModificationException extends RuntimeException { + + /** + * @param message + * @param cause + */ + public ConcurrentModificationException(String message, Throwable cause) { + super(message, cause); + } + + /** + * @param message + */ + public ConcurrentModificationException(String message) { + super(message); + } + + /** + * @param cause + */ + public ConcurrentModificationException(Throwable cause) { + super(cause); + } + + /** + * + */ + public ConcurrentModificationException() { + } + +} diff --git a/sgx-jvm/avian/classpath/java/util/Date.java b/sgx-jvm/avian/classpath/java/util/Date.java new file mode 100644 index 0000000000..2681c84bf5 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/Date.java @@ -0,0 +1,33 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public class Date { + public final long when; + + public Date() { + when = System.currentTimeMillis(); + } + + public Date(long when) { + this.when = when; + } + + public long getTime() { + return when; + } + + public String toString() { + return toString(when); + } + + private static native String toString(long when); +} diff --git a/sgx-jvm/avian/classpath/java/util/Deque.java b/sgx-jvm/avian/classpath/java/util/Deque.java new file mode 100644 index 0000000000..8586090f42 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/Deque.java @@ -0,0 +1,31 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public interface Deque extends Queue { + public boolean offerFirst(T e); + public void push(T e); + public void addFirst(T element); + public boolean offerLast(T e); + public void addLast(T element); + public T peekFirst(); + public T getFirst(); + public T peekLast(); + public T getLast(); + public T pollFirst(); + public T removeFirst(); + public T pop(); + public T pollLast(); + public T removeLast(); + public Iterator descendingIterator(); + public boolean removeLastOccurrence(Object o); + public boolean removeFirstOccurrence(Object o); +} diff --git a/sgx-jvm/avian/classpath/java/util/EmptyStackException.java b/sgx-jvm/avian/classpath/java/util/EmptyStackException.java new file mode 100644 index 0000000000..2e5a406987 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/EmptyStackException.java @@ -0,0 +1,13 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public class EmptyStackException extends RuntimeException {} diff --git a/sgx-jvm/avian/classpath/java/util/EnumSet.java b/sgx-jvm/avian/classpath/java/util/EnumSet.java new file mode 100644 index 0000000000..7b40b56405 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/EnumSet.java @@ -0,0 +1,155 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public class EnumSet> extends AbstractSet { + + private BitSet bitset; + private Class elementType; + + private EnumSet(int size, Class type) { + bitset = new BitSet(size); + elementType = type; + } + + @Override + public boolean add(T element) { + int index = element.ordinal(); + boolean contains = bitset.get(index); + if (!contains) { + bitset.set(index); + } + + return !contains; + } + + @Override + public boolean remove(Object toRemove) { + T element = tryToCast(toRemove); + + int index = element.ordinal(); + boolean contains = bitset.get(index); + if (contains) { + bitset.clear(index); + } + + return contains; + } + + @Override + public boolean contains(Object toCheck) { + T element = tryToCast(toCheck); + int index = element.ordinal(); + return bitset.get(index); + } + + @Override + public int size() { + return bitset.cardinality(); + } + + @Override + public Iterator iterator() { + return new EnumSetIterator(); + } + + public static >EnumSet allOf(Class elementType) { + EnumSet enumSet = createEmptyEnumSet(elementType); + enumSet.bitset.set(0, elementType.getEnumConstants().length); + + return enumSet; + } + + public static >EnumSet noneOf(Class elementType) { + return createEmptyEnumSet(elementType); + } + + public static >EnumSet of(T first, T ... rest) { + EnumSet enumSet = createEmptyEnumSet(first.getDeclaringClass()); + enumSet.add(first); + addAllElementsToSet(Arrays.asList(rest), enumSet); + + return enumSet; + } + + public static >EnumSet complementOf(EnumSet s) { + EnumSet enumSet = copyOf(s); + enumSet.bitset.flip(0, s.elementType.getEnumConstants().length); + + return enumSet; + } + + public static >EnumSet copyOf(EnumSet s) { + EnumSet enumSet = createEmptyEnumSet(s.elementType); + enumSet.bitset.or(s.bitset); + + return enumSet; + } + + private static >EnumSet createEmptyEnumSet(Class elementType) { + T[] constants = elementType.getEnumConstants(); + EnumSet enumSet = new EnumSet(constants.length, elementType); + + return enumSet; + } + + private static > void addAllElementsToSet(Iterable elements, EnumSet enumSet) { + for (T element : elements) { + enumSet.add(element); + } + } + + @SuppressWarnings("unchecked") + private T tryToCast(Object object) throws ClassCastException { + //We want the class cast exception if we can't convert. + return (T) object; + } + + private class EnumSetIterator implements Iterator { + private int currentIndex = 0; + private boolean removeAllowed = false; + + public T next() { + if (!hasNext()) { + throw new NoSuchElementException("EnumSet has no more elements"); + } + + int indexOfNextValue = nextIndex(); + T element = elementType.getEnumConstants()[indexOfNextValue]; + currentIndex = indexOfNextValue + 1; + removeAllowed = true; + + return element; + } + + public boolean hasNext() { + int indexOfNextValue = nextIndex(); + if (indexOfNextValue >= 0) { + return true; + } else { + return false; + } + } + + public void remove() { + if (!removeAllowed) { + throw new IllegalStateException("Cannot remove from this iterator in this state"); + } + + bitset.clear(currentIndex - 1); + removeAllowed = false; + } + + private int nextIndex() { + return bitset.nextSetBit(currentIndex); + } + } +} diff --git a/sgx-jvm/avian/classpath/java/util/Enumeration.java b/sgx-jvm/avian/classpath/java/util/Enumeration.java new file mode 100644 index 0000000000..70e50b6d59 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/Enumeration.java @@ -0,0 +1,17 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public interface Enumeration { + public T nextElement(); + + public boolean hasMoreElements(); +} diff --git a/sgx-jvm/avian/classpath/java/util/EventListener.java b/sgx-jvm/avian/classpath/java/util/EventListener.java new file mode 100644 index 0000000000..3b27d4a709 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/EventListener.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public interface EventListener { + +} diff --git a/sgx-jvm/avian/classpath/java/util/EventObject.java b/sgx-jvm/avian/classpath/java/util/EventObject.java new file mode 100644 index 0000000000..25be1f5df2 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/EventObject.java @@ -0,0 +1,23 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public class EventObject { + protected Object source; + + public EventObject(Object source) { + this.source = source; + } + + public Object getSource() { + return source; + } +} diff --git a/sgx-jvm/avian/classpath/java/util/Formatter.java b/sgx-jvm/avian/classpath/java/util/Formatter.java new file mode 100644 index 0000000000..12d5919e82 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/Formatter.java @@ -0,0 +1,158 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +import java.io.Closeable; +import java.io.File; +import java.io.FileWriter; +import java.io.Flushable; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintStream; +import java.io.Writer; + +import avian.FormatString; + +public class Formatter implements Closeable, Flushable, AutoCloseable { + + private final Appendable _out; + + private final Locale _locale; + + private IOException lastException; + + private boolean _closed; + + private void ensureNotClosed() { + if (this._closed) { + throw new IllegalStateException(); + } + } + + public Formatter() { + this((Appendable) null, (Locale) null); + } + + public Formatter(final Appendable a) { + this(a, null); + } + + public Formatter(final Appendable a, final Locale l) { + if (a == null) { + this._out = new StringBuilder(); + } else { + this._out = a; + } + if (l == null) { + this._locale = Locale.getDefault(); + } else { + this._locale = l; + } + } + + public Formatter(File file) throws IOException { + this(file, null); + } + + public Formatter(File file, Locale l) throws IOException { + this(new FileWriter(file), l); + } + + public Formatter(Locale l) { + this((Appendable) null, l); + } + + public Formatter(OutputStream os) { + this(os, null); + } + + public Formatter(OutputStream os, Locale l) { + this(new OutputStreamWriter(os), l); + } + + public Formatter(PrintStream ps) { + this(new OutputStreamWriter(ps)); + } + + public Formatter(String fileName) throws IOException { + this(fileName, (Locale) null); + } + + public Formatter(String fileName, Locale l) throws IOException { + this(new File(fileName), l); + } + + public void close() { + if (this._closed) { + return; + } + final Appendable out = out(); + if (out instanceof Closeable) { + final Closeable closeable = (Closeable) out; + try { + closeable.close(); + } catch (IOException e) { + throw new RuntimeException("An error occurred while closing.", e); + } + this._closed = true; + } + } + + public void flush() { + ensureNotClosed(); + final Appendable out = out(); + if (out instanceof Flushable) { + final Flushable flushable = (Flushable) out; + try { + flushable.flush(); + } catch (IOException e) { + throw new RuntimeException("An error occurred while flushing.", e); + } + } + } + + public Formatter format(Locale l, final String format, final Object...args) { + ensureNotClosed(); + try { + final FormatString formatString = FormatString.compile(format); + this._out.append(formatString.format(args)); + } catch (IOException e) { + this.lastException = e; + } + return this; + } + + public Formatter format(String format, Object... args) { + ensureNotClosed(); + return this.format(null, format, args); + } + + public IOException ioException() { + return lastException; + } + + public Locale locale() { + ensureNotClosed(); + return this._locale; + } + + public Appendable out() { + ensureNotClosed(); + return this._out; + } + + public String toString() { + ensureNotClosed(); + return this._out.toString(); + } + +} diff --git a/sgx-jvm/avian/classpath/java/util/HashMap.java b/sgx-jvm/avian/classpath/java/util/HashMap.java new file mode 100644 index 0000000000..c86f1dde4a --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/HashMap.java @@ -0,0 +1,385 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +import avian.Data; + +public class HashMap implements Map { + private static final int MinimumCapacity = 16; + + private int size; + private Cell[] array; + private final Helper helper; + + public HashMap(int capacity, Helper helper) { + if (capacity > 0) { + array = new Cell[Data.nextPowerOfTwo(capacity)]; + } + this.helper = helper; + } + + public HashMap(int capacity) { + this(capacity, new MyHelper()); + } + + public HashMap() { + this(0); + } + + public HashMap(Map map) { + this(map.size()); + for (Map.Entry entry : map.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + } + + public String toString() { + return avian.Data.toString(this); + } + + public boolean isEmpty() { + return size() == 0; + } + + public int size() { + return size; + } + + private void grow() { + if (array == null || size >= array.length * 2) { + resize(array == null ? MinimumCapacity : array.length * 2); + } + } + + private void shrink() { + if (array.length / 2 >= MinimumCapacity && size <= array.length / 3) { + resize(array.length / 2); + } + } + + private void resize(int capacity) { + Cell[] newArray = null; + if (capacity != 0) { + capacity = Data.nextPowerOfTwo(capacity); + if (array != null && array.length == capacity) { + return; + } + + newArray = new Cell[capacity]; + if (array != null) { + for (int i = 0; i < array.length; ++i) { + Cell next; + for (Cell c = array[i]; c != null; c = next) { + next = c.next(); + int index = c.hashCode() & (capacity - 1); + c.setNext(newArray[index]); + newArray[index] = c; + } + } + } + } + array = newArray; + } + + protected Cell find(Object key) { + if (array != null) { + int index = helper.hash(key) & (array.length - 1); + for (Cell c = array[index]; c != null; c = c.next()) { + if (helper.equal(key, c.getKey())) { + return c; + } + } + } + + return null; + } + + private void insert(Cell cell) { + ++ size; + + grow(); + + int index = cell.hashCode() & (array.length - 1); + cell.setNext(array[index]); + array[index] = cell; + } + + public void remove(Cell cell) { + int index = cell.hashCode() & (array.length - 1); + Cell p = null; + for (Cell c = array[index]; c != null; c = c.next()) { + if (c == cell) { + if (p == null) { + array[index] = c.next(); + } else { + p.setNext(c.next()); + } + -- size; + break; + } + } + + shrink(); + } + + private Cell putCell(K key, V value) { + Cell c = find(key); + if (c == null) { + insert(helper.make(key, value, null)); + } else { + c.setValue(value); + } + return c; + } + + public boolean containsKey(Object key) { + return find(key) != null; + } + + public boolean containsValue(Object value) { + if (array != null) { + for (int i = 0; i < array.length; ++i) { + for (Cell c = array[i]; c != null; c = c.next()) { + if (helper.equal(value, c.getValue())) { + return true; + } + } + } + } + + return false; + } + + public V get(Object key) { + Cell c = find(key); + return (c == null ? null : c.getValue()); + } + + public Cell removeCell(Object key) { + Cell old = null; + if (array != null) { + int index = helper.hash(key) & (array.length - 1); + Cell p = null; + for (Cell c = array[index]; c != null; c = c.next()) { + if (helper.equal(key, c.getKey())) { + old = c; + if (p == null) { + array[index] = c.next(); + } else { + p.setNext(c.next()); + } + -- size; + break; + } + p = c; + } + + shrink(); + } + return old; + } + + public V put(K key, V value) { + Cell c = find(key); + if (c == null) { + insert(helper.make(key, value, null)); + return null; + } else { + V old = c.getValue(); + c.setValue(value); + return old; + } + } + + public void putAll(Map elts) { + for (Map.Entry entry : elts.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + } + + public V remove(Object key) { + Cell c = removeCell((K)key); + return (c == null ? null : c.getValue()); + } + + public void clear() { + array = null; + size = 0; + } + + public Set> entrySet() { + return new Data.EntrySet(new MyEntryMap()); + } + + public Set keySet() { + return new Data.KeySet(new MyEntryMap()); + } + + public Collection values() { + return new Data.Values(new MyEntryMap()); + } + + Iterator> iterator() { + return new MyIterator(); + } + + private class MyEntryMap implements Data.EntryMap { + public int size() { + return HashMap.this.size(); + } + + public Entry find(Object key) { + return HashMap.this.find(key); + } + + public Entry remove(Object key) { + return removeCell(key); + } + + public void clear() { + HashMap.this.clear(); + } + + public Iterator> iterator() { + return HashMap.this.iterator(); + } + } + + interface Cell extends Entry { + public HashMap.Cell next(); + + public void setNext(HashMap.Cell next); + } + + interface Helper { + public Cell make(K key, V value, Cell next); + + public int hash(K key); + + public boolean equal(K a, K b); + } + + private static class MyCell implements Cell { + public final K key; + public V value; + public Cell next; + public int hashCode; + + public MyCell(K key, V value, Cell next, int hashCode) { + this.key = key; + this.value = value; + this.next = next; + this.hashCode = hashCode; + } + + public K getKey() { + return key; + } + + public V getValue() { + return value; + } + + public V setValue(V value) { + V old = this.value; + this.value = value; + return old; + } + + public HashMap.Cell next() { + return next; + } + + public void setNext(HashMap.Cell next) { + this.next = next; + } + + public int hashCode() { + return hashCode; + } + } + + static class MyHelper implements Helper { + public Cell make(K key, V value, Cell next) { + return new MyCell(key, value, next, hash(key)); + } + + public int hash(K a) { + return (a == null ? 0 : a.hashCode()); + } + + public boolean equal(K a, K b) { + return (a == null && b == null) || (a != null && a.equals(b)); + } + } + + private class MyIterator implements Iterator> { + private int currentIndex = -1; + private int nextIndex = -1; + private Cell previousCell; + private Cell currentCell; + private Cell nextCell; + + public MyIterator() { + hasNext(); + } + + public Entry next() { + if (hasNext()) { + if (currentCell != null) { + if (currentCell.next() != null) { + previousCell = currentCell; + } else { + previousCell = null; + } + } + + currentCell = nextCell; + currentIndex = nextIndex; + + nextCell = nextCell.next(); + + return currentCell; + } else { + throw new NoSuchElementException(); + } + } + + public boolean hasNext() { + if (array != null) { + while (nextCell == null && ++ nextIndex < array.length) { + if (array[nextIndex] != null) { + nextCell = array[nextIndex]; + return true; + } + } + } + return nextCell != null; + } + + public void remove() { + if (currentCell != null) { + if (previousCell == null) { + array[currentIndex] = currentCell.next(); + } else { + previousCell.setNext(currentCell.next()); + if (previousCell.next() == null) { + previousCell = null; + } + } + currentCell = null; + -- size; + } else { + throw new IllegalStateException(); + } + } + } +} diff --git a/sgx-jvm/avian/classpath/java/util/HashSet.java b/sgx-jvm/avian/classpath/java/util/HashSet.java new file mode 100644 index 0000000000..3c70cda172 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/HashSet.java @@ -0,0 +1,88 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public class HashSet extends AbstractSet implements Set { + private static final Object Value = new Object(); + + private final HashMap map; + + public HashSet(Collection c) { + map = new HashMap(c.size()); + addAll(c); + } + + public HashSet(int capacity) { + map = new HashMap(capacity); + } + + public HashSet() { + this(0); + } + + public int size() { + return map.size(); + } + + public boolean isEmpty() { + return map.isEmpty(); + } + + public boolean contains(Object element) { + return map.containsKey(element); + } + + public boolean add(T element) { + return map.put(element, Value) != Value; + } + + public boolean addAll(Collection collection) { + boolean change = false; + for (T t: collection) if (add(t)) change = true; + return change; + } + + public boolean remove(Object element) { + return map.remove(element) != Value; + } + + public void clear() { + map.clear(); + } + + public Iterator iterator() { + return new MyIterator(map.iterator()); + } + + public String toString() { + return avian.Data.toString(this); + } + + private static class MyIterator implements Iterator { + private final Iterator> it; + + public MyIterator(Iterator> it) { + this.it = it; + } + + public T next() { + return it.next().getKey(); + } + + public boolean hasNext() { + return it.hasNext(); + } + + public void remove() { + it.remove(); + } + } +} diff --git a/sgx-jvm/avian/classpath/java/util/Hashtable.java b/sgx-jvm/avian/classpath/java/util/Hashtable.java new file mode 100644 index 0000000000..4f277a1d18 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/Hashtable.java @@ -0,0 +1,91 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public class Hashtable implements Map { + private final HashMap map; + + public Hashtable(int capacity) { + map = new HashMap(capacity); + } + + public Hashtable() { + this(0); + } + + public Hashtable(Map m) { + this(m.size()); + for (Entry entry : m.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + } + + public synchronized String toString() { + return map.toString(); + } + + public synchronized boolean isEmpty() { + return map.isEmpty(); + } + + public synchronized int size() { + return map.size(); + } + + public synchronized boolean containsKey(Object key) { + return map.containsKey(key); + } + + public synchronized boolean containsValue(Object value) { + return map.containsValue(value); + } + + public synchronized V get(Object key) { + return map.get(key); + } + + public synchronized V put(K key, V value) { + return map.put(key, value); + } + + public synchronized void putAll(Map elts) { + map.putAll(elts); + } + + public synchronized V remove(Object key) { + return map.remove(key); + } + + public synchronized void clear() { + map.clear(); + } + + public Enumeration keys() { + return new Collections.IteratorEnumeration(keySet().iterator()); + } + + public Enumeration elements() { + return new Collections.IteratorEnumeration(values().iterator()); + } + + public Set> entrySet() { + return new Collections.SynchronizedSet(this, map.entrySet()); + } + + public Set keySet() { + return new Collections.SynchronizedSet(this, map.keySet()); + } + + public Collection values() { + return new Collections.SynchronizedCollection(this, map.values()); + } + +} diff --git a/sgx-jvm/avian/classpath/java/util/IdentityHashMap.java b/sgx-jvm/avian/classpath/java/util/IdentityHashMap.java new file mode 100644 index 0000000000..42fb4a5a6a --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/IdentityHashMap.java @@ -0,0 +1,83 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public class IdentityHashMap implements Map { + private final HashMap map; + + public IdentityHashMap(int capacity) { + map = new HashMap(capacity, new MyHelper()); + } + + public IdentityHashMap() { + this(0); + } + + public boolean isEmpty() { + return map.isEmpty(); + } + + public int size() { + return map.size(); + } + + public boolean containsKey(Object key) { + return map.containsKey(key); + } + + public boolean containsValue(Object value) { + return map.containsValue(value); + } + + public V get(Object key) { + return map.get(key); + } + + public V put(K key, V value) { + return map.put(key, value); + } + + public void putAll(Map elts) { + map.putAll(elts); + } + + public V remove(Object key) { + return map.remove(key); + } + + public void clear() { + map.clear(); + } + + public Set> entrySet() { + return map.entrySet(); + } + + public Set keySet() { + return map.keySet(); + } + + public Collection values() { + return map.values(); + } + + private static class MyHelper + extends HashMap.MyHelper + { + public int hash(K a) { + return (a == null ? 0 : System.identityHashCode(a)); + } + + public boolean equal(K a, K b) { + return a == b; + } + } +} diff --git a/sgx-jvm/avian/classpath/java/util/IllegalFormatException.java b/sgx-jvm/avian/classpath/java/util/IllegalFormatException.java new file mode 100644 index 0000000000..d9f24f4564 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/IllegalFormatException.java @@ -0,0 +1,31 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public class IllegalFormatException extends IllegalArgumentException { + + public IllegalFormatException() { + super(); + } + + public IllegalFormatException(String message, Throwable cause) { + super(message, cause); + } + + public IllegalFormatException(String message) { + super(message); + } + + public IllegalFormatException(Throwable cause) { + super(cause); + } + +} diff --git a/sgx-jvm/avian/classpath/java/util/Iterator.java b/sgx-jvm/avian/classpath/java/util/Iterator.java new file mode 100644 index 0000000000..cae013f04c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/Iterator.java @@ -0,0 +1,19 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public interface Iterator { + public T next(); + + public boolean hasNext(); + + public void remove(); +} diff --git a/sgx-jvm/avian/classpath/java/util/LinkedHashMap.java b/sgx-jvm/avian/classpath/java/util/LinkedHashMap.java new file mode 100644 index 0000000000..2b9bcce890 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/LinkedHashMap.java @@ -0,0 +1,267 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public class LinkedHashMap extends HashMap { + private static class LinkedKey { + private final K key; + private LinkedKey previous, next; + + public LinkedKey(K key) { + this.key = key; + } + + public boolean equals(Object other) { + LinkedKey o = (LinkedKey) other; + return key.equals(o.key); + } + + public int hashCode() { + return key.hashCode(); + } + } + + private LinkedKey first, last; + private HashMap> lookup; + + public LinkedHashMap(int capacity) { + super(capacity); + lookup = new HashMap>(); + } + + public LinkedHashMap() { + this(0); + } + + public LinkedHashMap(Map map) { + this(map.size()); + putAll(map); + } + + public V put(K key, V value) { + if (!super.containsKey(key)) { + LinkedKey k = new LinkedKey(key); + if (first == null) { + first = k; + } else { + last.next = k; + k.previous = last; + } + last = k; + lookup.put(key, k); + } + return super.put(key, value); + } + + public V remove(Object key) { + LinkedKey linked = lookup.get(key); + if (linked == null) { + return null; + } + if (linked.previous == null) { + first = linked.next; + } else { + linked.previous.next = linked.next; + } + if (linked.next == null) { + last = linked.previous; + } else { + linked.next.previous = linked.previous; + } + return super.remove(key); + } + + public void clear() { + first = last = null; + super.clear(); + } + + public Set> entrySet() { + return new EntrySet(); + } + + public Set keySet() { + return new KeySet(); + } + + public Collection values() { + return new Values(); + } + + Iterator> iterator() { + return new MyIterator(); + } + + private class EntrySet extends AbstractSet> { + public int size() { + return LinkedHashMap.this.size(); + } + + public boolean isEmpty() { + return LinkedHashMap.this.isEmpty(); + } + + public boolean contains(Object o) { + return (o instanceof Entry) + && containsKey(((Entry)o).getKey()); + } + + public boolean add(Entry e) { + return put(e.getKey(), e.getValue()) != null; + } + + public boolean remove(Object o) { + return (o instanceof Entry) && remove((Entry)o); + } + + public boolean remove(Entry e) { + return LinkedHashMap.this.remove(e.getKey()) != null; + } + + public Object[] toArray() { + return toArray(new Object[size()]); + } + + public T[] toArray(T[] array) { + return avian.Data.toArray(this, array); + } + + public void clear() { + LinkedHashMap.this.clear(); + } + + public Iterator> iterator() { + return new MyIterator(); + } + } + + private class KeySet extends AbstractSet { + public int size() { + return LinkedHashMap.this.size(); + } + + public boolean isEmpty() { + return LinkedHashMap.this.isEmpty(); + } + + public boolean contains(Object key) { + return containsKey(key); + } + + public boolean add(K key) { + return put(key, null) != null; + } + + public boolean remove(Object key) { + return LinkedHashMap.this.remove(key) != null; + } + + public Object[] toArray() { + return toArray(new Object[size()]); + } + + public T[] toArray(T[] array) { + return avian.Data.toArray(this, array); + } + + public void clear() { + LinkedHashMap.this.clear(); + } + + public Iterator iterator() { + return new avian.Data.KeyIterator(new MyIterator()); + } + } + + private class Values implements Collection { + public int size() { + return LinkedHashMap.this.size(); + } + + public boolean isEmpty() { + return LinkedHashMap.this.isEmpty(); + } + + public boolean contains(Object value) { + return containsValue(value); + } + + public boolean containsAll(Collection c) { + if (c == null) { + throw new NullPointerException("collection is null"); + } + + Iterator it = c.iterator(); + while (it.hasNext()) { + if (! contains(it.next())) { + return false; + } + } + + return true; + } + + public boolean add(V value) { + throw new UnsupportedOperationException(); + } + + public boolean addAll(Collection collection) { + throw new UnsupportedOperationException(); + } + + public boolean remove(Object value) { + throw new UnsupportedOperationException(); + } + + public boolean removeAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public Object[] toArray() { + return toArray(new Object[size()]); + } + + public T[] toArray(T[] array) { + return avian.Data.toArray(this, array); + } + + public void clear() { + LinkedHashMap.this.clear(); + } + + public Iterator iterator() { + return new avian.Data.ValueIterator(new MyIterator()); + } + } + + private class MyIterator implements Iterator> { + private LinkedKey current = first; + + public Entry next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + Entry result = find(current.key); + current = current.next; + return result; + } + + public boolean hasNext() { + return current != null; + } + + public void remove() { + LinkedHashMap.this.remove(current == null ? + last.key : current.previous.key); + } + } +} + diff --git a/sgx-jvm/avian/classpath/java/util/LinkedHashSet.java b/sgx-jvm/avian/classpath/java/util/LinkedHashSet.java new file mode 100644 index 0000000000..b508b1d5d5 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/LinkedHashSet.java @@ -0,0 +1,89 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public class LinkedHashSet extends AbstractSet implements Set { + private static final Object Value = new Object(); + + private final LinkedHashMap map; + + public LinkedHashSet(Collection c) { + map = new LinkedHashMap(c.size()); + addAll(c); + } + + public LinkedHashSet(int capacity) { + map = new LinkedHashMap(capacity); + } + + public LinkedHashSet() { + this(0); + } + + public int size() { + return map.size(); + } + + public boolean isEmpty() { + return map.isEmpty(); + } + + public boolean contains(Object element) { + return map.containsKey(element); + } + + public boolean add(T element) { + return map.put(element, Value) != Value; + } + + public boolean addAll(Collection collection) { + boolean change = false; + for (T t: collection) if (add(t)) change = true; + return change; + } + + public boolean remove(Object element) { + return map.remove(element) != Value; + } + + public void clear() { + map.clear(); + } + + public Iterator iterator() { + return new MyIterator(map.iterator()); + } + + public String toString() { + return avian.Data.toString(this); + } + + private static class MyIterator implements Iterator { + private final Iterator> it; + + public MyIterator(Iterator> it) { + this.it = it; + } + + public T next() { + return it.next().getKey(); + } + + public boolean hasNext() { + return it.hasNext(); + } + + public void remove() { + it.remove(); + } + } +} + diff --git a/sgx-jvm/avian/classpath/java/util/LinkedList.java b/sgx-jvm/avian/classpath/java/util/LinkedList.java new file mode 100644 index 0000000000..6896b1eb4e --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/LinkedList.java @@ -0,0 +1,463 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public class LinkedList extends AbstractSequentialList implements Deque { + private Cell front; + private Cell rear; + private int size; + + public LinkedList(Collection c) { + addAll(c); + } + + public LinkedList() { } + + private Cell find(int index) { + int i = 0; + for (Cell c = front; c != null; c = c.next) { + if (i == index) { + return c; + } + ++ i; + } + throw new IndexOutOfBoundsException(index + " not in [0, " + size + ")"); + } + + private static boolean equal(Object a, Object b) { + return (a == null && b == null) || (a != null && a.equals(b)); + } + + private void addFirst(Cell c) { + ++ size; + + if (front == null) { + front = rear = c; + } else { + c.next = front; + front.prev = c; + front = c; + } + } + + private void addLast(Cell c) { + ++ size; + + if (front == null) { + front = rear = c; + } else { + c.prev = rear; + rear.next = c; + rear = c; + } + } + + private Cell find(Object element) { + for (Cell c = front; c != null; c = c.next) { + if (equal(c.value, element)) { + return c; + } + } + return null; + } + + private void remove(Cell c) { + -- size; + + if (c.prev == null) { + front = c.next; + } else { + c.prev.next = c.next; + } + + if (c.next == null) { + rear = c.prev; + } else { + c.next.prev = c.prev; + } + } + + @Override + public int size() { + return size; + } + + @Override + public boolean contains(Object element) { + return find(element) != null; + } + + @Override + public int indexOf(Object element) { + int i = 0; + for (Cell c = front; c != null; c = c.next) { + if (equal(c.value, element)) { + return i; + } + ++ i; + } + return -1; + } + + @Override + public int lastIndexOf(Object element) { + int i = size; + for (Cell c = rear; c != null; c = c.prev) { + -- i; + if (equal(c.value, element)) { + return i; + } + } + return -1; + } + + @Override + public boolean offer(T element) { + return add(element); + } + + @Override + public boolean add(T element) { + addLast(element); + return true; + } + + @Override + public boolean addAll(Collection collection) { + for (T t: collection) add(t); + return true; + } + + @Override + public void add(int index, T element) { + if (index == 0) { + addFirst(element); + } else { + Cell cell = find(index); + Cell newCell = new Cell(element, cell.prev, cell); + cell.prev.next = newCell; + } + } + + @Override + public boolean offerFirst(T e) { + addFirst(e); + + return true; + } + + @Override + public void push(T e) { + addFirst(e); + } + + @Override + public void addFirst(T element) { + addFirst(new Cell(element, null, null)); + } + + @Override + public boolean offerLast(T e) { + addLast(e); + + return true; + } + + @Override + public void addLast(T element) { + addLast(new Cell(element, null, null)); + } + + public T get(int index) { + return find(index).value; + } + + @Override + public T set(int index, T value) { + Cell c = find(index); + T old = c.value; + c.value = value; + return old; + } + + @Override + public T peek() { + return peekFirst(); + } + + @Override + public T peekFirst() { + if (front != null) { + return front.value; + } else { + return null; + } + } + + @Override + public T getFirst() { + if (front != null) { + return front.value; + } else { + throw new NoSuchElementException(); + } + } + + @Override + public T peekLast() { + if (rear != null) { + return rear.value; + } else { + return null; + } + } + + @Override + public T getLast() { + if (rear != null) { + return rear.value; + } else { + throw new NoSuchElementException(); + } + } + + @Override + public T remove(int index) { + Cell c = find(index); + remove(c); + return c.value; + } + + @Override + public boolean isEmpty() { + return size() == 0; + } + + @Override + public T poll() { + return pollFirst(); + } + + @Override + public T pollFirst() { + if (front != null) { + T v = front.value; + remove(front); + return v; + } else { + return null; + } + } + + @Override + public T removeFirst() { + T result = pollFirst(); + + if (result == null) { + throw new NoSuchElementException(); + } else { + return result; + } + } + + @Override + public T pop() { + return removeFirst(); + } + + @Override + public T remove() { + return removeFirst(); + } + + @Override + public T pollLast() { + if (rear != null) { + T v = rear.value; + remove(rear); + return v; + } else { + throw new NoSuchElementException(); + } + } + + @Override + public T removeLast() { + T result = pollLast(); + + if (result == null) { + throw new NoSuchElementException(); + } else { + return result; + } + } + + @Override + public boolean remove(Object element) { + Cell c = find(element); + if (c == null) { + return false; + } else { + remove(c); + return true; + } + } + + @Override + public void clear() { + front = rear = null; + size = 0; + } + + @Override + public Iterator iterator() { + return listIterator(); + } + + @Override + public ListIterator listIterator() { + return listIterator(0); + } + + @Override + public ListIterator listIterator(int index) { + MyIterator it = new MyIterator(); + for (int i = 0; i < index; ++i) { + it.next(); + } + return it; + } + + @Override + public Iterator descendingIterator() { + final ListIterator li = listIterator(size()); + + return new Iterator() { + @Override + public T next() { + return li.previous(); + } + + @Override + public boolean hasNext() { + return li.hasPrevious(); + } + + @Override + public void remove() { + li.remove(); + } + }; + } + + @Override + public String toString() { + return avian.Data.toString(this); + } + + @Override + public T element() { + T result = peek(); + if (result == null) { + throw new NoSuchElementException(); + } else { + return result; + } + } + + @Override + public boolean removeFirstOccurrence(Object o) { + int index = indexOf(o); + if (index > 0) { + remove(index); + + return true; + } else { + return false; + } + } + + @Override + public boolean removeLastOccurrence(Object o) { + int lastIndex = lastIndexOf(o); + if (lastIndex > 0) { + remove(lastIndex); + + return true; + } else { + return false; + } + } + + private static class Cell { + public T value; + public Cell prev; + public Cell next; + + public Cell(T value, Cell prev, Cell next) { + this.value = value; + this.prev = prev; + this.next = next; + } + } + + private class MyIterator implements ListIterator { + private Cell toRemove; + private Cell current; + + public T previous() { + if (hasPrevious()) { + T v = current.value; + toRemove = current; + current = current.prev; + return v; + } else { + throw new NoSuchElementException(); + } + } + + public T next() { + if (hasNext()) { + if (current == null) { + current = front; + } else { + current = current.next; + } + toRemove = current; + return current.value; + } else { + throw new NoSuchElementException(); + } + } + + public boolean hasNext() { + if (current == null) { + return front != null; + } else { + return current.next != null; + } + } + + public boolean hasPrevious() { + return current != null; + } + + public void remove() { + if (toRemove != null) { + current = toRemove.prev; + LinkedList.this.remove(toRemove); + toRemove = null; + } else { + throw new IllegalStateException(); + } + } + } +} diff --git a/sgx-jvm/avian/classpath/java/util/List.java b/sgx-jvm/avian/classpath/java/util/List.java new file mode 100644 index 0000000000..a263f811bd --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/List.java @@ -0,0 +1,35 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public interface List extends Collection { + public T get(int index); + + public T set(int index, T value); + + public T remove(int index); + + public boolean add(T element); + + public void add(int index, T element); + + public boolean addAll(int startIndex, Collection c); + + public int indexOf(Object value); + + public int lastIndexOf(Object value); + + public boolean isEmpty(); + + public ListIterator listIterator(int index); + + public ListIterator listIterator(); +} diff --git a/sgx-jvm/avian/classpath/java/util/ListIterator.java b/sgx-jvm/avian/classpath/java/util/ListIterator.java new file mode 100644 index 0000000000..8259cb50aa --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/ListIterator.java @@ -0,0 +1,16 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public interface ListIterator extends Iterator { + public boolean hasPrevious(); + public E previous(); +} diff --git a/sgx-jvm/avian/classpath/java/util/Locale.java b/sgx-jvm/avian/classpath/java/util/Locale.java new file mode 100644 index 0000000000..c0ffeebff7 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/Locale.java @@ -0,0 +1,64 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public class Locale { + private static final Locale DEFAULT; + public static final Locale ENGLISH = new Locale("en", ""); + + private final String language; + private final String country; + private final String variant; + + static { + DEFAULT = new Locale(System.getProperty("user.language"), + System.getProperty("user.region")); + } + + public Locale(String language, String country, String variant) { + this.language = language; + this.country = country; + this.variant = variant; + } + + public Locale(String language, String country) { + this(language, country, ""); + } + + public Locale(String language) { + this(language, ""); + } + + public String getLanguage() { + return language; + } + + public String getCountry() { + return country; + } + + public String getVariant() { + return variant; + } + + public static Locale getDefault() { + return DEFAULT; + } + + public final String toString() { + boolean hasLanguage = language != ""; + boolean hasCountry = country != ""; + boolean hasVariant = variant != ""; + + if (!hasLanguage && !hasCountry) return ""; + return language + (hasCountry || hasVariant ? '_' + country : "") + (hasVariant ? '_' + variant : ""); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/Map.java b/sgx-jvm/avian/classpath/java/util/Map.java new file mode 100644 index 0000000000..f91b765544 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/Map.java @@ -0,0 +1,49 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public interface Map { + public boolean isEmpty(); + + public int size(); + + public boolean containsKey(Object obj); + + public boolean containsValue(Object obj); + + public V get(Object key); + + public V put(K key, V value); + + public void putAll(Map elts); + + public V remove(Object key); + + public void clear(); + + public Set> entrySet(); + + public Set keySet(); + + public Collection values(); + + public boolean equals(Object other); + + public int hashCode(); + + public interface Entry { + public K getKey(); + + public V getValue(); + + public V setValue(V value); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/MissingResourceException.java b/sgx-jvm/avian/classpath/java/util/MissingResourceException.java new file mode 100644 index 0000000000..df2f02bfee --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/MissingResourceException.java @@ -0,0 +1,30 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public class MissingResourceException extends RuntimeException { + private final String class_; + private final String key; + + public MissingResourceException(String message, String class_, String key) { + super(message); + this.class_ = class_; + this.key = key; + } + + public String getClassName() { + return class_; + } + + public String getKey() { + return key; + } +} diff --git a/sgx-jvm/avian/classpath/java/util/NavigableMap.java b/sgx-jvm/avian/classpath/java/util/NavigableMap.java new file mode 100644 index 0000000000..05136159c6 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/NavigableMap.java @@ -0,0 +1,6 @@ +package java.util; + +public interface NavigableMap extends SortedMap { + Map.Entry firstEntry(); + Map.Entry lastEntry(); +} diff --git a/sgx-jvm/avian/classpath/java/util/NoSuchElementException.java b/sgx-jvm/avian/classpath/java/util/NoSuchElementException.java new file mode 100644 index 0000000000..86c6dba6e7 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/NoSuchElementException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public class NoSuchElementException extends RuntimeException { + public NoSuchElementException(String message) { + super(message); + } + + public NoSuchElementException() { + super(); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/Objects.java b/sgx-jvm/avian/classpath/java/util/Objects.java new file mode 100644 index 0000000000..ca59ce1922 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/Objects.java @@ -0,0 +1,92 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public final class Objects { + private Objects() { + throw new AssertionError("Instantiating java.long.Objetcs is not allowed!"); + } + + public static int compare(final T x, final T y, final Comparator comparator) { + if (x == y) + return 0; + else + return comparator.compare(x, y); + } + + public static boolean deepEquals(final Object x, final Object y) { + if (x == y) + return true; + if (x == null || y == null) + return false; + if (x.getClass().isArray()) { + if(x instanceof Object[] && y instanceof Object[]) + return Arrays.deepEquals((Object[])x, (Object[])y); + if(x instanceof byte[] && y instanceof byte[]) + return Arrays.equals((byte[]) x, (byte[]) y); + if(x instanceof int[] && y instanceof int[]) + return Arrays.equals((int[]) x, (int[]) y); + if(x instanceof long[] && y instanceof long[]) + return Arrays.equals((long[]) x, (long[]) y); + if(x instanceof short[] && y instanceof short[]) + return Arrays.equals((short[]) x, (short[]) y); + if(x instanceof char[] && y instanceof char[]) + return Arrays.equals((char[]) x, (char[]) y); + if(x instanceof float[] && y instanceof float[]) + return Arrays.equals((float[]) x, (float[]) y); + if(x instanceof double[] && y instanceof double[]) + return Arrays.equals((double[]) x, (double[]) y); + } + return x.equals(y); + } + + public static boolean equals(final Object x, final Object y) { + if (x == y) + return true; + if (x != null) + return x.equals(y); + return false; + } + + public static int hash(final Object... values) { + return Arrays.hashCode(values); + } + + public static int hashCode(final Object value) { + if (value == null) + return 0; + return value.hashCode(); + } + + public static T requireNonNull(final T value) { + if (value == null) + throw new NullPointerException(); + else + return value; + } + + public static T requireNonNull(final T value, String message) { + if (value == null) + throw new NullPointerException(message); + else + return value; + } + + public static String toString(final Object value) { + return String.valueOf(value); + } + + public static String toString(final Object value, final String defaultValue) { + if (value == null) + return defaultValue; + return value.toString(); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/Observable.java b/sgx-jvm/avian/classpath/java/util/Observable.java new file mode 100644 index 0000000000..5b2d6dfc17 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/Observable.java @@ -0,0 +1,73 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public class Observable { + + private List observers = new ArrayList(); + private boolean changed = false; + + public void addObserver(Observer o) { + if(o == null) { + throw new NullPointerException(); + } + synchronized(this) { + if(!observers.contains(o)) { + observers.add(o); + } + } + } + + public synchronized int countObservers() { + return observers.size(); + } + + public void deleteObserver(Observer o) { + if(o == null) { + throw new NullPointerException(); + } + synchronized(this) { + observers.remove(o); + } + } + + public void notifyObservers() { + notifyObservers(null); + } + + public synchronized void notifyObservers(Object value) { + Observer[] obsArray = null; + synchronized(this) { + if(hasChanged()) { + clearChanged(); + obsArray = observers.toArray(new Observer[observers.size()]); + } + } + if(obsArray != null) { + for(Observer obs : obsArray) { + obs.update(this, value); + } + } + } + + public boolean hasChanged() { + return changed; + } + + protected void setChanged() { + changed = true; + } + + protected void clearChanged() { + changed = false; + } + +} \ No newline at end of file diff --git a/sgx-jvm/avian/classpath/java/util/Observer.java b/sgx-jvm/avian/classpath/java/util/Observer.java new file mode 100644 index 0000000000..f77b916219 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/Observer.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public interface Observer { + public void update(Observable o, Object arg); +} \ No newline at end of file diff --git a/sgx-jvm/avian/classpath/java/util/Properties.java b/sgx-jvm/avian/classpath/java/util/Properties.java new file mode 100644 index 0000000000..b7905b484d --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/Properties.java @@ -0,0 +1,213 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.IOException; +import java.io.Reader; + +public class Properties extends Hashtable { + public void load(InputStream in) throws IOException { + new InputStreamParser(in).parse(this); + } + + public void load(Reader reader) throws IOException { + new ReaderParser(reader).parse(this); + } + + public void store(OutputStream out, String comment) throws IOException { + PrintStream os = new PrintStream(out); + os.println("# " + comment); + for (Iterator it = entrySet().iterator(); + it.hasNext();) { + Map.Entry entry = (Map.Entry)it.next(); + os.print(entry.getKey()); + os.print('='); + os.println(entry.getValue()); + } + os.flush(); + } + + public String getProperty(String key) { + return (String)get(key); + } + + public String getProperty(String key, String defaultValue) { + String value = (String) get(key); + if (value == null) { + return defaultValue; + } + return value; + } + + public Object setProperty(String key, String value) { + return put(key, value); + } + + public Enumeration propertyNames() { + return keys(); + } + + public Set stringPropertyNames() { + return new HashSet(keySet()); + } + + private abstract static class Parser { + private StringBuilder key = null; + private StringBuilder value = null; + private StringBuilder current = null; + + private void append(int c) { + if (current == null) { + if (key == null) { + current = key = new StringBuilder(); + } else { + current = value = new StringBuilder(); + } + } + + current.append((char) c); + } + + private void finishLine(Map map) { + if (key != null) { + map.put(key.toString(), + (value == null ? "" : value.toString().trim())); + } + + key = value = current = null; + } + + abstract int readCharacter() throws IOException; + + char readUtf16() throws IOException { + char c = 0; + for (int i = 0; i < 4; ++i) { + int digit = Character.digit((char)readCharacter(), 16); + if (digit == -1) throw new IOException("Invalid Unicode escape encountered."); + c <<= 4; + c |= digit; + } + return c; + } + + void parse(Map map) + throws IOException + { + boolean escaped = false; + + int c; + while ((c = readCharacter()) != -1) { + if (c == '\\') { + if (escaped) { + escaped = false; + append(c); + } else { + escaped = true; + } + } else { + switch (c) { + case '#': + case '!': + if (key == null) { + while ((c = readCharacter()) != -1 && c != '\n'); + } else { + append(c); + } + break; + + case ' ': + case '\r': + case '\t': + if (escaped || (current != null && value == current)) { + append(c); + } else if (key == current) { + current = null; + } + break; + + case ':': + case '=': + if (escaped || (current != null && value == current)) { + append(c); + } else { + if (key == null) { + key = new StringBuilder(); + } + current = null; + } + break; + + case '\n': + if (escaped) { + append(c); + } else { + finishLine(map); + } + break; + case 'n': + if (escaped) { + append('\n'); + } else { + append(c); + } + break; + + case 'u': + if (escaped) { + append(readUtf16()); + } else { + append(c); + } break; + + default: + append(c); + break; + } + + escaped = false; + } + } + + finishLine(map); + } + } + + static class InputStreamParser extends Parser { + InputStream in; + + + public InputStreamParser(InputStream in) { + this.in = in; + } + + @Override + int readCharacter() throws IOException { + return in.read(); + } + } + + static class ReaderParser extends Parser { + Reader in; + + public ReaderParser(Reader in) { + this.in = in; + } + + @Override + int readCharacter() throws IOException { + return in.read(); + } + } + +} diff --git a/sgx-jvm/avian/classpath/java/util/PropertyResourceBundle.java b/sgx-jvm/avian/classpath/java/util/PropertyResourceBundle.java new file mode 100644 index 0000000000..d9dbff2101 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/PropertyResourceBundle.java @@ -0,0 +1,30 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +import java.io.InputStream; +import java.io.IOException; + +public class PropertyResourceBundle extends ResourceBundle { + private final Properties map = new Properties(); + + public PropertyResourceBundle(InputStream in) throws IOException { + map.load(in); + } + + public Object handleGetObject(String key) { + return map.get(key); + } + + public Enumeration getKeys() { + return map.keys(); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/Queue.java b/sgx-jvm/avian/classpath/java/util/Queue.java new file mode 100644 index 0000000000..d0f6578817 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/Queue.java @@ -0,0 +1,20 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public interface Queue extends Collection, Iterable { + public boolean add(T element); + public T element(); + public boolean offer(T element); + public T peek(); + public T poll(); + public T remove(); +} diff --git a/sgx-jvm/avian/classpath/java/util/Random.java b/sgx-jvm/avian/classpath/java/util/Random.java new file mode 100644 index 0000000000..9d244c5e0c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/Random.java @@ -0,0 +1,87 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public class Random { + private static final long Mask = 0x5DEECE66DL; + + private static final long InitialSeed = 123456789987654321L; + + private static long nextSeed = InitialSeed; + + private long seed; + + public Random(long seed) { + setSeed(seed); + } + + public Random() { + synchronized (Random.class) { + setSeed(nextSeed ^ System.currentTimeMillis()); + nextSeed *= 123456789987654321L; + if (nextSeed == 0) { + nextSeed = InitialSeed; + } + } + } + + public void setSeed(long seed) { + this.seed = (seed ^ Mask) & ((1L << 48) - 1); + } + + protected int next(int bits) { + seed = ((seed * Mask) + 0xBL) & ((1L << 48) - 1); + return (int) (seed >>> (48 - bits)); + } + + public int nextInt(int limit) { + if (limit <= 0) { + throw new IllegalArgumentException(); + } + + if ((limit & -limit) == limit) { + // limit is a power of two + return (int) ((limit * (long) next(31)) >> 31); + } + + int bits; + int value; + do { + bits = next(31); + value = bits % limit; + } while (bits - value + (limit - 1) < 0); + + return value; + } + + public int nextInt() { + return next(32); + } + + public void nextBytes(byte[] bytes) { + final int length = bytes.length; + for (int i = 0; i < length;) { + int r = nextInt(); + for (int j = Math.min(length - i, 4); j > 0; --j) { + bytes[i++] = (byte) r; + r >>= 8; + } + } + } + + public long nextLong() { + return ((long) next(32) << 32) + next(32); + } + + public double nextDouble() { + return (((long) next(26) << 27) + next(27)) / (double) (1L << 53); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/RandomAccess.java b/sgx-jvm/avian/classpath/java/util/RandomAccess.java new file mode 100644 index 0000000000..17afe39dba --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/RandomAccess.java @@ -0,0 +1,16 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public interface RandomAccess { + /* nothing added here, this interface just indicates a + * structure is efficient to access via index's directly.*/ +} diff --git a/sgx-jvm/avian/classpath/java/util/ResourceBundle.java b/sgx-jvm/avian/classpath/java/util/ResourceBundle.java new file mode 100644 index 0000000000..ccab80f876 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/ResourceBundle.java @@ -0,0 +1,122 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +import java.lang.reflect.Method; +import java.io.InputStream; +import java.io.IOException; + +public abstract class ResourceBundle { + protected String name; + protected ResourceBundle parent; + + private static String replace(char a, char b, String s) { + char[] array = new char[s.length()]; + for (int i = 0; i < array.length; ++i) { + char c = s.charAt(i); + array[i] = (c == a ? b : c); + } + return new String(array, 0, array.length, false); + } + + private static ResourceBundle findProperties(String name, ClassLoader loader, + ResourceBundle parent) + throws IOException + { + InputStream in = loader.getResourceAsStream + (replace('.', '/', name) + ".properties"); + if (in != null) { + try { + ResourceBundle r = new PropertyResourceBundle(in); + r.name = name; + r.parent = parent; + return r; + } finally { + in.close(); + } + } else { + return null; + } + } + + private static ResourceBundle find(String name, ClassLoader loader, + ResourceBundle parent) + throws Exception + { + try { + Class c = Class.forName(name, true, loader); + if (c.isAssignableFrom(ResourceBundle.class)) { + return (ResourceBundle) c.getConstructor().newInstance(); + } + } catch (ClassNotFoundException ok) { + } catch (NoSuchMethodException ok) { } + + return findProperties(name, loader, parent); + } + + public static ResourceBundle getBundle(String name, Locale locale, + ClassLoader loader) + { + try { + ResourceBundle b = find(name, loader, null); + + if (locale.getLanguage() != null) { + name = name + "_" + locale.getLanguage(); + ResourceBundle b2 = find(name, loader, b); + if (b2 != null) b = b2; + + if (locale.getCountry() != null) { + name = name + "_" + locale.getCountry(); + b2 = find(name, loader, b); + if (b2 != null) b = b2; + + if (locale.getVariant() != null) { + name = name + "_" + locale.getVariant(); + b2 = find(name, loader, b); + if (b2 != null) b = b2; + } + } + } + return b; + } catch (Exception e) { + RuntimeException re = new MissingResourceException(name, name, null); + re.initCause(e); + throw re; + } + } + + public static ResourceBundle getBundle(String name, Locale locale) { + return getBundle(name, locale, Method.getCaller().class_.loader); + } + + public static ResourceBundle getBundle(String name) { + return getBundle + (name, Locale.getDefault(), Method.getCaller().class_.loader); + } + + public Object getObject(String key) { + for (ResourceBundle b = this; b != null; b = b.parent) { + Object value = b.handleGetObject(key); + if (value != null) { + return value; + } + } + throw new MissingResourceException(key, name, key); + } + + public String getString(String key) { + return (String) getObject(key); + } + + protected abstract Object handleGetObject(String key); + + public abstract Enumeration getKeys(); +} diff --git a/sgx-jvm/avian/classpath/java/util/Set.java b/sgx-jvm/avian/classpath/java/util/Set.java new file mode 100644 index 0000000000..1dc124d0e1 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/Set.java @@ -0,0 +1,13 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public interface Set extends Collection { } diff --git a/sgx-jvm/avian/classpath/java/util/SortedMap.java b/sgx-jvm/avian/classpath/java/util/SortedMap.java new file mode 100644 index 0000000000..d7abb282ba --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/SortedMap.java @@ -0,0 +1,15 @@ +package java.util; + +public interface SortedMap extends Map { + public Comparator comparator(); + + public K firstKey(); + + public K lastKey(); + + public SortedMap headMap(K toKey); + + public SortedMap tailMap(K fromKey); + + public SortedMap subMap(K fromKey, K toKey); +} diff --git a/sgx-jvm/avian/classpath/java/util/SortedSet.java b/sgx-jvm/avian/classpath/java/util/SortedSet.java new file mode 100644 index 0000000000..26ddf8159c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/SortedSet.java @@ -0,0 +1,20 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public interface SortedSet extends Collection, Iterable, Set { + public Comparator comparator(); + public T first(); + public SortedSet headSet(T toElement); + public T last(); + public SortedSet subSet(T fromElement, T toElement); + public SortedSet tailSet(T fromElement); +} diff --git a/sgx-jvm/avian/classpath/java/util/Stack.java b/sgx-jvm/avian/classpath/java/util/Stack.java new file mode 100644 index 0000000000..9d7def967c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/Stack.java @@ -0,0 +1,30 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public class Stack extends Vector { + public boolean empty() { + return size() == 0; + } + + public T peek() { + return get(size() - 1); + } + + public T pop() { + return remove(size() - 1); + } + + public T push(T element) { + add(element); + return element; + } +} diff --git a/sgx-jvm/avian/classpath/java/util/StringTokenizer.java b/sgx-jvm/avian/classpath/java/util/StringTokenizer.java new file mode 100644 index 0000000000..e2127fce20 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/StringTokenizer.java @@ -0,0 +1,108 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public class StringTokenizer implements Enumeration { + private final String in; + private String delimiters; + private final boolean includeDelimiters; + private int position; + + public StringTokenizer(String in, String delimiters, + boolean includeDelimiters) + { + this.in = in; + this.delimiters = delimiters; + this.includeDelimiters = includeDelimiters; + } + + public StringTokenizer(String in, String delimiters) { + this(in, delimiters, false); + } + + public StringTokenizer(String in) { + this(in, " \t\r\n\f"); + } + + private boolean isDelimiter(char c) { + return delimiters.indexOf(c) >= 0; + } + + public int countTokens() { + int count = 0; + boolean sawNonDelimiter = false; + for (int i = position; i < in.length(); ++i) { + if (isDelimiter(in.charAt(i))) { + if (sawNonDelimiter) { + sawNonDelimiter = false; + ++ count; + } + + if (includeDelimiters) { + ++ count; + } + } else { + sawNonDelimiter = true; + } + } + + if (sawNonDelimiter) { + ++ count; + } + + return count; + } + + public boolean hasMoreTokens() { + for (int i = position; i < in.length(); ++i) { + if (isDelimiter(in.charAt(i))) { + if (includeDelimiters) { + return true; + } + } else { + position = i; + return true; + } + } + return false; + } + + public String nextToken() { + for (int i = position; i < in.length(); ++i) { + if (isDelimiter(in.charAt(i))) { + if (includeDelimiters) { + return in.substring(i, i + 1); + } + } else { + position = i; + while (position < in.length() && ! isDelimiter(in.charAt(position))) { + ++ position; + } + return in.substring(i, position); + } + } + throw new NoSuchElementException(); + } + + public String nextToken(String delimiters) { + this.delimiters = delimiters; + return nextToken(); + } + + public boolean hasMoreElements() { + return hasMoreTokens(); + } + + public Object nextElement() { + return nextToken(); + } + +} diff --git a/sgx-jvm/avian/classpath/java/util/TimeZone.java b/sgx-jvm/avian/classpath/java/util/TimeZone.java new file mode 100644 index 0000000000..ec933e7649 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/TimeZone.java @@ -0,0 +1,35 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public class TimeZone { + + // for now, we only support GMT + private static final TimeZone GMT = new TimeZone("GMT"); + + private final String name; + + private TimeZone(String name) { + this.name = name; + } + + public static TimeZone getTimeZone(String id) { + // technically the Java spec says that this method + // returns GMT if it can't understand the "id" parameter + // sigh. + return GMT; + } + + public String getDisplayName() { + return name; + } + +} diff --git a/sgx-jvm/avian/classpath/java/util/TreeMap.java b/sgx-jvm/avian/classpath/java/util/TreeMap.java new file mode 100644 index 0000000000..9b8792d9d6 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/TreeMap.java @@ -0,0 +1,304 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +public class TreeMap implements NavigableMap { + private final Comparator comparator; + private transient TreeSet> set; + + public TreeMap(Comparator comparator) { + this.comparator = comparator; + initializeSet(); + } + + private void initializeSet() { + final Comparator comparator = this.comparator != null ? + this.comparator : new Comparator() { + public int compare(K a, K b) { + return ((Comparable) a).compareTo(b); + } + }; + set = new TreeSet(new Comparator>() { + public int compare(MyEntry a, MyEntry b) { + return comparator.compare(a.key, b.key); + } + }); + } + + public TreeMap() { + this(null); + } + + public String toString() { + return avian.Data.toString(this); + } + + @Override + public Comparator comparator() { + return comparator; + } + + @Override + public Map.Entry firstEntry() { + return set.first(); + } + + @Override + public Map.Entry lastEntry() { + return set.last(); + } + + @Override + public K firstKey() { + return set.first().key; + } + + @Override + public K lastKey() { + return set.last().key; + } + + @Override + public SortedMap headMap(K toKey) { + // TODO - this should be implemented, the trick is making the returned SortedMap backed by this TreeSet + throw new UnsupportedOperationException(); + } + + @Override + public SortedMap tailMap(K fromKey) { + // TODO - this should be implemented, the trick is making the returned SortedMap backed by this TreeSet + throw new UnsupportedOperationException(); + } + + @Override + public SortedMap subMap(K fromKey, K toKey) { + // TODO - this should be implemented, the trick is making the returned SortedMap backed by this TreeSet + throw new UnsupportedOperationException(); + } + + public V get(Object key) { + MyEntry e = set.find(new MyEntry(key, null)); + return e == null ? null : e.value; + } + + public V put(K key, V value) { + MyEntry e = set.addAndReplace(new MyEntry(key, value)); + return e == null ? null : e.value; + } + + public void putAll(Map elts) { + for (Map.Entry entry : elts.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + } + + public V remove(Object key) { + MyEntry e = set.removeAndReturn(new MyEntry(key, null)); + return e == null ? null : e.value; + } + + public void clear() { + set.clear(); + } + + public int size() { + return set.size(); + } + + public boolean isEmpty() { + return size() == 0; + } + + public boolean containsKey(Object key) { + return set.contains(new MyEntry(key, null)); + } + + private boolean equal(Object a, Object b) { + return a == null ? b == null : a.equals(b); + } + + public boolean containsValue(Object value) { + for (V v: values()) { + if (equal(v, value)) { + return true; + } + } + return false; + } + + public Set> entrySet() { + return (Set>) (Set) set; + } + + public Set keySet() { + return new KeySet(); + } + + public Collection values() { + return new Values(); + } + + private static class MyEntry implements Entry { + public final K key; + public V value; + + public MyEntry(K key, V value) { + this.key = key; + this.value = value; + } + + public K getKey() { + return key; + } + + public V getValue() { + return value; + } + + public V setValue(V value) { + V old = this.value; + this.value = value; + return old; + } + + } + + private class KeySet extends AbstractSet { + public int size() { + return TreeMap.this.size(); + } + + public boolean isEmpty() { + return TreeMap.this.isEmpty(); + } + + public boolean contains(Object key) { + return containsKey(key); + } + + public boolean add(K key) { + return set.addAndReplace(new MyEntry(key, null)) != null; + } + + public boolean addAll(Collection collection) { + boolean change = false; + for (K k: collection) if (add(k)) change = true; + return change; + } + + public boolean remove(Object key) { + return set.removeAndReturn(new MyEntry(key, null)) != null; + } + + public Object[] toArray() { + return toArray(new Object[size()]); + } + + public T[] toArray(T[] array) { + return avian.Data.toArray(this, array); + } + + public void clear() { + TreeMap.this.clear(); + } + + public Iterator iterator() { + return new avian.Data.KeyIterator(set.iterator()); + } + } + + private class Values implements Collection { + public int size() { + return TreeMap.this.size(); + } + + public boolean isEmpty() { + return TreeMap.this.isEmpty(); + } + + public boolean contains(Object value) { + return containsValue(value); + } + + public boolean containsAll(Collection c) { + if (c == null) { + throw new NullPointerException("collection is null"); + } + + Iterator it = c.iterator(); + while (it.hasNext()) { + if (! contains(it.next())) { + return false; + } + } + + return true; + } + + public boolean add(V value) { + throw new UnsupportedOperationException(); + } + + public boolean addAll(Collection collection) { + throw new UnsupportedOperationException(); + } + + public boolean remove(Object value) { + throw new UnsupportedOperationException(); + } + + public boolean removeAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public Object[] toArray() { + return toArray(new Object[size()]); + } + + public T[] toArray(T[] array) { + return avian.Data.toArray(this, array); + } + + public void clear() { + TreeMap.this.clear(); + } + + public Iterator iterator() { + return new avian.Data.ValueIterator(set.iterator()); + } + } + + public final static long serialVersionUID = 0x0cc1f63e2d256ae6l; + + private void writeObject(ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + out.writeInt(size()); + for (Entry entry : entrySet()) { + out.writeObject(entry.getKey()); + out.writeObject(entry.getValue()); + } + } + + private void readObject(ObjectInputStream in) throws IOException { + in.defaultReadObject(); + initializeSet(); + int size = in.readInt(); + for (int i = 0; i < size; i++) try { + put((K) in.readObject(), (V) in.readObject()); + } catch (ClassNotFoundException e) { + throw new IOException(e); + } + } +} diff --git a/sgx-jvm/avian/classpath/java/util/TreeSet.java b/sgx-jvm/avian/classpath/java/util/TreeSet.java new file mode 100644 index 0000000000..ccb03e3b6e --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/TreeSet.java @@ -0,0 +1,214 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +import avian.PersistentSet; +import avian.Cell; + +public class TreeSet extends AbstractSet implements Collection { + private PersistentSet> set; + + public TreeSet(final Comparator comparator) { + set = new PersistentSet(new Comparator>() { + public int compare(Cell a, Cell b) { + return comparator.compare(a.value, b.value); + } + }); + } + + public TreeSet() { + this(new Comparator() { + public int compare(T a, T b) { + return ((Comparable) a).compareTo(b); + } + }); + } + + public TreeSet(Collection collection) { + this(); + + for (T item: collection) { + add(item); + } + } + + public T first() { + if (isEmpty()) throw new NoSuchElementException(); + + return set.first().value().value; + } + + public T last() { + if (isEmpty()) throw new NoSuchElementException(); + + return set.last().value().value; + } + + public Iterator iterator() { + return new MyIterator(set.first()); + } + + public Iterator descendingIterator() { + return new MyIterator(set.last(), true); + } + + public String toString() { + return avian.Data.toString(this); + } + + public boolean add(T value) { + PersistentSet.Path> p = set.find(new Cell(value, null)); + if (p.fresh()) { + set = p.add(); + return true; + } + return false; + } + + T addAndReplace(T value) { + PersistentSet.Path> p = set.find(new Cell(value, null)); + if (p.fresh()) { + set = p.add(); + return null; + } else { + T old = p.value().value; + set = p.replaceWith(new Cell(value, null)); + return old; + } + } + + T find(T value) { + PersistentSet.Path> p = set.find(new Cell(value, null)); + return p.fresh() ? null : p.value().value; + } + + T removeAndReturn(T value) { + Cell cell = removeCell(value); + return cell == null ? null : cell.value; + } + + private Cell removeCell(Object value) { + PersistentSet.Path> p = set.find(new Cell(value, null)); + if (p.fresh()) { + return null; + } else { + Cell old = p.value(); + + if (p.value().next != null) { + set = p.replaceWith(p.value().next); + } else { + set = p.remove(); + } + + return old; + } + } + + public boolean remove(Object value) { + return removeCell(value) != null; + } + + public int size() { + return set.size(); + } + + public boolean isEmpty() { + return set.size() == 0; + } + + public boolean contains(Object value) { + return !set.find(new Cell(value, null)).fresh(); + } + + public void clear() { + set = new PersistentSet(set.comparator()); + } + + private class MyIterator implements java.util.Iterator { + private PersistentSet.Path> path; + private PersistentSet.Path> nextPath; + private Cell cell; + private Cell prevCell; + private Cell prevPrevCell; + private boolean canRemove = false; + private final boolean reversed; + + private MyIterator(PersistentSet.Path> path) { + this(path, false); + } + + private MyIterator(PersistentSet.Path> path, boolean reversed) { + this.path = path; + this.reversed = reversed; + if (path != null) { + cell = path.value(); + nextPath = nextPath(); + } + } + + private MyIterator(MyIterator start) { + path = start.path; + nextPath = start.nextPath; + cell = start.cell; + prevCell = start.prevCell; + prevPrevCell = start.prevPrevCell; + canRemove = start.canRemove; + reversed = start.reversed; + } + + public boolean hasNext() { + return cell != null || nextPath != null; + } + + public T next() { + if (cell == null) { + path = nextPath; + nextPath = nextPath(); + cell = path.value(); + } + prevPrevCell = prevCell; + prevCell = cell; + cell = cell.next; + canRemove = true; + return prevCell.value; + } + + private PersistentSet.Path nextPath() { + return reversed ? path.predecessor() : path.successor(); + } + + public void remove() { + if (! canRemove) throw new IllegalStateException(); + + if (prevPrevCell != null && prevPrevCell.next == prevCell) { + // cell to remove is not the first in the list. + prevPrevCell.next = prevCell.next; + prevCell = prevPrevCell; + } else if (prevCell.next == cell && cell != null) { + // cell to remove is the first in the list, but not the last. + set = (PersistentSet) path.replaceWith(cell); + prevCell = null; + } else { + // cell is alone in the list. + set = (PersistentSet) path.remove(); + path = nextPath; + if (path != null) { + prevCell = null; + cell = path.value(); + path = (PersistentSet.Path) set.find((Cell) cell); + nextPath = nextPath(); + } + } + + canRemove = false; + } + } +} diff --git a/sgx-jvm/avian/classpath/java/util/UUID.java b/sgx-jvm/avian/classpath/java/util/UUID.java new file mode 100644 index 0000000000..4c5be5ccac --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/UUID.java @@ -0,0 +1,55 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public class UUID { + private final byte[] data; + + private UUID(byte[] data) { + this.data = data; + } + + public static UUID randomUUID() { + byte[] array = new byte[16]; + + new Random().nextBytes(array); + + array[6] &= 0x0f; + array[6] |= 0x40; + array[8] &= 0x3f; + array[8] |= 0x80; + + return new UUID(array); + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + toHex(sb, data, 0, 4); sb.append('-'); + toHex(sb, data, 4, 2); sb.append('-'); + toHex(sb, data, 6, 2); sb.append('-'); + toHex(sb, data, 8, 2); sb.append('-'); + toHex(sb, data, 10, 6); + return sb.toString(); + } + + private static char toHex(int i) { + return (char) (i < 10 ? i + '0' : (i - 10) + 'A'); + } + + private static void toHex(StringBuilder sb, byte[] array, int offset, + int length) + { + for (int i = offset; i < offset + length; ++i) { + sb.append(toHex((array[i] >> 4) & 0xf)); + sb.append(toHex((array[i] ) & 0xf)); + } + } +} diff --git a/sgx-jvm/avian/classpath/java/util/Vector.java b/sgx-jvm/avian/classpath/java/util/Vector.java new file mode 100644 index 0000000000..3b8a9d831a --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/Vector.java @@ -0,0 +1,137 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public class Vector extends AbstractList implements java.io.Serializable, Cloneable, RandomAccess { + private final ArrayList list; + + public Vector(int capacity) { + list = new ArrayList(capacity); + } + + public Vector() { + this(0); + } + + public Vector(Collection source) { + list = new ArrayList(source); + } + + public synchronized int size() { + return list.size(); + } + + public synchronized boolean contains(Object element) { + return list.contains(element); + } + + public synchronized void add(int index, T element) { + list.add(index, element); + } + + public void insertElementAt(T element, int index) { + add(index, element); + } + + public synchronized boolean add(T element) { + return list.add(element); + } + + public synchronized boolean addAll(Collection collection) { + return list.addAll(collection); + } + + public void addElement(T element) { + add(element); + } + + public synchronized T get(int index) { + return list.get(index); + } + + public synchronized T set(int index, T value) { + return list.set(index, value); + } + + public void setElementAt(T value, int index) { + set(index, value); + } + + public T elementAt(int index) { + return get(index); + } + + public synchronized T remove(int index) { + return list.remove(index); + } + + public synchronized boolean isEmpty() { + return list.isEmpty(); + } + + public void removeElementAt(int index) { + remove(index); + } + + public synchronized void removeAllElements() { + list.clear(); + } + + public synchronized boolean remove(Object element) { + return list.remove(element); + } + + public boolean removeElement(T element) { + return remove(element); + } + + public synchronized void clear() { + list.clear(); + } + + public synchronized int indexOf(Object element) { + return list.indexOf(element); + } + + public synchronized int lastIndexOf(Object element) { + return list.lastIndexOf(element); + } + + public synchronized void copyInto(Object[] array) { + for (int i = 0; i < size(); ++i) { + array[i] = list.get(i); + } + } + + public Iterator iterator() { + return listIterator(); + } + + public ListIterator listIterator(int index) { + return new Collections.ArrayListIterator(this, index); + } + + public ListIterator listIterator() { + return listIterator(0); + } + + public Enumeration elements() { + return new Collections.IteratorEnumeration(iterator()); + } + + public synchronized Object clone() { + Vector copy = new Vector(size()); + for (T t : this) { + copy.add(t); + } + return copy; + } +} diff --git a/sgx-jvm/avian/classpath/java/util/WeakHashMap.java b/sgx-jvm/avian/classpath/java/util/WeakHashMap.java new file mode 100644 index 0000000000..6cdd19f53c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/WeakHashMap.java @@ -0,0 +1,143 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; + +public class WeakHashMap implements Map { + private final HashMap map; + private final ReferenceQueue queue; + + public WeakHashMap(int capacity) { + map = new HashMap(capacity, new MyHelper()); + queue = new ReferenceQueue(); + } + + public WeakHashMap() { + this(0); + } + + private void poll() { + for (MyCell c = (MyCell) queue.poll(); + c != null; + c = (MyCell) queue.poll()) + { + map.remove(c); + } + } + + public boolean isEmpty() { + return map.isEmpty(); + } + + public int size() { + return map.size(); + } + + public boolean containsKey(Object key) { + poll(); + return map.containsKey(key); + } + + public boolean containsValue(Object value) { + poll(); + return map.containsValue(value); + } + + public V get(Object key) { + poll(); + return map.get(key); + } + + public V put(K key, V value) { + poll(); + return map.put(key, value); + } + + public void putAll(Map elts) { + map.putAll(elts); + } + + public V remove(Object key) { + poll(); + return map.remove(key); + } + + public void clear() { + map.clear(); + } + + public Set> entrySet() { + return map.entrySet(); + } + + public Set keySet() { + return map.keySet(); + } + + public Collection values() { + return map.values(); + } + + private static class MyCell + extends WeakReference + implements HashMap.Cell + { + public V value; + public HashMap.Cell next; + public int hashCode; + + public MyCell(K key, ReferenceQueue queue, V value, + HashMap.Cell next, int hashCode) + { + super(key, queue); + this.value = value; + this.next = next; + this.hashCode = hashCode; + } + + public K getKey() { + return get(); + } + + public V getValue() { + return value; + } + + public V setValue(V value) { + V old = this.value; + this.value = value; + return old; + } + + public HashMap.Cell next() { + return next; + } + + public void setNext(HashMap.Cell next) { + this.next = next; + } + + public int hashCode() { + return hashCode; + } + } + + private class MyHelper + extends HashMap.MyHelper + { + public HashMap.Cell make(K key, V value, HashMap.Cell next) { + return new MyCell(key, queue, value, next, hash(key)); + } + } +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/BlockingDeque.java b/sgx-jvm/avian/classpath/java/util/concurrent/BlockingDeque.java new file mode 100644 index 0000000000..156d6d469c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/BlockingDeque.java @@ -0,0 +1,23 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent; + +import java.util.Deque; + +public interface BlockingDeque extends Deque, BlockingQueue { + public T takeFirst() throws InterruptedException; + + public T takeLast() throws InterruptedException; + + public T pollFirst(long timeout, TimeUnit unit) throws InterruptedException; + + public T pollLast(long timeout, TimeUnit unit) throws InterruptedException; +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/BlockingQueue.java b/sgx-jvm/avian/classpath/java/util/concurrent/BlockingQueue.java new file mode 100644 index 0000000000..e5a2479f59 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/BlockingQueue.java @@ -0,0 +1,30 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent; + +import java.util.Collection; +import java.util.Queue; + +public interface BlockingQueue extends Queue { + public void put(T e) throws InterruptedException; + + public boolean offer(T e, long timeout, TimeUnit unit) throws InterruptedException; + + public T take() throws InterruptedException; + + public T poll(long timeout, TimeUnit unit) throws InterruptedException; + + public int remainingCapacity(); + + public int drainTo(Collection c); + + public int drainTo(Collection c, int maxElements); +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/Callable.java b/sgx-jvm/avian/classpath/java/util/concurrent/Callable.java new file mode 100644 index 0000000000..8ffca41fec --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/Callable.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent; + +public interface Callable { + public T call() throws Exception; +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/CancellationException.java b/sgx-jvm/avian/classpath/java/util/concurrent/CancellationException.java new file mode 100644 index 0000000000..866ab4a7a3 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/CancellationException.java @@ -0,0 +1,23 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent; + +public class CancellationException extends IllegalStateException { + private static final long serialVersionUID = -9202173006928992231L; + + public CancellationException() { + super(); + } + + public CancellationException(String message) { + super(message); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/CompletionService.java b/sgx-jvm/avian/classpath/java/util/concurrent/CompletionService.java new file mode 100644 index 0000000000..1c671c7590 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/CompletionService.java @@ -0,0 +1,23 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent; + +public interface CompletionService { + public Future submit(Callable task); + + public Future submit(Runnable task, T result); + + public Future take() throws InterruptedException; + + public Future poll(); + + public Future poll(long timeout, TimeUnit unit) throws InterruptedException; +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/ConcurrentHashMap.java b/sgx-jvm/avian/classpath/java/util/concurrent/ConcurrentHashMap.java new file mode 100644 index 0000000000..abb0713525 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/ConcurrentHashMap.java @@ -0,0 +1,438 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent; + +import avian.Data; +import avian.PersistentSet; +import avian.PersistentSet.Path; + +import sun.misc.Unsafe; +import java.util.AbstractMap; +import java.util.Map; +import java.util.Set; +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; + +public class ConcurrentHashMap + extends AbstractMap + implements ConcurrentMap +{ + private static final Unsafe unsafe = Unsafe.getUnsafe(); + private static final long Content; + private static final Content Empty = new Content(new PersistentSet(), 0); + + static { + try { + Content = unsafe.objectFieldOffset + (ConcurrentHashMap.class.getDeclaredField("content")); + } catch (NoSuchFieldException e) { + throw new Error(e); + } + } + + private volatile Content content; + + public ConcurrentHashMap() { + content = Empty; + } + + public ConcurrentHashMap(int initialCapacity) { + this(); + } + + public ConcurrentHashMap(int initialCapacity, float loadFactor) { + this(); + } + + public ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) { + this(); + } + + public boolean isEmpty() { + return content.size == 0; + } + + public int size() { + return content.size; + } + + public boolean containsKey(Object key) { + return find(key) != null; + } + + public boolean containsValue(Object value) { + for (V v: values()) { + if (value.equals(v)) { + return true; + } + } + return false; + } + + public V get(Object key) { + Cell cell = find(key); + return cell == null ? null : cell.value; + } + + private Cell find(Object key) { + Content c = content; + + Path>> path = c.set.find(new Node(key.hashCode())); + for (Cell cell = path.value().value; + cell != null; + cell = cell.next) + { + if (key.equals(cell.key)) { + return cell; + } + } + return null; + } + + public V putIfAbsent(K key, V value) { + Cell cell = put(key, value, PutCondition.IfAbsent, null); + return cell == null ? null : cell.value; + } + + public boolean remove(K key, V value) { + Cell cell = remove(key, RemoveCondition.IfEqual, value); + return cell != null && cell.value.equals(value); + } + + public V replace(K key, V value) { + Cell cell = put(key, value, PutCondition.IfPresent, null); + return cell == null ? null : cell.value; + } + + public boolean replace(K key, V oldValue, V newValue) { + Cell cell = put(key, newValue, PutCondition.IfEqual, oldValue); + return cell != null && cell.value.equals(oldValue); + } + + public V put(K key, V value) { + Cell cell = put(key, value, PutCondition.Always, null); + return cell == null ? null : cell.value; + } + + public V remove(Object key) { + Cell cell = remove(key, RemoveCondition.Always, null); + return cell == null ? null : cell.value; + } + + private enum PutCondition { + Always() { + public boolean addIfAbsent() { return true; } + public boolean addIfPresent(V a, V b) { return true; } + }, IfAbsent() { + public boolean addIfAbsent() { return true; } + public boolean addIfPresent(V a, V b) { return false; } + }, IfPresent() { + public boolean addIfAbsent() { return false; } + public boolean addIfPresent(V a, V b) { return true; } + }, IfEqual() { + public boolean addIfAbsent() { return false; } + public boolean addIfPresent(V a, V b) { return a.equals(b); } + }; + + public boolean addIfAbsent() { throw new AssertionError(); } + public boolean addIfPresent(V a, V b) { throw new AssertionError(); } + } + + private enum RemoveCondition { + Always() { + public boolean remove(V a, V b) { return true; } + }, IfEqual() { + public boolean remove(V a, V b) { return a.equals(b); } + }; + + public boolean remove(V a, V b) { throw new AssertionError(); } + } + + private Cell put(K key, V value, PutCondition condition, V oldValue) { + Node> node = new Node(key.hashCode()); + + loop: while (true) { + node.value = null; + Content content = this.content; + Path>> path = content.set.find(node); + for (Cell cell = path.value().value; + cell != null; + cell = cell.next) + { + if (key.equals(cell.key)) { + if (! condition.addIfPresent(cell.value, oldValue)) { + return cell; + } + + Cell start = null; + Cell last = null; + for (Cell cell2 = path.value().value; + true; + cell2 = cell2.next) + { + Cell c; + c = cell2.clone(); + + if (last == null) { + last = start = c; + } else { + last.next = c; + last = c; + } + + if (cell2 == cell) { + c.value = value; + break; + } + } + + node.value = start; + if (unsafe.compareAndSwapObject + (this, Content, content, new Content + (path.replaceWith(node), content.size))) + { + return cell; + } else { + continue loop; + } + } + } + + // no mapping found -- add a new one if appropriate + if (! condition.addIfAbsent()) { + return null; + } + + node.value = new Cell(key, value, null); + if (unsafe.compareAndSwapObject + (this, Content, content, new Content + (path.fresh() ? path.add() : path.replaceWith(node), + content.size + 1))) + { + return null; + } + } + } + + public void putAll(Map map) { + for (Map.Entry e: map.entrySet()) { + put(e.getKey(), e.getValue()); + } + } + + private Cell remove(Object key, RemoveCondition condition, + V oldValue) + { + Node> node = new Node(key.hashCode()); + + loop: while (true) { + node.value = null; + Content content = this.content; + Path>> path = content.set.find(node); + for (Cell cell = path.value().value; + cell != null; + cell = cell.next) + { + if (key.equals(cell.key)) { + if (! condition.remove(cell.value, oldValue)) { + return cell; + } + + Cell start = null; + Cell last = null; + for (Cell cell2 = path.value().value; + cell2 != cell; + cell2 = cell2.next) + { + Cell c = cell2.clone(); + if (last == null) { + last = start = c; + } else { + last.next = c; + last = c; + } + } + + if (last == null) { + start = last = cell.next; + } else { + last.next = cell.next; + } + + node.value = start; + if (unsafe.compareAndSwapObject + (this, Content, content, new Content + (start == null ? path.remove() : path.replaceWith(node), + content.size - 1))) + { + return cell; + } else { + continue loop; + } + } + } + + return null; + } + } + + public void clear() { + content = Empty; + } + + public Set> entrySet() { + return new Data.EntrySet(new MyEntryMap()); + } + + public Set keySet() { + return new Data.KeySet(new MyEntryMap()); + } + + public Collection values() { + return new Data.Values(new MyEntryMap()); + } + + private class MyEntryMap implements Data.EntryMap { + public int size() { + return ConcurrentHashMap.this.size(); + } + + public Map.Entry find(Object key) { + return new MyEntry(ConcurrentHashMap.this.find(key)); + } + + public Map.Entry remove(Object key) { + return new MyEntry + (ConcurrentHashMap.this.remove(key, RemoveCondition.Always, null)); + } + + public void clear() { + ConcurrentHashMap.this.clear(); + } + + public Iterator> iterator() { + return new MyIterator(content); + } + } + + private static class Content { + private final PersistentSet>> set; + private final int size; + + public Content(PersistentSet>> set, + int size) + { + this.set = set; + this.size = size; + } + } + + private static class Cell implements Cloneable { + public final K key; + public V value; + public Cell next; + + public Cell(K key, V value, Cell next) { + this.key = key; + this.value = value; + this.next = next; + } + + public Cell clone() { + try { + return (Cell) super.clone(); + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } + } + + private static class Node implements Comparable> { + public final int key; + public T value; + + public Node(int key) { + this.key = key; + } + + public int compareTo(Node n) { + return key - n.key; + } + } + + private class MyEntry implements Map.Entry { + private final K key; + private V value; + + public MyEntry(Cell cell) { + key = cell.key; + value = cell.value; + } + + public K getKey() { + return key; + } + + public V getValue() { + return value; + } + + public V setValue(V value) { + V v = value; + this.value = value; + put(key, value); + return v; + } + } + + private class MyIterator implements Iterator> { + private final Content content; + private final Iterator>> iterator; + private Cell currentCell; + private Cell nextCell; + + public MyIterator(Content content) { + this.content = content; + this.iterator = content.set.iterator(); + hasNext(); + } + + public Map.Entry next() { + if (hasNext()) { + currentCell = nextCell; + + nextCell = nextCell.next; + + return new MyEntry(currentCell); + } else { + throw new NoSuchElementException(); + } + } + + public boolean hasNext() { + if (nextCell == null && iterator.hasNext()) { + nextCell = iterator.next().value; + } + return nextCell != null; + } + + public void remove() { + if (currentCell != null) { + ConcurrentHashMap.this.remove + (currentCell.key, RemoveCondition.Always, null); + currentCell = null; + } else { + throw new IllegalStateException(); + } + } + } +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/ConcurrentLinkedQueue.java b/sgx-jvm/avian/classpath/java/util/concurrent/ConcurrentLinkedQueue.java new file mode 100644 index 0000000000..21c64cff2c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/ConcurrentLinkedQueue.java @@ -0,0 +1,171 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent; + +import java.util.AbstractQueue; +import java.util.Collection; +import java.util.Iterator; + +import avian.Atomic; + +public class ConcurrentLinkedQueue extends AbstractQueue { + private static final long QueueHead; + private static final long QueueTail; + private static final long NodeNext; + + static { + try { + QueueHead = Atomic.getOffset + (ConcurrentLinkedQueue.class.getField("head")); + + QueueTail = Atomic.getOffset + (ConcurrentLinkedQueue.class.getField("tail")); + + NodeNext = Atomic.getOffset + (Node.class.getField("next")); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + } + + private volatile Node head = new Node(null, null); + private volatile Node tail = head; + + @Override + public void clear() { + // TODO - can we safely make this O(1)? + while (poll() != null) { } + } + + @Override + public boolean offer(T element) { + add(element); + + return true; + } + + @Override + public boolean add(T value) { + Node n = new Node(value, null); + while (true) { + Node t = tail; + Node next = tail.next; + if (t == tail) { + if (next != null) { + Atomic.compareAndSwapObject(this, QueueTail, t, next); + } else if (Atomic.compareAndSwapObject(tail, NodeNext, null, n)) { + Atomic.compareAndSwapObject(this, QueueTail, t, n); + break; + } + } + } + + return true; + } + + @Override + public T peek() { + return poll(false); + } + + @Override + public T poll() { + return poll(true); + } + + private T poll(boolean remove) { + while (true) { + Node h = head; + Node t = tail; + Node next = head.next; + + if (h == head) { + if (h == t) { + if (next != null) { + Atomic.compareAndSwapObject(this, QueueTail, t, next); + } else { + return null; + } + } else { + T value = next.value; + if ((! remove) + || Atomic.compareAndSwapObject(this, QueueHead, h, next)) + { + return value; + } + } + } + } + } + + private static class Node { + public volatile T value; + public volatile Node next; + + public Node(T value, Node next) { + this.value = value; + this.next = next; + } + } + + @Override + public int size() { + // TODO - implement + throw new UnsupportedOperationException(); + } + + @Override + public boolean isEmpty() { + return size() == 0; + } + + @Override + public boolean contains(Object element) { + // TODO - implement + throw new UnsupportedOperationException(); + } + + @Override + public boolean containsAll(Collection c) { + // TODO - implement + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(Object element) { + // TODO - implement + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(Collection c) { + // TODO - implement + throw new UnsupportedOperationException(); + } + + @Override + public Object[] toArray() { + // TODO - implement + throw new UnsupportedOperationException(); + } + + @Override + public S[] toArray(S[] array) { + // TODO - implement + throw new UnsupportedOperationException(); + } + + @Override + public Iterator iterator() { + // TODO - implement + throw new UnsupportedOperationException(); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/ConcurrentMap.java b/sgx-jvm/avian/classpath/java/util/concurrent/ConcurrentMap.java new file mode 100644 index 0000000000..9328320eaa --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/ConcurrentMap.java @@ -0,0 +1,23 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent; + +import java.util.Map; + +public interface ConcurrentMap extends Map { + public V putIfAbsent(K key, V value); + + public boolean remove(K key, V value); + + public V replace(K key, V value); + + public boolean replace(K key, V oldValue, V newValue); +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/Delayed.java b/sgx-jvm/avian/classpath/java/util/concurrent/Delayed.java new file mode 100644 index 0000000000..0c7368ee62 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/Delayed.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent; + +public interface Delayed extends Comparable { + public long getDelay(TimeUnit unit); +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/ExecutionException.java b/sgx-jvm/avian/classpath/java/util/concurrent/ExecutionException.java new file mode 100644 index 0000000000..7f6560b26e --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/ExecutionException.java @@ -0,0 +1,31 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent; + +public class ExecutionException extends Exception { + private static final long serialVersionUID = 7830266012832686185L; + + protected ExecutionException() { + super(); + } + + protected ExecutionException(String message) { + super(message); + } + + public ExecutionException(String message, Throwable cause) { + super(message, cause); + } + + public ExecutionException(Throwable cause) { + super(cause); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/Executor.java b/sgx-jvm/avian/classpath/java/util/concurrent/Executor.java new file mode 100644 index 0000000000..cd9f5d4b27 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/Executor.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent; + +public interface Executor { + public void execute(Runnable task); +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/ExecutorCompletionService.java b/sgx-jvm/avian/classpath/java/util/concurrent/ExecutorCompletionService.java new file mode 100644 index 0000000000..f08ef9960d --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/ExecutorCompletionService.java @@ -0,0 +1,73 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent; + +public class ExecutorCompletionService implements CompletionService { + private final Executor executor; + private final BlockingQueue> completionQueue; + + public ExecutorCompletionService(Executor executor) { + this(executor, new LinkedBlockingQueue>()); + } + + public ExecutorCompletionService(Executor executor, BlockingQueue> completionQueue) { + this.executor = executor; + this.completionQueue = completionQueue; + } + + @Override + public Future submit(Callable task) { + ECSFuture f = new ECSFuture(task); + + executor.execute(f); + + return f; + } + + @Override + public Future submit(Runnable task, T result) { + ECSFuture f = new ECSFuture(task, result); + + executor.execute(f); + + return f; + } + + @Override + public Future take() throws InterruptedException { + return completionQueue.take(); + } + + @Override + public Future poll() { + return completionQueue.poll(); + } + + @Override + public Future poll(long timeout, TimeUnit unit) throws InterruptedException { + return completionQueue.poll(timeout, unit); + } + + private class ECSFuture extends FutureTask implements Future { + private ECSFuture(Runnable r, T result) { + super(r, result); + } + + private ECSFuture(Callable callable) { + super(callable); + } + + @Override + protected void done() { + completionQueue.add(this); + } + } +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/ExecutorService.java b/sgx-jvm/avian/classpath/java/util/concurrent/ExecutorService.java new file mode 100644 index 0000000000..15556bff4c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/ExecutorService.java @@ -0,0 +1,41 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent; +import java.util.List; +import java.util.Collection; + +public interface ExecutorService extends Executor { + public void shutdown(); + + public List shutdownNow(); + + public boolean isShutdown(); + + public boolean isTerminated(); + + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException; + + public Future submit(Callable task); + + public Future submit(Runnable task, T result); + + public Future submit(Runnable task); + + public List> invokeAll(Collection> tasks) throws InterruptedException; + + public List> invokeAll(Collection> tasks, + long timeout, TimeUnit unit) throws InterruptedException; + + public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException; + + public T invokeAny(Collection> tasks, + long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/Executors.java b/sgx-jvm/avian/classpath/java/util/concurrent/Executors.java new file mode 100644 index 0000000000..96496b5c99 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/Executors.java @@ -0,0 +1,24 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent; + +public class Executors { + public static Callable callable(final Runnable task, final T result) { + return new Callable() { + @Override + public T call() throws Exception { + task.run(); + + return result; + } + }; + } +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/Future.java b/sgx-jvm/avian/classpath/java/util/concurrent/Future.java new file mode 100644 index 0000000000..93c219b81c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/Future.java @@ -0,0 +1,25 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent; + +public interface Future { + public boolean cancel(boolean mayInterruptIfRunning); + + public boolean isCancelled(); + + public boolean isDone(); + + public V get() throws InterruptedException, ExecutionException; + + public V get(long timeout, TimeUnit unit) throws InterruptedException, + ExecutionException, + TimeoutException; +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/FutureTask.java b/sgx-jvm/avian/classpath/java/util/concurrent/FutureTask.java new file mode 100644 index 0000000000..967d2c8a1d --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/FutureTask.java @@ -0,0 +1,169 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent; + +import java.util.concurrent.atomic.AtomicReference; + +public class FutureTask implements RunnableFuture { + private enum State { New, Canceling, Canceled, Running, Done }; + + private final AtomicReference currentState; + private final Callable callable; + private final Object notifyLock; + private volatile Thread runningThread; + private volatile T result; + private volatile Throwable failure; + + public FutureTask(final Runnable r, final T result) { + this(new Callable() { + @Override + public T call() { + r.run(); + + return result; + } + }); + } + + public FutureTask(Callable callable) { + currentState = new AtomicReference(State.New); + this.callable = callable; + notifyLock = new Object(); + runningThread = null; + result = null; + failure = null; + } + + @Override + public void run() { + if (currentState.compareAndSet(State.New, State.Running)) { + runningThread = Thread.currentThread(); + try { + result = callable.call(); + } catch (Throwable t) { + failure = t; + } finally { + if (currentState.compareAndSet(State.Running, State.Done) || + currentState.get() == State.Canceled) { + /* in either of these conditions we either were not canceled + * or we already were interrupted. The thread may or MAY NOT + * be in an interrupted status depending on when it was + * interrupted and what the callable did with the state. + */ + } else { + /* Should be in canceling state, so block forever till we are + * interrupted. If state already transitioned into canceled + * and thus thread is in interrupted status, the exception should + * throw immediately on the sleep call. + */ + try { + Thread.sleep(Long.MAX_VALUE); + } catch (InterruptedException e) { + // expected + } + } + + Thread.interrupted(); // reset interrupted status if set + handleDone(); + runningThread = null; // must be last operation + } + } + } + + private void handleDone() { + done(); + + synchronized (notifyLock) { + notifyLock.notifyAll(); + } + } + + protected void done() { + // default implementation does nothing, designed to be overridden + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + if (currentState.compareAndSet(State.New, State.Canceled)) { + handleDone(); + + return true; + } else if (mayInterruptIfRunning && + currentState.compareAndSet(State.Running, State.Canceling)) { + // handleDone will be called from running thread + try { + Thread runningThread = this.runningThread; + if (runningThread != null) { + runningThread.interrupt(); + + return true; + } + } finally { + // we can not set to canceled until interrupt status has been set + currentState.set(State.Canceled); + } + } + + return false; + } + + @Override + public boolean isCancelled() { + return currentState.get() == State.Canceled; + } + + @Override + public boolean isDone() { + return currentState.get() == State.Done || isCancelled(); + } + + @Override + public T get() throws InterruptedException, ExecutionException { + try { + return get(Long.MAX_VALUE, TimeUnit.MILLISECONDS); + } catch (TimeoutException e) { + // not possible + throw new RuntimeException(e); + } + } + + @Override + public T get(long timeout, TimeUnit unit) throws InterruptedException, + ExecutionException, + TimeoutException { + long timeoutInMillis = unit.toMillis(timeout); + long startTime = 0; + if (timeoutInMillis < Long.MAX_VALUE) { + startTime = System.currentTimeMillis(); + } + long remainingTime; + synchronized (notifyLock) { + remainingTime = timeoutInMillis; + while (! isDone() && remainingTime > 0) { + notifyLock.wait(remainingTime); + + if (timeoutInMillis < Long.MAX_VALUE) { + remainingTime = timeoutInMillis - (System.currentTimeMillis() - startTime); + } + } + } + + if (remainingTime <= 0) { + throw new TimeoutException(); + } else if (currentState.get() == State.Canceled) { + throw new CancellationException(); + } else if (failure != null) { + throw new ExecutionException(failure); + } else { + return result; + } + } +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/LinkedBlockingQueue.java b/sgx-jvm/avian/classpath/java/util/concurrent/LinkedBlockingQueue.java new file mode 100644 index 0000000000..49be16ccb6 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/LinkedBlockingQueue.java @@ -0,0 +1,302 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent; + +import java.util.AbstractQueue; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedList; + +public class LinkedBlockingQueue extends AbstractQueue + implements BlockingQueue { + private final Object collectionLock; + private final LinkedList storage; + private final int capacity; + + public LinkedBlockingQueue() { + this(Integer.MAX_VALUE); + } + + public LinkedBlockingQueue(int capacity) { + collectionLock = new Object(); + this.capacity = capacity; + storage = new LinkedList(); + } + + // should be synchronized on collectionLock before calling + private void handleRemove() { + collectionLock.notifyAll(); + } + + // should be synchronized on collectionLock before calling + private void handleAdd() { + collectionLock.notifyAll(); + } + + // should be synchronized on collectionLock before calling + private void blockTillNotFull() throws InterruptedException { + blockTillNotFull(Long.MAX_VALUE); + } + + // should be synchronized on collectionLock before calling + private void blockTillNotFull(long maxWaitInMillis) throws InterruptedException { + if (capacity > storage.size()) { + return; + } + + long startTime = 0; + if (maxWaitInMillis != Long.MAX_VALUE) { + startTime = System.currentTimeMillis(); + } + long remainingWait = maxWaitInMillis; + while (remainingWait > 0) { + collectionLock.wait(remainingWait); + + if (capacity > storage.size()) { + return; + } else if (maxWaitInMillis != Long.MAX_VALUE) { + remainingWait = maxWaitInMillis - (System.currentTimeMillis() - startTime); + } + } + } + + // should be synchronized on collectionLock before calling + private void blockTillNotEmpty() throws InterruptedException { + blockTillNotEmpty(Long.MAX_VALUE); + } + + // should be synchronized on collectionLock before calling + private void blockTillNotEmpty(long maxWaitInMillis) throws InterruptedException { + if (! storage.isEmpty()) { + return; + } + + long startTime = 0; + if (maxWaitInMillis != Long.MAX_VALUE) { + startTime = System.currentTimeMillis(); + } + long remainingWait = maxWaitInMillis; + while (remainingWait > 0) { + collectionLock.wait(remainingWait); + + if (! storage.isEmpty()) { + return; + } else if (maxWaitInMillis != Long.MAX_VALUE) { + remainingWait = maxWaitInMillis - (System.currentTimeMillis() - startTime); + } + } + } + + @Override + public boolean offer(T element) { + synchronized (collectionLock) { + if (capacity > storage.size()) { + storage.addLast(element); + + handleAdd(); + + return true; + } else { + return false; + } + } + } + + @Override + public boolean offer(T e, long timeout, TimeUnit unit) throws InterruptedException { + long timeoutInMillis = unit.toMillis(timeout); + synchronized (collectionLock) { + // block till we can add or have reached timeout + blockTillNotFull(timeoutInMillis); + + return offer(e); + } + } + + @Override + public void put(T e) throws InterruptedException { + synchronized (collectionLock) { + // block till we have space + blockTillNotFull(); + + storage.add(e); + handleAdd(); + } + } + + @Override + public boolean addAll(Collection c) { + synchronized (collectionLock) { + if (storage.size() + c.size() > capacity) { + throw new IllegalStateException("Not enough space"); + } + + if (c.isEmpty()) { + return false; + } else { + storage.addAll(c); + + return true; + } + } + } + + @Override + public T peek() { + synchronized (collectionLock) { + if (storage.isEmpty()) { + return null; + } else { + return storage.getFirst(); + } + } + } + + // should be synchronized on collectionLock before calling + private T removeFirst() { + T result = storage.removeFirst(); + handleRemove(); + + return result; + } + + @Override + public T poll() { + synchronized (collectionLock) { + if (storage.isEmpty()) { + return null; + } else { + return removeFirst(); + } + } + } + + @Override + public T poll(long timeout, TimeUnit unit) throws InterruptedException { + long timeoutInMillis = unit.toMillis(timeout); + synchronized (collectionLock) { + // block till we available or timeout + blockTillNotEmpty(timeoutInMillis); + + return poll(); + } + } + + @Override + public T take() throws InterruptedException { + synchronized (collectionLock) { + // block till we available + blockTillNotEmpty(); + + return removeFirst(); + } + } + + @Override + public int drainTo(Collection c) { + return drainTo(c, Integer.MAX_VALUE); + } + + @Override + public int drainTo(Collection c, int maxElements) { + int remainingElements = maxElements; + synchronized (collectionLock) { + while (remainingElements > 0 && ! storage.isEmpty()) { + c.add(storage.removeFirst()); + remainingElements--; + } + + if (remainingElements != maxElements) { + handleRemove(); + } + + return maxElements - remainingElements; + } + } + + @Override + public int remainingCapacity() { + synchronized (collectionLock) { + return capacity - storage.size(); + } + } + + @Override + public int size() { + synchronized (collectionLock) { + return storage.size(); + } + } + + @Override + public boolean contains(Object element) { + synchronized (collectionLock) { + return storage.contains(element); + } + } + + @Override + public boolean containsAll(Collection c) { + synchronized (collectionLock) { + return storage.containsAll(c); + } + } + + @Override + public boolean remove(Object element) { + synchronized (collectionLock) { + if (storage.remove(element)) { + handleRemove(); + return true; + } else { + return false; + } + } + } + + @Override + public boolean removeAll(Collection c) { + synchronized (collectionLock) { + if (storage.removeAll(c)) { + handleRemove(); + return true; + } else { + return false; + } + } + } + + @Override + public void clear() { + synchronized (collectionLock) { + storage.clear(); + } + } + + @Override + public Object[] toArray() { + synchronized (collectionLock) { + return storage.toArray(); + } + } + + @Override + public S[] toArray(S[] array) { + synchronized (collectionLock) { + return storage.toArray(array); + } + } + + @Override + public Iterator iterator() { + throw new UnsupportedOperationException("Not implemented yet"); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/RejectedExecutionException.java b/sgx-jvm/avian/classpath/java/util/concurrent/RejectedExecutionException.java new file mode 100644 index 0000000000..e3230c1cd1 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/RejectedExecutionException.java @@ -0,0 +1,31 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent; + +public class RejectedExecutionException extends RuntimeException { + private static final long serialVersionUID = -375805702767069545L; + + public RejectedExecutionException() { + super(); + } + + public RejectedExecutionException(String message) { + super(message); + } + + public RejectedExecutionException(String message, Throwable cause) { + super(message, cause); + } + + public RejectedExecutionException(Throwable cause) { + super(cause); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/RunnableFuture.java b/sgx-jvm/avian/classpath/java/util/concurrent/RunnableFuture.java new file mode 100644 index 0000000000..7b8b38cad6 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/RunnableFuture.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent; + +public interface RunnableFuture extends Runnable, Future { + // nothing added to interface +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/ScheduledExecutorService.java b/sgx-jvm/avian/classpath/java/util/concurrent/ScheduledExecutorService.java new file mode 100644 index 0000000000..d0cba8caed --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/ScheduledExecutorService.java @@ -0,0 +1,29 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent; + +public interface ScheduledExecutorService extends ExecutorService { + public ScheduledFuture schedule(Runnable command, + long delay, TimeUnit unit); + + public ScheduledFuture schedule(Callable callable, + long delay, TimeUnit unit); + + public ScheduledFuture scheduleAtFixedRate(Runnable command, + long initialDelay, + long period, + TimeUnit unit); + + public ScheduledFuture scheduleWithFixedDelay(Runnable command, + long initialDelay, + long delay, + TimeUnit unit); +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/ScheduledFuture.java b/sgx-jvm/avian/classpath/java/util/concurrent/ScheduledFuture.java new file mode 100644 index 0000000000..eb9ab4da01 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/ScheduledFuture.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent; + +public interface ScheduledFuture extends Delayed, Future { + +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/ThreadFactory.java b/sgx-jvm/avian/classpath/java/util/concurrent/ThreadFactory.java new file mode 100644 index 0000000000..ed7def9977 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/ThreadFactory.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent; + +public interface ThreadFactory { + public Thread newThread(Runnable r); +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/TimeUnit.java b/sgx-jvm/avian/classpath/java/util/concurrent/TimeUnit.java new file mode 100644 index 0000000000..3503d672b9 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/TimeUnit.java @@ -0,0 +1,416 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent; + +public enum TimeUnit { + NANOSECONDS { + @Override + public long toNanos(long d) { + return d; + } + + @Override + public long toMicros(long d) { + return d / NANOSECONDS_PER_MICROSECOND; + } + + @Override + public long toMillis(long d) { + return d / NANOSECONDS_PER_MILLISECOND; + } + + @Override + public long toSeconds(long d) { + return d / NANOSECONDS_PER_SECOND; + } + + @Override + public long toMinutes(long d) { + return d / NANOSECONDS_PER_MINUTE; + } + + @Override + public long toHours(long d) { + return d / NANOSECONDS_PER_HOUR; + } + + @Override + public long toDays(long d) { + return d / NANOSECONDS_PER_DAY; + } + + @Override + public long convert(long d, TimeUnit u) { + return u.toNanos(d); + } + + @Override + int excessNanos(long d, long m) { + return (int) (d - (m * NANOSECONDS_PER_MILLISECOND)); + } + }, + MICROSECONDS { + @Override + public long toNanos(long d) { + return scale(d, NANOSECONDS_PER_MICROSECOND); + } + + @Override + public long toMicros(long d) { + return d; + } + + @Override + public long toMillis(long d) { + return d / MICROSECONDS_PER_MILLISECOND; + } + + @Override + public long toSeconds(long d) { + return d / MICROSECONDS_PER_SECOND; + } + + @Override + public long toMinutes(long d) { + return d / MICROSECONDS_PER_MINUTE; + } + + @Override + public long toHours(long d) { + return d / MICROSECONDS_PER_HOUR; + } + + @Override + public long toDays(long d) { + return d / MICROSECONDS_PER_DAY; + } + + @Override + public long convert(long d, TimeUnit u) { + return u.toMicros(d); + } + + @Override + int excessNanos(long d, long m) { + return (int) ((d * NANOSECONDS_PER_MICROSECOND) - (m * NANOSECONDS_PER_MILLISECOND)); + } + }, + MILLISECONDS { + @Override + public long toNanos(long d) { + return scale(d, NANOSECONDS_PER_MILLISECOND); + } + + @Override + public long toMicros(long d) { + return scale(d, MICROSECONDS_PER_MILLISECOND); + } + + @Override + public long toMillis(long d) { + return d; + } + + @Override + public long toSeconds(long d) { + return d / MILLISECONDS_PER_SECOND; + } + + @Override + public long toMinutes(long d) { + return d / MILLISECONDS_PER_MINUTE; + } + + @Override + public long toHours(long d) { + return d / MILLISECONDS_PER_HOUR; + } + + @Override + public long toDays(long d) { + return d / MILLISECONDS_PER_DAY; + } + + @Override + public long convert(long d, TimeUnit u) { + return u.toMillis(d); + } + + @Override + int excessNanos(long d, long m) { + return 0; + } + }, + SECONDS { + @Override + public long toNanos(long d) { + return scale(d, NANOSECONDS_PER_SECOND); + } + + @Override + public long toMicros(long d) { + return scale(d, MICROSECONDS_PER_SECOND); + } + + @Override + public long toMillis(long d) { + return scale(d, MILLISECONDS_PER_SECOND); + } + + @Override + public long toSeconds(long d) { + return d; + } + + @Override + public long toMinutes(long d) { + return d / SECONDS_PER_MINUTE; + } + + @Override + public long toHours(long d) { + return d / SECONDS_PER_HOUR; + } + + @Override + public long toDays(long d) { + return d / SECONDS_PER_DAY; + } + + @Override + public long convert(long d, TimeUnit u) { + return u.toSeconds(d); + } + + @Override + int excessNanos(long d, long m) { + return 0; + } + }, + MINUTES { + @Override + public long toNanos(long d) { + return scale(d, NANOSECONDS_PER_MINUTE); + } + + @Override + public long toMicros(long d) { + return scale(d, MICROSECONDS_PER_MINUTE); + } + + @Override + public long toMillis(long d) { + return scale(d, MILLISECONDS_PER_MINUTE); + } + + @Override + public long toSeconds(long d) { + return scale(d, SECONDS_PER_MINUTE); + } + + @Override + public long toMinutes(long d) { + return d; + } + + @Override + public long toHours(long d) { + return d / MINUETS_PER_HOUR; + } + + @Override + public long toDays(long d) { + return d / MINUETS_PER_DAY; + } + + @Override + public long convert(long d, TimeUnit u) { + return u.toMinutes(d); + } + + @Override + int excessNanos(long d, long m) { + return 0; + } + }, + HOURS { + @Override + public long toNanos(long d) { + return scale(d, NANOSECONDS_PER_HOUR); + } + + @Override + public long toMicros(long d) { + return scale(d, MICROSECONDS_PER_HOUR); + } + + @Override + public long toMillis(long d) { + return scale(d, MILLISECONDS_PER_HOUR); + } + + @Override + public long toSeconds(long d) { + return scale(d, SECONDS_PER_HOUR); + } + + @Override + public long toMinutes(long d) { + return scale(d, MINUETS_PER_HOUR); + } + + @Override + public long toHours(long d) { + return d; + } + + @Override + public long toDays(long d) { + return d / HOURS_PER_DAY; + } + + @Override + public long convert(long d, TimeUnit u) { + return u.toHours(d); + } + + @Override + int excessNanos(long d, long m) { + return 0; + } + }, + DAYS { + @Override + public long toNanos(long d) { + return scale(d, NANOSECONDS_PER_DAY); + } + + @Override + public long toMicros(long d) { + return scale(d, MICROSECONDS_PER_DAY); + } + + @Override + public long toMillis(long d) { + return scale(d, MILLISECONDS_PER_DAY); + } + + @Override + public long toSeconds(long d) { + return scale(d, SECONDS_PER_DAY); + } + + @Override + public long toMinutes(long d) { + return scale(d, MINUETS_PER_DAY); + } + + @Override + public long toHours(long d) { + return scale(d, HOURS_PER_DAY); + } + + @Override + public long toDays(long d) { + return d; + } + + @Override + public long convert(long d, TimeUnit u) { + return u.toDays(d); + } + + @Override + int excessNanos(long d, long m) { + return 0; + } + }; + + private static final long NANOSECONDS_PER_MICROSECOND = 1000L; + private static final long MICROSECONDS_PER_MILLISECOND = 1000L; + private static final long MILLISECONDS_PER_SECOND = 1000L; + private static final long SECONDS_PER_MINUTE = 60; + private static final long MINUETS_PER_HOUR = 60; + private static final long HOURS_PER_DAY = 24; + + private static final long NANOSECONDS_PER_MILLISECOND = NANOSECONDS_PER_MICROSECOND * MICROSECONDS_PER_MILLISECOND; + private static final long NANOSECONDS_PER_SECOND = NANOSECONDS_PER_MILLISECOND * MILLISECONDS_PER_SECOND; + private static final long NANOSECONDS_PER_MINUTE = NANOSECONDS_PER_SECOND * SECONDS_PER_MINUTE; + private static final long NANOSECONDS_PER_HOUR = NANOSECONDS_PER_MINUTE * MINUETS_PER_HOUR; + private static final long NANOSECONDS_PER_DAY = NANOSECONDS_PER_HOUR * HOURS_PER_DAY; + + private static final long MICROSECONDS_PER_SECOND = MICROSECONDS_PER_MILLISECOND * MILLISECONDS_PER_SECOND; + private static final long MICROSECONDS_PER_MINUTE = MICROSECONDS_PER_SECOND * SECONDS_PER_MINUTE; + private static final long MICROSECONDS_PER_HOUR = MICROSECONDS_PER_MINUTE * MINUETS_PER_HOUR; + private static final long MICROSECONDS_PER_DAY = MICROSECONDS_PER_HOUR * HOURS_PER_DAY; + + private static final long MILLISECONDS_PER_MINUTE = MILLISECONDS_PER_SECOND * SECONDS_PER_MINUTE; + private static final long MILLISECONDS_PER_HOUR = MILLISECONDS_PER_MINUTE * MINUETS_PER_HOUR; + private static final long MILLISECONDS_PER_DAY = MILLISECONDS_PER_HOUR * HOURS_PER_DAY; + + private static final long SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUETS_PER_HOUR; + private static final long SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY; + + private static final long MINUETS_PER_DAY = MINUETS_PER_HOUR * HOURS_PER_DAY; + + private static long scale(long value, long conversion) { + long result = value * conversion; + if (value > 0 && result < value) { + return Long.MAX_VALUE; + } else if (value < 0 && result > value) { + return Long.MIN_VALUE; + } else { + return result; + } + } + + public abstract long convert(long sourceDuration, TimeUnit sourceUnit); + + public abstract long toNanos(long duration); + + public abstract long toMicros(long duration); + + public abstract long toMillis(long duration); + + public abstract long toSeconds(long duration); + + public abstract long toMinutes(long duration); + + public abstract long toHours(long duration); + + public abstract long toDays(long duration); + + abstract int excessNanos(long d, long m); + + public void timedWait(Object obj, long timeout) throws InterruptedException { + if (timeout > 0) { + long ms = toMillis(timeout); + int ns = excessNanos(timeout, ms); + obj.wait(ms, ns); + } + } + + public void timedJoin(Thread thread, long timeout) throws InterruptedException { + if (timeout > 0) { + long ms = toMillis(timeout); + int ns = excessNanos(timeout, ms); + thread.join(ms, ns); + } + } + + public void sleep(long timeout) throws InterruptedException { + if (timeout > 0) { + long ms = toMillis(timeout); + int ns = excessNanos(timeout, ms); + Thread.sleep(ms, ns); + } + } +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/TimeoutException.java b/sgx-jvm/avian/classpath/java/util/concurrent/TimeoutException.java new file mode 100644 index 0000000000..fcb62e5c8e --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/TimeoutException.java @@ -0,0 +1,23 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent; + +public class TimeoutException extends Exception { + private static final long serialVersionUID = 1900926677490660714L; + + public TimeoutException() { + super(); + } + + public TimeoutException(String message) { + super(message); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/atomic/AtomicBoolean.java b/sgx-jvm/avian/classpath/java/util/concurrent/atomic/AtomicBoolean.java new file mode 100644 index 0000000000..fc7253f3fa --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/atomic/AtomicBoolean.java @@ -0,0 +1,67 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent.atomic; + +public class AtomicBoolean implements java.io.Serializable { + private static final long serialVersionUID = 4654671469794556979L; + + private static final int FALSE_VALUE = 0; + private static final int TRUE_VALUE = 1; + + private final AtomicInteger value; + + public AtomicBoolean() { + this(false); + } + + public AtomicBoolean(boolean initialValue) { + value = new AtomicInteger(intValue(initialValue)); + } + + private static int intValue(boolean value) { + return value ? TRUE_VALUE : FALSE_VALUE; + } + + private static boolean booleanValue(int value) { + return value == TRUE_VALUE; + } + + public boolean get() { + return booleanValue(value.get()); + } + + public boolean compareAndSet(boolean expect, boolean update) { + return value.compareAndSet(intValue(expect), intValue(update)); + } + + public boolean weakCompareAndSet(boolean expect, boolean update) { + return value.weakCompareAndSet(intValue(expect), intValue(update)); + } + + public void set(boolean newValue) { + value.set(intValue(newValue)); + } + + public void lazySet(boolean newValue) { + value.lazySet(intValue(newValue)); + } + + public boolean getAndSet(boolean newValue) { + int intResult = value.getAndSet(intValue(newValue)); + + return booleanValue(intResult); + } + + @Override + public String toString() { + return Boolean.toString(get()); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/atomic/AtomicInteger.java b/sgx-jvm/avian/classpath/java/util/concurrent/atomic/AtomicInteger.java new file mode 100644 index 0000000000..34621f4eff --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/atomic/AtomicInteger.java @@ -0,0 +1,136 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent.atomic; + +import java.lang.reflect.Field; + +import sun.misc.Unsafe; + +public class AtomicInteger extends Number implements java.io.Serializable { + private static final long serialVersionUID = 6214790243416807050L; + + private static final Unsafe unsafe = Unsafe.getUnsafe(); + private static final long valueOffset; + + static { + try { + Field f = AtomicInteger.class.getDeclaredField("value"); + valueOffset = unsafe.objectFieldOffset(f); + } catch (NoSuchFieldException e) { + throw new Error(e); + } + } + + private volatile int value; + + public AtomicInteger() { + this(0); + } + + public AtomicInteger(int initialValue) { + this.value = initialValue; + } + + public int get() { + return value; + } + + public void set(int newValue) { + this.value = newValue; + } + + public void lazySet(int newValue) { + unsafe.putOrderedInt(this, valueOffset, newValue); + } + + public boolean compareAndSet(int expect, int update) { + return unsafe.compareAndSwapInt(this, valueOffset, expect, update); + } + + public boolean weakCompareAndSet(int expect, int update) { + return unsafe.compareAndSwapInt(this, valueOffset, expect, update); + } + + public int getAndSet(int newValue) { + while (true) { + int current = value; + if (compareAndSet(current, newValue)) { + return current; + } + } + } + + public int getAndAdd(int delta) { + while (true) { + int current = value; + int next = current + delta; + if (compareAndSet(current, next)) { + return current; + } + } + } + + public int getAndIncrement() { + return getAndAdd(1); + } + + public int getAndDecrement() { + return getAndAdd(-1); + } + + public int addAndGet(int delta) { + while (true) { + int current = value; + int next = current + delta; + if (compareAndSet(current, next)) { + return next; + } + } + } + + public int incrementAndGet() { + return addAndGet(1); + } + + public int decrementAndGet() { + return addAndGet(-1); + } + + @Override + public byte byteValue() { + return (byte)value; + } + + @Override + public short shortValue() { + return (short)value; + } + + @Override + public int intValue() { + return value; + } + + @Override + public long longValue() { + return value; + } + + @Override + public float floatValue() { + return value; + } + + @Override + public double doubleValue() { + return value; + } +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/atomic/AtomicLong.java b/sgx-jvm/avian/classpath/java/util/concurrent/atomic/AtomicLong.java new file mode 100644 index 0000000000..849ea91b7b --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/atomic/AtomicLong.java @@ -0,0 +1,136 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent.atomic; + +import java.lang.reflect.Field; + +import sun.misc.Unsafe; + +public class AtomicLong extends Number implements java.io.Serializable { + private static final long serialVersionUID = 1927816293512124184L; + + private static final Unsafe unsafe = Unsafe.getUnsafe(); + private static final long valueOffset; + + static { + try { + Field f = AtomicLong.class.getDeclaredField("value"); + valueOffset = unsafe.objectFieldOffset(f); + } catch (NoSuchFieldException e) { + throw new Error(e); + } + } + + private volatile long value; + + public AtomicLong() { + this(0); + } + + public AtomicLong(long initialValue) { + this.value = initialValue; + } + + public long get() { + return value; + } + + public void set(long newValue) { + this.value = newValue; + } + + public void lazySet(long newValue) { + unsafe.putOrderedLong(this, valueOffset, newValue); + } + + public boolean compareAndSet(long expect, long update) { + return unsafe.compareAndSwapLong(this, valueOffset, expect, update); + } + + public boolean weakCompareAndSet(long expect, long update) { + return unsafe.compareAndSwapLong(this, valueOffset, expect, update); + } + + public long getAndSet(long newValue) { + while (true) { + long current = value; + if (compareAndSet(current, newValue)) { + return current; + } + } + } + + public long getAndAdd(long delta) { + while (true) { + long current = value; + long next = current + delta; + if (compareAndSet(current, next)) { + return current; + } + } + } + + public long getAndIncrement() { + return getAndAdd(1); + } + + public long getAndDecrement() { + return getAndAdd(-1); + } + + public long addAndGet(long delta) { + while (true) { + long current = value; + long next = current + delta; + if (compareAndSet(current, next)) { + return next; + } + } + } + + public long incrementAndGet() { + return addAndGet(1); + } + + public long decrementAndGet() { + return addAndGet(-1); + } + + @Override + public byte byteValue() { + return (byte)value; + } + + @Override + public short shortValue() { + return (short)value; + } + + @Override + public int intValue() { + return (int)value; + } + + @Override + public long longValue() { + return value; + } + + @Override + public float floatValue() { + return value; + } + + @Override + public double doubleValue() { + return value; + } +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/atomic/AtomicReference.java b/sgx-jvm/avian/classpath/java/util/concurrent/atomic/AtomicReference.java new file mode 100644 index 0000000000..80361d407e --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/atomic/AtomicReference.java @@ -0,0 +1,75 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent.atomic; + +import java.lang.reflect.Field; + +import sun.misc.Unsafe; + +public class AtomicReference implements java.io.Serializable { + private static final long serialVersionUID = -1848883965231344442L; + + private static final Unsafe unsafe = Unsafe.getUnsafe(); + private static final long valueOffset; + + static { + try { + Field f = AtomicReference.class.getDeclaredField("value"); + valueOffset = unsafe.objectFieldOffset(f); + } catch (NoSuchFieldException e) { + throw new Error(e); + } + } + + private volatile T value; + + public AtomicReference() { + this(null); + } + + public AtomicReference(T initialValue) { + this.value = initialValue; + } + + public T get() { + return value; + } + + public void set(T newValue) { + value = newValue; + } + + public void lazySet(T newValue) { + unsafe.putOrderedObject(this, valueOffset, newValue); + } + + public boolean compareAndSet(T expect, T update) { + return unsafe.compareAndSwapObject(this, valueOffset, expect, update); + } + + public boolean weakCompareAndSet(T expect, T update) { + return unsafe.compareAndSwapObject(this, valueOffset, expect, update); + } + + public final T getAndSet(T newValue) { + while (true) { + T current = value; + if (compareAndSet(current, newValue)) { + return current; + } + } + } + + @Override + public String toString() { + return String.valueOf(value); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/atomic/AtomicReferenceArray.java b/sgx-jvm/avian/classpath/java/util/concurrent/atomic/AtomicReferenceArray.java new file mode 100644 index 0000000000..96f6f47586 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/atomic/AtomicReferenceArray.java @@ -0,0 +1,69 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent.atomic; + +import java.util.Arrays; + +import sun.misc.Unsafe; + +public class AtomicReferenceArray { + private static final Unsafe unsafe = Unsafe.getUnsafe(); + private static final long arrayOffset = unsafe.arrayBaseOffset(Object.class); + private static final long arrayScale = unsafe.arrayIndexScale(Object.class); + + private final Object[] array; + + public AtomicReferenceArray(int length) { + array = new Object[length]; + } + + public T get(int index) { + return (T) unsafe.getObjectVolatile + (array, arrayOffset + (index * arrayScale)); + } + + public void set(int index, T newValue) { + unsafe.putObjectVolatile + (array, arrayOffset + (index * arrayScale), newValue); + } + + public void lazySet(int index, T newValue) { + unsafe.putOrderedObject + (array, arrayOffset + (index * arrayScale), newValue); + } + + public boolean compareAndSet(int index, T expect, T update) { + return unsafe.compareAndSwapObject + (array, arrayOffset + (index * arrayScale), expect, update); + } + + public boolean weakCompareAndSet(int index, T expect, T update) { + return compareAndSet(index, expect, update); + } + + public final T getAndSet(int index, T newValue) { + while (true) { + T current = get(index); + if (compareAndSet(index, current, newValue)) { + return current; + } + } + } + + public int length() { + return array.length; + } + + @Override + public String toString() { + return Arrays.toString(array); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/locks/Condition.java b/sgx-jvm/avian/classpath/java/util/concurrent/locks/Condition.java new file mode 100644 index 0000000000..75c835f006 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/locks/Condition.java @@ -0,0 +1,24 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent.locks; + +import java.util.Date; +import java.util.concurrent.TimeUnit; + +public interface Condition { + public void await(); + public boolean await(long time, TimeUnit unit); + public long awaitNanos(long nanosTimeout); + public void awaitUninterruptibly(); + public boolean awaitUntil(Date deadline); + public void signal(); + public void signalAll(); +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/locks/Lock.java b/sgx-jvm/avian/classpath/java/util/concurrent/locks/Lock.java new file mode 100644 index 0000000000..2d21f97822 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/locks/Lock.java @@ -0,0 +1,22 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent.locks; + +import java.util.concurrent.TimeUnit; + +public interface Lock { + public void lock(); + public void lockInterruptibly() throws InterruptedException; + public Condition newCondition(); + public boolean tryLock(); + public boolean tryLock(long time, TimeUnit unit) throws InterruptedException; + public void unlock(); +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/locks/LockSupport.java b/sgx-jvm/avian/classpath/java/util/concurrent/locks/LockSupport.java new file mode 100644 index 0000000000..7276b75afd --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/locks/LockSupport.java @@ -0,0 +1,85 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent.locks; + +import sun.misc.Unsafe; + +public class LockSupport { + private LockSupport() { + // can't construct + } + + private static final Unsafe unsafe; + private static final long parkBlockerOffset; + + static { + unsafe = Unsafe.getUnsafe(); + try { + parkBlockerOffset = unsafe.objectFieldOffset(Thread.class.getDeclaredField("parkBlocker")); + } catch (Exception ex) { + throw new Error(ex); + } + } + + public static void unpark(Thread thread) { + if (thread != null) { + unsafe.unpark(thread); + } + } + + public static void park(Object blocker) { + doParkNanos(blocker, 0L); + } + + public static void parkNanos(Object blocker, long nanos) { + if (nanos <= 0) { + return; + } + + doParkNanos(blocker, nanos); + } + + private static void doParkNanos(Object blocker, long nanos) { + Thread t = Thread.currentThread(); + unsafe.putObject(t, parkBlockerOffset, blocker); + unsafe.park(false, nanos); + unsafe.putObject(t, parkBlockerOffset, null); + } + + public static void parkUntil(Object blocker, long deadline) { + Thread t = Thread.currentThread(); + unsafe.putObject(t, parkBlockerOffset, blocker); + unsafe.park(true, deadline); + unsafe.putObject(t, parkBlockerOffset, null); + } + + public static Object getBlocker(Thread t) { + if (t == null) { + throw new NullPointerException(); + } + + return unsafe.getObjectVolatile(t, parkBlockerOffset); + } + + public static void park() { + unsafe.park(false, 0L); + } + + public static void parkNanos(long nanos) { + if (nanos > 0) { + unsafe.park(false, nanos); + } + } + + public static void parkUntil(long deadline) { + unsafe.park(true, deadline); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/concurrent/locks/ReadWriteLock.java b/sgx-jvm/avian/classpath/java/util/concurrent/locks/ReadWriteLock.java new file mode 100644 index 0000000000..b0c1b5cae7 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/concurrent/locks/ReadWriteLock.java @@ -0,0 +1,16 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent.locks; + +public interface ReadWriteLock { + public Lock readLock(); + public Lock writeLock(); +} diff --git a/sgx-jvm/avian/classpath/java/util/jar/Attributes.java b/sgx-jvm/avian/classpath/java/util/jar/Attributes.java new file mode 100644 index 0000000000..6f4ee6cf2d --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/jar/Attributes.java @@ -0,0 +1,27 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.jar; + +public class Attributes { + public static class Name { + private final String name; + + private static final int MAX_NAME_LENGTH = 70; + + public Name(String s) { + int len = s.length(); + if (len == 0 || len > MAX_NAME_LENGTH) + throw new IllegalArgumentException(); + + name = s; + } + } +} diff --git a/sgx-jvm/avian/classpath/java/util/jar/JarEntry.java b/sgx-jvm/avian/classpath/java/util/jar/JarEntry.java new file mode 100644 index 0000000000..6cee2310a5 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/jar/JarEntry.java @@ -0,0 +1,19 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.jar; + +import java.util.zip.ZipEntry; + +public abstract class JarEntry extends ZipEntry { + public JarEntry(){ + super(null); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/jar/JarFile.java b/sgx-jvm/avian/classpath/java/util/jar/JarFile.java new file mode 100644 index 0000000000..08adc3d9c8 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/jar/JarFile.java @@ -0,0 +1,81 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.jar; + +import java.io.File; +import java.io.IOException; +import java.util.Enumeration; +import java.util.zip.ZipFile; +import java.util.zip.ZipEntry; + +public class JarFile extends ZipFile { + public JarFile(String name) throws IOException { + super(name); + } + + public JarFile(File file) throws IOException { + super(file); + } + + public Enumeration entries() { + return (Enumeration) makeEnumeration(JarEntryFactory.Instance); + } + + public JarEntry getJarEntry(String name) { + return (JarEntry) getEntry(JarEntryFactory.Instance, name); + } + + private static class MyJarEntry extends JarEntry implements MyEntry { + public final Window window; + public final int pointer; + + public MyJarEntry(Window window, int pointer) { + this.window = window; + this.pointer = pointer; + } + + public String getName() { + try { + return entryName(window, pointer); + } catch (IOException e) { + return null; + } + } + + public long getCompressedSize() { + try { + return compressedSize(window, pointer); + } catch (IOException e) { + return 0; + } + } + + public long getSize() { + try { + return uncompressedSize(window, pointer); + } catch (IOException e) { + return 0; + } + } + + public int pointer() { + return pointer; + } + } + + private static class JarEntryFactory implements EntryFactory { + public static final JarEntryFactory Instance = new JarEntryFactory(); + + public ZipEntry makeEntry(Window window, int pointer) { + return new MyJarEntry(window, pointer); + } + } +} diff --git a/sgx-jvm/avian/classpath/java/util/logging/Handler.java b/sgx-jvm/avian/classpath/java/util/logging/Handler.java new file mode 100644 index 0000000000..2e2fc41cb6 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/logging/Handler.java @@ -0,0 +1,16 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.logging; + +public class Handler { + public void publish(LogRecord r) { + } +} diff --git a/sgx-jvm/avian/classpath/java/util/logging/Level.java b/sgx-jvm/avian/classpath/java/util/logging/Level.java new file mode 100644 index 0000000000..e301ab7a32 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/logging/Level.java @@ -0,0 +1,40 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.logging; + +public class Level { + public static final Level FINEST = new Level("FINEST", 300); + public static final Level FINER = new Level("FINER", 400); + public static final Level FINE = new Level("FINE", 500); + public static final Level INFO = new Level("INFO", 800); + public static final Level WARNING = new Level("WARNING", 900); + public static final Level SEVERE = new Level("SEVERE", 1000); + + private final int value; + private final String name; + + private Level(String name, int value) { + this.name = name; + this.value = value; + } + + public int intValue() { + return value; + } + + public String getName() { + return name; + } + + public String toString() { + return name; + } +} diff --git a/sgx-jvm/avian/classpath/java/util/logging/LogRecord.java b/sgx-jvm/avian/classpath/java/util/logging/LogRecord.java new file mode 100644 index 0000000000..23d4d536bc --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/logging/LogRecord.java @@ -0,0 +1,48 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.logging; + +public class LogRecord { + private final String loggerName; + private final String message; + private final Throwable thrown; + private final Level level; + private final String methodName; + + LogRecord(String loggerName, String methodName, Level level, String message, + Throwable thrown) { + this.loggerName = loggerName; + this.message = message; + this.thrown = thrown; + this.level = level; + this.methodName = methodName; + } + + public String getLoggerName() { + return loggerName; + } + + public String getMessage() { + return message; + } + + public Throwable getThrown() { + return thrown; + } + + public Level getLevel() { + return level; + } + + public String getSourceMethodName() { + return methodName; + } +} diff --git a/sgx-jvm/avian/classpath/java/util/logging/Logger.java b/sgx-jvm/avian/classpath/java/util/logging/Logger.java new file mode 100644 index 0000000000..ad710ae7ce --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/logging/Logger.java @@ -0,0 +1,232 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.logging; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +public class Logger { + private final String name; + private Level levelValue = null; + private static final ArrayList handlers; + private static Logger rootLogger; + private Logger parent; + + static { + rootLogger = new Logger(""); + rootLogger.setLevel(Level.INFO); + handlers = new ArrayList(); + handlers.add(new DefaultHandler()); + } + + public static Logger getLogger(String name) { + if (name.equals("")) return rootLogger; + Logger logger = new Logger(name); + logger.parent = rootLogger; + return logger; + } + + private Logger(String name) { + this.name = name; + } + + public Handler[] getHandlers() { + return handlers.toArray(new Handler[handlers.size()]); + } + + public void addHandler(Handler handler) { + handlers.add(handler); + } + + public void removeHandler(Handler handler) { + handlers.remove(handler); + } + + public Logger getParent() { + return parent; + } + + public void fine(String message) { + log(Level.FINE, Method.getCaller(), message, null); + } + + public void finer(String message) { + log(Level.FINER, Method.getCaller(), message, null); + } + + public void finest(String message) { + log(Level.FINEST, Method.getCaller(), message, null); + } + + public void info(String message) { + log(Level.INFO, Method.getCaller(), message, null); + } + + public void warning(String message) { + log(Level.WARNING, Method.getCaller(), message, null); + } + + public void severe(String message) { + log(Level.SEVERE, Method.getCaller(), message, null); + } + + public void log(Level level, String message) { + log(level, Method.getCaller(), message, null); + } + + public void log(Level level, String message, Throwable exception) { + log(level, Method.getCaller(), message, exception); + } + + public void log(Level level, String message, Object param) { + log(level, Method.getCaller(), replaceParameters(message, param), null); + } + + private static String replaceParameters(String message, Object... params) { + StringBuilder builder = new StringBuilder(); + int offset = 0; + for (int i = 0; i < params.length; ++i) { + int curly = message.indexOf("{}", offset); + if (curly < 0) { + break; + } + if (curly > offset) { + builder.append(message, offset, curly); + } + offset = curly + 2; + builder.append(params[i]); + } + if (message.length() > offset) { + builder.append(message, offset, message.length()); + } + return builder.toString(); + } + + public void logp(Level level, String sourceClass, String sourceMethod, String msg) { + if (!isLoggable(level)) { + return; + } + publish(new LogRecord(name, sourceMethod, level, msg, null)); + } + + public void logp(Level level, String sourceClass, String sourceMethod, + String msg, Throwable thrown) { + if (!isLoggable(level)) { + return; + } + publish(new LogRecord(name, sourceMethod, level, msg, thrown)); + } + + public Level getLevel() { + return levelValue; + } + + private Level getEffectiveLevel() { + Logger logger = this; + + while (logger.levelValue == null) { + logger = logger.getParent(); + } + return logger.getLevel(); + } + + private void log(Level level, avian.VMMethod caller, String message, + Throwable exception) { + + if (level.intValue() < getEffectiveLevel().intValue()) { + return; + } + LogRecord r = new LogRecord + (name, caller == null ? "" : Method.getName(caller), level, + message, exception); + publish(r); + } + + private void publish(LogRecord logRecord) { + for (Handler h : handlers) { + h.publish(logRecord); + } + } + + public void setLevel(Level level) { + levelValue = level; + } + + public boolean isLoggable(Level level) { + return level.intValue() >= getEffectiveLevel().intValue(); + } + + private static class DefaultHandler extends Handler { + private static final int NAME_WIDTH = 14; + private static final int METHOD_WIDTH = 15; + private static final int LEVEL_WIDTH = 8; + private final String newline; + + public DefaultHandler() { + newline = System.getProperty("line.separator"); + } + + public Object clone() { return this; } + public void close() { } + public void flush() { } + + private void maybeLogThrown(StringBuilder sb, Throwable t) { + if (t != null) { + sb.append("\nCaused by: "); + sb.append(t.getClass().getName()); + sb.append(": "); + sb.append(t.getMessage()); + sb.append(newline); + + for (StackTraceElement elt : t.getStackTrace()) { + sb.append('\t'); + sb.append(elt.getClassName()); + sb.append('.'); + sb.append(elt.getMethodName()); + sb.append("(line"); + sb.append(':'); + int lineNumber = elt.getLineNumber(); + if (lineNumber == -2) { + sb.append("unknown"); + } else if (lineNumber == -1) { + sb.append("native"); + } else { + sb.append(lineNumber); + } + sb.append(')'); + sb.append(newline); + } + maybeLogThrown(sb, t.getCause()); + } + } + + private void indent(StringBuilder sb, int amount) { + do { + sb.append(' '); + } while (--amount > 0); + } + + public void publish(LogRecord r) { + StringBuilder sb = new StringBuilder(); + sb.append(r.getLoggerName()); + indent(sb, NAME_WIDTH - r.getLoggerName().length()); + sb.append(r.getSourceMethodName()); + indent(sb, METHOD_WIDTH - r.getSourceMethodName().length()); + sb.append(r.getLevel().getName()); + indent(sb, LEVEL_WIDTH - r.getLevel().getName().length()); + sb.append(r.getMessage()); + maybeLogThrown(sb, r.getThrown()); + System.out.println(sb.toString()); + } + } + +} diff --git a/sgx-jvm/avian/classpath/java/util/regex/CharacterMatcher.java b/sgx-jvm/avian/classpath/java/util/regex/CharacterMatcher.java new file mode 100644 index 0000000000..0df1a947c6 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/regex/CharacterMatcher.java @@ -0,0 +1,332 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.regex; + +/** + * A class to match classes of characters. + *

+ * This class is intended to be the working horse behind character classes + * such as {@code [a-z]}. + *

+ * @author Johannes Schindelin + */ +class CharacterMatcher { + private boolean[] map; + private boolean inversePattern; + + public static CharacterMatcher parse(String description) { + return parse(description.toCharArray()); + } + + public static CharacterMatcher parse(char[] description) { + Parser parser = new Parser(description); + CharacterMatcher result = parser.parseClass(); + if (parser.getEndOffset() != description.length) { + throw new RuntimeException("Short character class @" + + parser.getEndOffset() + ": " + new String(description)); + } + return result; + } + + public boolean matches(char c) { + int index = c; + return (map.length > index && map[index]) ^ inversePattern; + } + + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("["); + if (inversePattern) { + builder.append("^"); + } + for (int i = 0; i < map.length; ++ i) { + if (!map[i]) { + continue; + } + builder.append(i >= ' ' && i <= 0x7f ? + "" + (char)i : ("\\x" + Integer.toHexString(i))); + int j = i + 1; + while (j < map.length && map[j]) { + ++ j; + } + -- j; + if (j > i) { + if (j > i + 1) { + builder.append('-'); + } + builder.append(j >= ' ' && j <= 0x7f ? + "" + (char)j : ("\\x" + Integer.toHexString(j))); + i = j; + } + } + builder.append("]"); + return builder.toString(); + } + + private static String specialClass(int c) { + if ('d' == c) { + return "[0-9]"; + } + if ('D' == c) { + return "[^0-9]"; + } + if ('s' == c) { + return "[ \\t\\n\\x0B\\f\\r]"; + } + if ('S' == c) { + return "[^ \\t\\n\\x0B\\f\\r]"; + } + if ('w' == c) { + return "[a-zA-Z_0-9]"; + } + if ('W' == c) { + return "[^a-zA-Z_0-9]"; + } + return null; + } + + private CharacterMatcher(boolean[] map, boolean inversePattern) { + this.map = map; + this.inversePattern = inversePattern; + } + + private void setMatch(int c) { + ensureCapacity(c + 1); + map[c] = true; + } + + private void ensureCapacity(int length) { + if (map.length >= length) { + return; + } + int size = map.length; + if (size < 32) { + size = 32; + } + while (size < length) { + size <<= 1; + } + map = java.util.Arrays.copyOf(map, size); + } + + private void merge(CharacterMatcher other) { + boolean inversePattern = this.inversePattern || other.inversePattern; + if ((map.length < other.map.length) ^ inversePattern) { + map = java.util.Arrays.copyOf(map, other.map.length); + } + for (int i = 0; i < map.length; ++ i) { + map[i] = (matches((char)i) || other.matches((char)i)) ^ inversePattern; + } + this.inversePattern = inversePattern; + } + + private void intersect(CharacterMatcher other) { + boolean inversePattern = this.inversePattern && other.inversePattern; + if ((map.length > other.map.length) ^ inversePattern) { + map = java.util.Arrays.copyOf(map, other.map.length); + } + for (int i = 0; i < map.length; ++ i) { + map[i] = (matches((char)i) && other.matches((char)i)) ^ inversePattern; + } + this.inversePattern = inversePattern; + } + + static class Parser { + private final char[] description; + private int offset; + + public Parser(char[] description) { + this.description = description; + } + + public int getEndOffset() { + return offset; + } + + /** + * Parses an escaped character. + * + * @param start the offset after the backslash + * @return the escaped character, or -1 if no character was recognized + */ + public int parseEscapedCharacter(int start) { + offset = start; + return parseEscapedCharacter(); + } + + private int parseEscapedCharacter() { + if (offset == description.length) { + throw new IllegalArgumentException("Short escaped character"); + } + char c = description[offset++]; + if (c == '0') { + int len = digits(offset, 3, 8); + if (len == 3 && description[offset] > '3') { + --len; + } + c = (char)Integer.parseInt(new String(description, offset, len), 8); + offset += len; + return c; + } + if (c == 'x' || c == 'u') { + int len = digits(offset, 4, 16); + c = (char)Integer.parseInt(new String(description, offset, len), 16); + offset += len; + return c; + } + switch (c) { + case 'a': + return 0x0007; + case 'e': + return 0x001B; + case 'f': + return 0x000C; + case 'n': + return 0x000A; + case 'r': + return 0x000D; + case 't': + return 0x0009; + case '\\': + case '.': + case '*': + case '+': + case '?': + case '|': + case '[': + case ']': + case '{': + case '}': + case '(': + case ')': + case '^': + case '$': + return c; + } + return -1; + } + + public int digits(int offset, int maxLength, int base) { + for (int i = 0; ; ++i) { + if (i == maxLength || offset + i >= description.length) { + return i; + } + int value = description[offset + i] - '0'; + if (value < 0) { + return i; + } + if (base > 10 && value >= 10) { + value += 10 - (value >= 'a' - '0' ? 'a' - '0' : 'A' - '0'); + } + if (value >= base) { + return i; + } + } + } + + public CharacterMatcher parseClass(int start) { + offset = start; + return parseClass(); + } + + public CharacterMatcher parseClass() { + if (description[offset] != '[') { + if (description[offset] == '\\') { + String range = specialClass(description[++ offset]); + if (range != null) { + ++ offset; + return CharacterMatcher.parse(range); + } + } + return null; + } + CharacterMatcher matcher = new CharacterMatcher(new boolean[0], + description[++ offset] == '^'); + if (matcher.inversePattern) { + ++ offset; + } + + int previous = -1; + boolean firstCharacter = true; + for (;;) { + if (offset >= description.length) { + unsupported("short regex"); + } + char c = description[offset++]; + if (c == '-' && !firstCharacter && description[offset] != ']') { + if (previous < 0) { + unsupported("invalid range"); + } + int rangeEnd = description[offset]; + if ('\\' == rangeEnd) { + rangeEnd = parseEscapedCharacter(); + if (rangeEnd < 0) { + unsupported("invalid range"); + } + } + matcher.ensureCapacity(rangeEnd + 1); + for (int j = previous + 1; j <= rangeEnd; j++) { + matcher.map[j] = true; + } + } else if (c == '\\') { + int saved = offset; + previous = parseEscapedCharacter(); + if (previous < 0) { + offset = saved - 1; + CharacterMatcher clazz = parseClass(); + if (clazz == null) { + unsupported("escape"); + } + matcher.merge(clazz); + } else { + matcher.setMatch(previous); + } + } else if (c == '[') { + Parser parser = new Parser(description); + CharacterMatcher other = parser.parseClass(offset - 1); + if (other == null) { + unsupported("invalid merge"); + } + matcher.merge(other); + offset = parser.getEndOffset(); + previous = -1; + } else if (c == '&') { + if (offset + 2 > description.length || description[offset] != '&' + || description[offset + 1] != '[') { + unsupported("operation"); + } + Parser parser = new Parser(description); + CharacterMatcher other = parser.parseClass(offset + 1); + if (other == null) { + unsupported("invalid intersection"); + } + matcher.intersect(other); + offset = parser.getEndOffset(); + previous = -1; + } else if (c == ']') { + break; + } else { + previous = c; + matcher.setMatch(previous); + } + firstCharacter = false; + } + + return matcher; + } + + private void unsupported(String msg) throws UnsupportedOperationException { + throw new UnsupportedOperationException("Unsupported " + msg + " @" + + offset + ": " + + new String(description, 0, description.length)); + } + } +} diff --git a/sgx-jvm/avian/classpath/java/util/regex/Compiler.java b/sgx-jvm/avian/classpath/java/util/regex/Compiler.java new file mode 100644 index 0000000000..4e495a5f80 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/regex/Compiler.java @@ -0,0 +1,533 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.regex; + +import java.util.ArrayList; +import java.util.Stack; + +/** + * Compiles regular expressions into {@link PikeVM}s. + * + * @author Johannes Schindelin + */ +class Compiler implements PikeVMOpcodes { + private final static CharacterMatcher regularCharacter = + CharacterMatcher.parse("[^\\\\.*+?|\\[\\]{}()^$]"); + + private static class Output { + private int[] program; + private int offset; + private int groupCount = -1; + private int findPreambleSize; + private ArrayList classes; + private ArrayList lookarounds; + + public Output(Expression expr) { + // try-run to determine the code size + expr.writeCode(this); + program = new int[offset]; + offset = 0; + groupCount = -1; + classes = new ArrayList(); + lookarounds = new ArrayList(); + // write it out! + expr.writeCode(this); + } + + public void add(int opcode) { + if (program != null) { + program[offset] = opcode; + } + offset++; + } + + public int markJump() { + return offset++; + } + + public void setJump(int mark) { + if (program != null) { + program[mark] = offset; + } + } + + public void markFindPreambleEnd() { + findPreambleSize = offset; + } + + public PikeVM toVM() { + CharacterMatcher[] classes = new CharacterMatcher[this.classes.size()]; + this.classes.toArray(classes); + PikeVM[] lookarounds = new PikeVM[this.lookarounds.size()]; + this.lookarounds.toArray(lookarounds); + return new PikeVM(program, findPreambleSize, groupCount, classes, + lookarounds); + } + + public int addClass(CharacterMatcher characterClass) { + if (program == null) { + return -1; + } + int result = classes.size(); + classes.add(characterClass); + return result; + } + + public int addLookaround(PikeVM lookaround) { + if (program == null) { + return -1; + } + int result = lookarounds.size(); + lookarounds.add(lookaround); + return result; + } + } + + private abstract class Expression { + protected abstract void writeCode(Output output); + } + + private class CharacterRange extends Expression { + private final CharacterMatcher characterClass; + + public CharacterRange(CharacterMatcher characterClass) { + this.characterClass = characterClass; + } + + protected void writeCode(Output output) { + output.add(CHARACTER_CLASS); + output.add(output.addClass(characterClass)); + } + + public String toString() { + return characterClass.toString(); + } + } + + private class Repeat extends Expression { + private Expression expr; + private int minCount, maxCount; + private boolean greedy; + + public Repeat(Expression expr, int minCount, int maxCount, boolean greedy) { + if (minCount < 0) { + throw new RuntimeException("Unexpected min count: " + minCount); + } + if (maxCount != -1) { + if (maxCount == 0) { + throw new RuntimeException("Unexpected max count: " + maxCount); + } + if (minCount > maxCount) { + throw new RuntimeException("Unexpected range: " + minCount + ", " + maxCount); + } + } + this.expr = expr; + this.minCount = minCount; + this.maxCount = maxCount; + this.greedy = greedy; + } + + protected void writeCode(Output output) { + int start = output.offset; + int splitJmp = greedy ? SPLIT_JMP : SPLIT; + int split = greedy ? SPLIT : SPLIT_JMP; + for (int i = 1; i < minCount; ++ i) { + expr.writeCode(output); + } + if (maxCount == -1) { + if (minCount > 0) { + int jump = output.offset; + expr.writeCode(output); + output.add(splitJmp); + output.add(jump); + } else { + output.add(split); + int jump = output.markJump(); + expr.writeCode(output); + output.add(splitJmp); + output.add(start + 2); + output.setJump(jump); + } + } else { + if (minCount > 0) { + expr.writeCode(output); + } + if (maxCount > minCount) { + int[] jumps = new int[maxCount - minCount]; + for (int i = 0; i < jumps.length; ++ i) { + output.add(split); + jumps[i] = output.markJump(); + expr.writeCode(output); + } + for (int jump : jumps) { + output.setJump(jump); + } + } + } + } + + public String toString() { + String qualifier = greedy ? "" : "?"; + if (minCount == 0 && maxCount < 2) { + return expr.toString() + (minCount < 0 ? "*" : "?") + qualifier; + } + if (minCount == 1 && maxCount < 0) { + return expr.toString() + "+" + qualifier; + } + return expr.toString() + "{" + minCount + "," + + (maxCount < 0 ? "" : "" + maxCount) + "}" + qualifier; + } + } + + private class Group extends Expression { + private final boolean capturing; + + private ArrayList list = new ArrayList(); + private ArrayList alternatives; + + public Group(boolean capturing, ArrayList initialList) { + this.capturing = capturing; + if (initialList != null) { + list.addAll(initialList); + } + } + + public void push(Expression expr) { + list.add(expr); + } + + public void push(final int c) { + push(new Expression() { + public void writeCode(Output output) { + output.add(c); + } + + public String toString() { + if (c >= 0) { + return "" + (char)c; + } + switch (c) { + case DOT: + return "."; + case WORD_BOUNDARY: + return "\\b"; + case NON_WORD_BOUNDARY: + return "\\B"; + case LINE_START: + return "^"; + case LINE_END: + return "$"; + default: + throw new RuntimeException("Unhandled opcode: " + c); + } + } + }); + } + + public void startAlternative() { + if (alternatives == null) { + alternatives = new ArrayList(); + } + alternatives.add(new Group(false, list)); + list.clear(); + } + + public Expression pop() { + Expression result = list.remove(list.size() - 1); + return result; + } + + protected void writeCode(Output output) { + int groupIndex = -1; + if (capturing) { + groupIndex = ++ output.groupCount; + output.add(SAVE_OFFSET); + output.add(2 * groupIndex); + } + int[] jumps = null; + if (alternatives != null) { + jumps = new int[alternatives.size()]; + int i = 0; + for (Group alternative : alternatives) { + output.add(SPLIT); + int jump = output.markJump(); + alternative.writeCode(output); + output.add(JMP); + jumps[i++] = output.markJump(); + output.setJump(jump); + } + } + for (Expression expr : list) { + expr.writeCode(output); + } + if (jumps != null) { + for (int jump : jumps) { + output.setJump(jump); + } + } + if (capturing) { + output.add(SAVE_OFFSET); + output.add(2 * groupIndex + 1); + } + } + + public String toString() { + StringBuilder builder = new StringBuilder(); + if (alternatives != null || list.size() > 1) { + builder.append('('); + if (!capturing) { + builder.append("?:"); + } + } + if (alternatives != null) { + for (Group alternative : alternatives) { + builder.append(alternative).append('|'); + } + } + for (Expression expr : list) { + builder.append(expr); + } + if (alternatives != null || list.size() > 1) { + builder.append(')'); + } + return builder.toString(); + } + } + + private class Lookaround extends Expression { + private final Group group = new Group(false, null); + private final boolean forward, negative; + + public Lookaround(boolean forward, boolean negative) { + this.forward = forward; + this.negative = negative; + } + + @Override + protected void writeCode(Output output) { + PikeVM vm = new Output(group).toVM(); + if (!forward) { + vm.reverse(); + } + output.add(forward ? + (negative ? NEGATIVE_LOOKAHEAD : LOOKAHEAD) : + (negative ? NEGATIVE_LOOKAHEAD : LOOKBEHIND)); + output.add(output.addLookaround(vm)); + } + + public String toString() { + String inner = group.toString(); + if (inner.startsWith("(?:")) { + inner = inner.substring(3); + } else { + inner += ")"; + } + return "(?=" + inner; + } + } + + private class Group0 extends Expression { + private final Group group; + + public Group0() { + group = new Group(true, null); + } + + public void writeCode(Output output) { + // find() preamble + int start = output.offset; + output.add(SPLIT_JMP); + output.add(start + 5); + output.add(DOTALL); + output.add(SPLIT); + output.add(start + 2); + output.markFindPreambleEnd(); + group.writeCode(output); + } + + public String toString() { + String inner = group.toString(); + return inner.startsWith("(?:") && inner.endsWith(")") ? + inner.substring(1, inner.length() - 1) : inner; + } + } + + private Group0 root; + private Stack groups; + + public Compiler() { + root = new Group0(); + groups = new Stack(); + groups.add(root.group); + } + + public Pattern compile(String regex) { + char[] array = regex.toCharArray(); + CharacterMatcher.Parser characterClassParser = + new CharacterMatcher.Parser(array); + for (int index = 0; index < array.length; ++ index) { + char c = array[index]; + Group current = groups.peek(); + if (regularCharacter.matches(c)) { + current.push(c); + continue; + } + switch (c) { + case '.': + current.push(DOT); + continue; + case '\\': + int unescaped = characterClassParser.parseEscapedCharacter(index + 1); + if (unescaped >= 0) { + index = characterClassParser.getEndOffset() - 1; + current.push((char)unescaped); + continue; + } + CharacterMatcher characterClass = characterClassParser.parseClass(index); + if (characterClass != null) { + index = characterClassParser.getEndOffset() - 1; + current.push(new CharacterRange(characterClass)); + continue; + } + switch (array[index + 1]) { + case 'b': + index++; + current.push(WORD_BOUNDARY); + continue; + case 'B': + index++; + current.push(NON_WORD_BOUNDARY); + continue; + } + throw new RuntimeException("Parse error @" + index + ": " + regex); + case '?': + case '*': + case '+': { + boolean greedy = true; + if (index + 1 < array.length && array[index + 1] == '?') { + greedy = false; + ++ index; + } + current.push(new Repeat(current.pop(), + c == '+' ? 1 : 0, c == '?' ? 1 : -1, greedy)); + continue; + } + case '{': { + ++ index; + int length = characterClassParser.digits(index, 8, 10); + int min = Integer.parseInt(regex.substring(index, index + length)); + int max = min; + index += length - 1; + c = index + 1 < array.length ? array[index + 1] : 0; + if (c == ',') { + ++ index; + length = characterClassParser.digits(index + 1, 8, 10); + max = length == 0 ? -1 : + Integer.parseInt(regex.substring(index + 1, index + 1 + length)); + index += length; + c = index + 1< array.length ? array[index + 1] : 0; + } + if (c != '}') { + throw new RuntimeException("Invalid quantifier @" + index + ": " + + regex); + } + ++ index; + boolean greedy = true; + if (index + 1 < array.length && array[index + 1] == '?') { + ++ index; + greedy = false; + } + current.push(new Repeat(current.pop(), min, max, greedy)); + continue; + } + case '(': { + boolean capturing = true; + if (index + 1 < array.length && array[index + 1] == '?') { + index += 2; + if (index >= array.length) { + throw new RuntimeException("Short pattern @" + index + ": " + + regex); + } + c = array[index]; + boolean lookAhead = true; + if (c == '<') { + if (++ index >= array.length) { + throw new RuntimeException("Short pattern @" + index + ": " + + regex); + } + lookAhead = false; + c = array[index]; + if (c != '=' && c != '!') { + throw new IllegalArgumentException("Named groups not supported @" + + index + ": " + regex); + } + } + switch (c) { + case ':': + capturing = false; + break; + case '!': + case '=': { + capturing = false; + Lookaround lookaround = new Lookaround(lookAhead, c == '!'); + current.push(lookaround); + groups.push(lookaround.group); + continue; + } + default: + throw new UnsupportedOperationException("Not yet supported: " + + regex.substring(index)); + } + } + current.push(groups.push(new Group(capturing, null))); + continue; + } + case ')': + if (groups.size() < 2) { + throw new RuntimeException("Invalid group close @" + index + ": " + + regex); + } + groups.pop(); + continue; + case '[': { + CharacterMatcher matcher = characterClassParser.parseClass(index); + if (matcher == null) { + throw new RuntimeException("Invalid range @" + index + ": " + regex); + } + current.push(new CharacterRange(matcher)); + index = characterClassParser.getEndOffset() - 1; + continue; + } + case '|': + current.startAlternative(); + continue; + case '^': + current.push(LINE_START); + continue; + case '$': + current.push(LINE_END); + continue; + default: + throw new RuntimeException("Parse error @" + index + ": " + regex); + } + } + if (groups.size() != 1) { + throw new IllegalArgumentException("Unclosed groups: (" + + (groups.size() - 1) + "): " + regex); + } + PikeVM vm = new Output(root).toVM(); + String plain = vm.isPlainString(); + if (plain != null) { + return new TrivialPattern(regex, plain, 0); + } + return new RegexPattern(regex, 0, vm); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/regex/Matcher.java b/sgx-jvm/avian/classpath/java/util/regex/Matcher.java new file mode 100644 index 0000000000..a192942e02 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/regex/Matcher.java @@ -0,0 +1,119 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.regex; + +/** + * This is a work in progress. + * + * @author zsombor and others + */ +public abstract class Matcher { + protected CharSequence input; + protected int start; + protected int end; + + public Matcher(CharSequence input) { + reset(input); + } + + public abstract boolean matches(); + + public boolean find() { + return find(end); + } + + public abstract boolean find(int start); + + public Matcher reset() { + return reset(input); + } + + public Matcher reset(CharSequence input) { + this.input = input; + start = 0; + end = 0; + return this; + } + + public String replaceAll(String replacement) { + return replace(replacement, Integer.MAX_VALUE); + } + + public String replaceFirst(String replacement) { + return replace(replacement, 1); + } + + protected String replace(String replacement, int limit) { + reset(); + + StringBuilder sb = null; + int index = 0; + int count = 0; + while (count < limit && index < input.length()) { + if (find(index)) { + if (sb == null) { + sb = new StringBuilder(); + } + if (start > index) { + sb.append(input.subSequence(index, start)); + } + sb.append(replacement); + index = end; + ++ count; + } else if (index == 0) { + return input.toString(); + } else { + break; + } + } + if (index < input.length()) { + sb.append(input.subSequence(index, input.length())); + } + return sb.toString(); + } + + public int start() { + return start; + } + + public int end() { + return end; + } + + public String group() { + return input.subSequence(start, end).toString(); + } + + public int start(int group) { + if (group == 0) { + return start(); + } + throw new UnsupportedOperationException(); + } + + public int end(int group) { + if (group == 0) { + return end(); + } + throw new UnsupportedOperationException(); + } + + public String group(int group) { + if (group == 0) { + return group(); + } + throw new UnsupportedOperationException(); + } + + public int groupCount() { + return 0; + } +} diff --git a/sgx-jvm/avian/classpath/java/util/regex/Pattern.java b/sgx-jvm/avian/classpath/java/util/regex/Pattern.java new file mode 100644 index 0000000000..299de7ab8e --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/regex/Pattern.java @@ -0,0 +1,89 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.regex; + +import java.util.ArrayList; +import java.util.List; + +/** + * This is a work in progress. + * + * @author zsombor and others + * + */ +public abstract class Pattern implements PikeVMOpcodes { + + public static final int UNIX_LINES = 1; + public static final int CASE_INSENSITIVE = 2; + public static final int COMMENTS = 4; + public static final int MULTILINE = 8; + public static final int LITERAL = 16; + public static final int DOTALL = 32; + public static final int UNICODE_CASE = 64; + public static final int CANON_EQ = 128; + + private final int patternFlags; + private final String pattern; + + protected Pattern(String pattern, int flags) { + this.pattern = pattern; + this.patternFlags = flags; + } + + public static Pattern compile(String regex) { + return compile(regex, 0); + } + + public static Pattern compile(String regex, int flags) { + if (flags != 0) { + throw new UnsupportedOperationException("TODO"); + } + return new Compiler().compile(regex); + } + + public int flags() { + return patternFlags; + } + + public abstract Matcher matcher(CharSequence input); + + public static boolean matches(String regex, CharSequence input) { + return Pattern.compile(regex).matcher(input).matches(); + } + + public String pattern() { + return pattern; + } + + public String[] split(CharSequence input) { + return split(input, 0); + } + + public String[] split(CharSequence input, int limit) { + if (limit <= 0) { + limit = Integer.MAX_VALUE; + } + Matcher matcher = matcher(input); + List result = new ArrayList(); + int offset = 0; + for (;;) { + if (result.size() >= limit || !matcher.find()) { + break; + } + result.add(input.subSequence(offset, matcher.start()).toString()); + offset = matcher.end(); + } + if (offset == 0 || offset < input.length()) { + result.add(input.subSequence(offset, input.length()).toString()); + } + return result.toArray(new String[result.size()]); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/regex/PikeVM.java b/sgx-jvm/avian/classpath/java/util/regex/PikeVM.java new file mode 100644 index 0000000000..b364aecaa6 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/regex/PikeVM.java @@ -0,0 +1,629 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.regex; + +/** + * A minimal implementation of a regular expression engine. + * + * @author Johannes Schindelin + */ +class PikeVM implements PikeVMOpcodes { + private final int[] program; + private final int groupCount; + private final int offsetsCount; + /* + * For find(), we do not want to anchor the match at the start offset. Our + * compiler allows this by prefixing the code with an implicit '(?:.*?)'. For + * regular matches() calls, we want to skip that code and start at {@code + * findPrefixLength} instead. + */ + private final int findPrefixLength; + private final CharacterMatcher[] classes; + private final PikeVM[] lookarounds; + private final static CharacterMatcher wordCharacter = + CharacterMatcher.parse("\\w"); + private final static CharacterMatcher lineTerminator = + CharacterMatcher.parse("[\n\r\u0085\u2028\u2029]"); + private boolean multiLine; + + public interface Result { + void set(int[] start, int[] end); + } + + protected PikeVM(int[] program, int findPrefixLength, int groupCount, + CharacterMatcher[] classes, PikeVM[] lookarounds) + { + this.program = program; + this.findPrefixLength = findPrefixLength; + this.groupCount = groupCount; + offsetsCount = 2 * groupCount + 2; + this.classes = classes; + this.lookarounds = lookarounds; + } + + /** + * The current thread states. + *

+ * The threads are identified by their program counter. The rationale: as all + * threads are executed in lock-step, i.e. for the same character in the + * string to be matched, it does not make sense for two threads to be at the + * same program counter -- they would both do exactly the same for the rest of + * the execution. + *

+ *

+ * For efficiency, the threads are kept in a linked list that actually lives + * in an array indexed by the program counter, pointing to the next thread's + * program counter, in the order of high to low priority. + *

+ *

+ * Program counters which have no thread associated thread are marked as -1. + * The program counter associated with the least-priority thread (the last one + * in the linked list) is marked as -2 to be able to tell it apart from + * unscheduled threads. + *

+ *

+ * We actually never need to have an explicit value for the priority, the + * ordering is sufficient: whenever a new thread is to be scheduled and it is + * found to be scheduled already, it was already scheduled by a + * higher-priority thread. + *

+ */ + private class ThreadQueue { + private int head, tail; + // next[pc] is 1 + the next thread's pc + private int[] next; + // offsets[pc][2 * group] is 1 + start offset + private int[][] offsets; + + public ThreadQueue() { + head = tail = -1; + next = new int[program.length + 1]; + offsets = new int[program.length + 1][]; + } + + public ThreadQueue(int startPC) { + head = tail = startPC; + next = new int[program.length + 1]; + offsets = new int[program.length + 1][]; + offsets[head] = new int[offsetsCount]; + } + + public int queueOneImmediately(ThreadQueue into) { + for (;;) { + if (head < 0) { + return -1; + } + boolean wasQueued = queueNext(head, head, into); + int pc = head; + if (head == tail) { + head = tail = -1; + } else { + head = next[pc] - 1; + next[pc] = 0; + } + offsets[pc] = null; + if (wasQueued) { + into.tail = pc; + return pc; + } + } + } + + /** + * Schedules the instruction at {@code nextPC} to be executed immediately. + *

+ * For non-matching steps (SPLIT, SAVE_STATE, etc) we need to schedule the + * corresponding program counter(s) to be handled right after this opcode, + * before advancing to the next character. + *

+ *

+ * To achieve this, we insert the program counter to-be-scheduled in the + * linked thread list at the current position, but only if it has not been + * scheduled yet: if it has, a higher-priority thread already reached that + * state. + *

+ *

+ * In contrast to {@link #queueNext(int, int, ThreadQueue)}, this method + * works on the current step's thread list. + *

+ * + * @param currentPC + * the current program counter + * @param nextPC + * the program counter to schedule + * @param copyThreadState + * whether to spawn off a new thread + * @return whether the step was queued (i.e. no thread was queued for the + * same {@code nextPC} already) + */ + public boolean queueImmediately(int currentPC, int nextPC, + boolean copyThreadState) { + if (isScheduled(nextPC)) { + return false; + } + int[] offsets = this.offsets[currentPC]; + if (copyThreadState) { + offsets = java.util.Arrays.copyOf(offsets, offsetsCount); + } + if (currentPC == tail) { + tail = nextPC; + } else { + next[nextPC] = next[currentPC]; + } + this.offsets[nextPC] = offsets; + next[currentPC] = nextPC + 1; + return true; + } + + /** + * Schedules the instruction at {@code nextPC} to be executed in the next + * step. + *

+ * This method advances the current thread to the next program counter, to + * be executed after reading the next character. + *

+ * + * @param currentPC + * the current program counter + * @param nextPC + * the program counter to schedule + * @param next + * the thread state of the next step + * @return whether the step was queued (i.e. no thread was queued for the + * same {@code nextPC} already) + */ + private boolean queueNext(int currentPC, int nextPC, ThreadQueue next) { + if (next.tail < 0) { + next.head = nextPC; + } else if (next.isScheduled(nextPC)) { + return false; + } else { + next.next[next.tail] = nextPC + 1; + } + next.offsets[nextPC] = offsets[currentPC]; + next.tail = nextPC; + return true; + } + + public void saveOffset(int pc, int index, int offset) { + offsets[pc][index] = offset + 1; + } + + public void setResult(Result result) { + // copy offsets + int[] offsets = this.offsets[program.length]; + int[] groupStart = new int[groupCount + 1]; + int[] groupEnd = new int[groupCount + 1]; + for (int j = 0; j <= groupCount; ++j) { + groupStart[j] = offsets[2 * j] - 1; + groupEnd[j] = offsets[2 * j + 1] - 1; + } + result.set(groupStart, groupEnd); + } + + private void mustStartMatchAt(int start) { + int previous = -1; + for (int pc = head; pc >= 0; ) { + int nextPC = next[pc] - 1; + if (start + 1 == offsets[pc][0]) { + previous = pc; + } else { + next[pc] = 0; + offsets[pc] = null; + if (pc == tail) { + head = tail = -1; + } else if (previous < 0) { + head = nextPC; + } else { + next[previous] = 1 + nextPC; + } + } + pc = nextPC; + } + } + + private int startOffset(int pc) { + return offsets[pc][0] - 1; + } + + public boolean isEmpty() { + return head < 0; + } + + public boolean isScheduled(int pc) { + return pc == tail || next[pc] > 0; + } + + public int next(int pc) { + return pc < 0 ? head : next[pc] - 1; + } + + public void clean() { + for (int pc = head; pc >= 0; ) { + int nextPC = next[pc] - 1; + next[pc] = 0; + offsets[pc] = null; + pc = nextPC; + } + head = tail = -1; + } + } + + /** + * Executes the Pike VM defined by the program. + *

+ * The idea is to execute threads in parallel, at each step executing them + * from the highest priority thread to the lowest one. In contrast to most + * regular expression engines, the Thompson/Pike one gets away with linear + * complexity because the string is matched from left to right, at each step + * executing a number of threads bounded by the length of the program: if two + * threads would execute at the same instruction pointer of the program, we + * need only consider the higher-priority one. + *

+ *

+ * This implementation is based on the description of Russ Cox. + *

+ * + * @param characters + * the {@link String} to match + * @param start + * the start offset where to match + * @param length + * the end offset + * @param anchorStart + * whether the match must start at {@code start} + * @param anchorEnd + * whether the match must start at {@code end} + * @param result + * the {@link Matcher} to store the groups' offsets in, if successful + * @return whether a match was found + */ + public boolean matches(char[] characters, int start, int end, + boolean anchorStart, boolean anchorEnd, Result result) + { + ThreadQueue current = new ThreadQueue(); + ThreadQueue next = new ThreadQueue(); + + // initialize the first thread + int startPC = anchorStart ? findPrefixLength : 0; + ThreadQueue queued = new ThreadQueue(startPC); + + boolean foundMatch = false; + int step = end > start ? +1 : -1; + for (int i = start; i != end + step; i += step) { + if (queued.isEmpty()) { + // no threads left + return foundMatch; + } + + char c = i != end ? characters[i] : 0; + int pc = -1; + for (;;) { + pc = current.next(pc); + if (pc < 0) { + pc = queued.queueOneImmediately(current); + } + if (pc < 0) { + break; + } + + // pc == program.length is a match! + if (pc == program.length) { + if (anchorEnd && i != end) { + continue; + } + if (result == null) { + // only interested in a match, no need to go on + return true; + } + current.setResult(result); + + // now that we found a match, even higher-priority matches must match + // at the same start offset + if (!anchorStart) { + next.mustStartMatchAt(current.startOffset(pc)); + } + foundMatch = true; + break; + } + + int opcode = program[pc]; + switch (opcode) { + case DOT: + if (c != '\0' && c != '\r' && c != '\n') { + current.queueNext(pc, pc + 1, next); + } + break; + case DOTALL: + current.queueNext(pc, pc + 1, next); + break; + case WORD_BOUNDARY: + case NON_WORD_BOUNDARY: { + int i2 = i - step; + int c2 = i2 < 0 || i2 >= characters.length ? -1 : characters[i2]; + switch (opcode) { + case WORD_BOUNDARY: + if ((c2 < 0 || !wordCharacter.matches((char)c2))) { + if (wordCharacter.matches(c)) { + current.queueImmediately(pc, pc + 1, false); + } + } else if (i >= 0 && i < characters.length && + !wordCharacter.matches(c)) { + current.queueImmediately(pc, pc + 1, false); + } + break; + case NON_WORD_BOUNDARY: + if ((c2 < 0 || !wordCharacter.matches((char)c2))) { + if (i >= 0 && i < characters.length && + !wordCharacter.matches(c)) { + current.queueImmediately(pc, pc + 1, false); + } + } else if (wordCharacter.matches(c)) { + current.queueImmediately(pc, pc + 1, false); + } + break; + } + break; + } + case LINE_START: + if (i == 0 || (multiLine && + lineTerminator.matches(characters[i - 1]))) { + current.queueImmediately(pc, pc + 1, false); + } + break; + case LINE_END: + if (i == characters.length || (multiLine && + lineTerminator.matches(c))) { + current.queueImmediately(pc, pc + 1, false); + } + break; + case CHARACTER_CLASS: + if (classes[program[pc + 1]].matches(c)) { + current.queueNext(pc, pc + 2, next); + } + break; + case LOOKAHEAD: + if (lookarounds[program[pc + 1]].matches(characters, + i, characters.length, true, false, null)) { + current.queueImmediately(pc, pc + 2, false); + } + break; + case LOOKBEHIND: + if (lookarounds[program[pc + 1]].matches(characters, + i - 1, -1, true, false, null)) { + current.queueImmediately(pc, pc + 2, false); + } + break; + case NEGATIVE_LOOKAHEAD: + if (!lookarounds[program[pc + 1]].matches(characters, + i, characters.length, true, false, null)) { + current.queueImmediately(pc, pc + 2, false); + } + break; + case NEGATIVE_LOOKBEHIND: + if (!lookarounds[program[pc + 1]].matches(characters, + i - 1, -1, true, false, null)) { + current.queueImmediately(pc, pc + 2, false); + } + break; + /* immediate opcodes, i.e. thread continues within the same step */ + case SAVE_OFFSET: + if (result != null) { + int index = program[pc + 1]; + current.saveOffset(pc, index, i); + } + current.queueImmediately(pc, pc + 2, false); + break; + case SPLIT: + current.queueImmediately(pc, program[pc + 1], true); + current.queueImmediately(pc, pc + 2, false); + break; + case SPLIT_JMP: + current.queueImmediately(pc, pc + 2, true); + current.queueImmediately(pc, program[pc + 1], false); + break; + case JMP: + current.queueImmediately(pc, program[pc + 1], false); + break; + default: + if (program[pc] >= 0 && program[pc] <= 0xffff) { + if (c == (char)program[pc]) { + current.queueNext(pc, pc + 1, next); + } + break; + } + throw new RuntimeException("Invalid opcode: " + opcode + + " at pc " + pc); + } + } + // clean linked thread list (and states) + current.clean(); + + // prepare for next step + ThreadQueue swap = queued; + queued = next; + next = swap; + } + return foundMatch; + } + + /** + * Determines whether this machine recognizes a pattern without special + * operators. + *

+ * In case that the regular expression is actually a plain string without any + * special operators, we can avoid using a full-blown Pike VM and instead fall + * back to using the much faster {@link TrivialPattern}. + *

+ * + * @return the string to match, or null if the machine recognizes a + * non-trivial pattern + */ + public String isPlainString() { + // we expect the machine to start with the find preamble and SAVE_OFFSET 0 + // end with SAVE_OFFSET 1 + int start = findPrefixLength; + if (start + 1 < program.length && + program[start] == SAVE_OFFSET && program[start + 1] == 0) { + start += 2; + } + int end = program.length; + if (end > start + 1 && + program[end - 2] == SAVE_OFFSET && program[end - 1] == 1) { + end -= 2; + } + for (int i = start; i < end; ++ i) { + if (program[i] < 0) { + return null; + } + } + char[] array = new char[end - start]; + for (int i = start; i < end; ++ i) { + array[i - start] = (char)program[i]; + } + return new String(array); + } + + private static int length(int opcode) { + return opcode <= SINGLE_ARG_START && opcode >= SINGLE_ARG_END ? 2 : 1; + } + + private static boolean isJump(int opcode) { + return opcode <= SPLIT && opcode >= JMP; + } + + /** + * Reverses the program (effectively matching the reverse pattern). + *

+ * It is a well-known fact that any regular expression can be reordered + * trivially into an equivalent regular expression to be applied in backward + * direction (coming in real handy for look-behind expressions). + *

+ *

+ * Example: instead of matching the sequence "aaaabb" with the pattern "a+b+", + * we can match the reverse sequence "bbaaaa" with the pattern "b+a+". + *

+ *

+ * One caveat: while the reverse pattern is equivalent in the sense that it + * matches if, and only if, the original pattern matches the forward + * direction, the same is not true for submatches. Consider the input "a" and + * the pattern "(a?)a?": when matching in forward direction the captured group + * is "a", while the backward direction will yield the empty string. For that + * reason, Java dictates that capturing groups in look-behind patterns are + * ignored. + *

+ */ + public void reverse() { + reverse(findPrefixLength, program.length); + } + + /** + * Reverses a specific part of the program (to match in reverse direction). + *

+ * This is the work-horse of {@link #reverse()}. + *

+ *

+ * To visualize the process of reversing a program, let's look at it as a + * directed graph (each jump is represented by an "X + * ", non-jumping steps are represented by a "o"s, arrows show the + * direction of the flow, SPLITs spawn two arrows): + * + *

+   * o -> X -> X -> o -> X    o -> o
+   * ^    |     \         \___^____^
+   *  \__/       \____________|
+   * 
+ * + * The concept of reversing the program is easiest explained as following: if + * we insert auxiliary nodes "Y" for jump targets, the graph looks + * like this instead: + * + *
+   * Y -> o -> X -> X -> o -> X    Y -> o -> Y -> o
+   * ^         |     \         \___^_________^
+   *  \_______/       \____________|
+   * 
+ * + * It is now obvious that reversing the program is equivalent to reversing all + * arrows, simply deleting all Xs and substituting each Y + * with a jump. Note that the reverse program will have the same number of + * JMP, but they will not be associated with the same arrows!: + * + *
+   * X <- o <- o    X <- o <- X <- o
+   * |    ^    ^____|________/
+   *  \__/ \_______/
+   * 
+ * + *

+ * @param start + * start reversing the program with this instruction + * @param end + * stop reversing at this instruction (this must be either an index + * aligned exactly with an instruction, or exactly + * {@code program.length}. + */ + private void reverse(int start, int end) { + // Pass 1: build the list of jump targets + int[] newJumps = new int[end + 1]; + boolean[] brokenArrows = new boolean[end + 1]; + for (int pc = start; pc < end; pc += length(program[pc])) { + if (isJump(program[pc])) { + int target = program[pc + 1]; + newJumps[pc + 1] = newJumps[target]; + newJumps[target] = pc + 1; + if (program[pc] == JMP) { + brokenArrows[pc + 2] = true; + } + } + } + + // Pass 2: determine mapped program counters + int[] mapping = new int[end]; + for (int pc = start, mappedPC = end; mappedPC > 0 + && pc < end; pc += length(program[pc])) { + for (int jump = newJumps[pc]; jump > 0; jump = newJumps[jump]) { + mappedPC -= 2; + } + if (!isJump(program[pc])) { + mappedPC -= length(program[pc]); + } + mapping[pc] = mappedPC; + } + + // Pass 3: write the new program + int[] reverse = new int[end]; + for (int pc = start, mappedPC = end; mappedPC > 0; + pc += length(program[pc])) { + boolean brokenArrow = brokenArrows[pc]; + for (int jump = newJumps[pc]; jump > 0; jump = newJumps[jump]) { + reverse[--mappedPC] = mapping[jump - 1]; + if (brokenArrow) { + reverse[--mappedPC] = JMP; + brokenArrow = false; + } else { + reverse[--mappedPC] = + program[jump - 1] == SPLIT_JMP ? SPLIT_JMP : SPLIT; + } + } + if (pc == end) { + break; + } + if (!isJump(program[pc])) { + for (int i = length(program[pc]); i-- > 0; ) { + reverse[--mappedPC] = program[pc + i]; + } + } + } + System.arraycopy(reverse, start, program, start, end - start); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/regex/PikeVMOpcodes.java b/sgx-jvm/avian/classpath/java/util/regex/PikeVMOpcodes.java new file mode 100644 index 0000000000..7558bf2736 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/regex/PikeVMOpcodes.java @@ -0,0 +1,45 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.regex; + +/** + * Opcodes for the Pike VM. + *

+ * See {@link PikeVM}. + *

+ * + * @author Johannes Schindelin + */ +interface PikeVMOpcodes { + final static int DOT = -1; + final static int DOTALL = -2; + + final static int WORD_BOUNDARY = -10; + final static int NON_WORD_BOUNDARY = -11; + final static int LINE_START = -12; + final static int LINE_END = -13; + + final static int CHARACTER_CLASS = -20; + + final static int LOOKAHEAD = -30; + final static int LOOKBEHIND = -31; + final static int NEGATIVE_LOOKAHEAD = -32; + final static int NEGATIVE_LOOKBEHIND = -33; + + final static int SAVE_OFFSET = -40; + + final static int SPLIT = -50; + final static int SPLIT_JMP = -51; // this split prefers to jump + final static int JMP = -52; + + final static int SINGLE_ARG_START = CHARACTER_CLASS; + final static int SINGLE_ARG_END = JMP; +} diff --git a/sgx-jvm/avian/classpath/java/util/regex/RegexMatcher.java b/sgx-jvm/avian/classpath/java/util/regex/RegexMatcher.java new file mode 100644 index 0000000000..acd4f9fa7f --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/regex/RegexMatcher.java @@ -0,0 +1,80 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.regex; + +/** + * A minimal implementation of a regular expression matcher. + * + * @author Johannes Schindelin + */ +public class RegexMatcher extends Matcher { + private final PikeVM vm; + private char[] array; + int[] groupStart, groupEnd; + + RegexMatcher(PikeVM vm, CharSequence string) { + super(string); + this.vm = vm; + } + + private final PikeVM.Result adapter = new PikeVM.Result() { + public void set(int[] start, int[] end) { + RegexMatcher.this.start = start[0]; + RegexMatcher.this.end = end[0]; + RegexMatcher.this.groupStart = start; + RegexMatcher.this.groupEnd = end; + } + }; + + public Matcher reset() { + start = end = -1; + return this; + } + + public Matcher reset(CharSequence input) { + this.input = input; + array = input.toString().toCharArray(); + return reset(); + } + + public boolean matches() { + return vm.matches(array, 0, array.length, true, true, adapter); + } + + public boolean find() { + return find(end + (start == end ? 1 : 0)); + } + + public boolean find(int offset) { + return vm.matches(array, offset, array.length, false, false, adapter); + } + + public int start(int group) { + return groupStart[group]; + } + + public int end(int group) { + return groupEnd[group]; + } + + public String group(int group) { + int offset = start(group); + if (offset < 0) { + return null; + } + int length = end(group) - offset; + return new String(array, offset, length); + } + + public int groupCount() { + return groupStart.length - 1; + } +} diff --git a/sgx-jvm/avian/classpath/java/util/regex/RegexPattern.java b/sgx-jvm/avian/classpath/java/util/regex/RegexPattern.java new file mode 100644 index 0000000000..cd7dcc582e --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/regex/RegexPattern.java @@ -0,0 +1,57 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.regex; + +/** + * A minimal implementation of a regular expression engine. + *

+ * Intended as a permissively-licensed drop-in replacement for Oracle JDK's + * regular expression engine, this class uses the Pike VM implemented in + * {@link PikeVM} to match regular expressions. + *

+ *

+ * The Pike VM not only has a nicer runtime performance than Oracle JDK's + * backtracking approach -- O(n*m) instead of O(2^m) where + * n is the length of the regular expression pattern (after normalizing + * {<n>} quantifiers) and m the length of the text to match against + * the pattern -- but also supports arbitrary-sized look-behinds. + *

+ *

+ * The current implementation supports all regular expression constructs + * supported by Oracle JDK's regular expression engine except for the following + * ones: + *

    + *
  • control characters: \cX
  • + *
  • extended character classes: \p{...}
  • + *
  • extended boundary matchers: \A,\G,\Z,\z
  • + *
  • possessive quantifiers: X?+
  • + *
  • back references: \<n>, \k<name>
  • + *
  • long escape: \Q, \E
  • + *
  • named groups: (?<name>X)
  • + *
  • flags: (?idmsuxU)
  • + *
  • independent, non-capturing group: (?>X)
  • + *
+ *

+ * + * @author Johannes Schindelin + */ +public class RegexPattern extends Pattern { + private PikeVM vm; + + public RegexMatcher matcher(CharSequence string) { + return new RegexMatcher(vm, string); + } + + RegexPattern(String regex, int flags, PikeVM vm) { + super(regex, flags); + this.vm = vm; + } +} diff --git a/sgx-jvm/avian/classpath/java/util/regex/TrivialMatcher.java b/sgx-jvm/avian/classpath/java/util/regex/TrivialMatcher.java new file mode 100644 index 0000000000..ed4360bf8c --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/regex/TrivialMatcher.java @@ -0,0 +1,48 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.regex; + +/** + * This is a work in progress. + * + * @author zsombor and others + */ +class TrivialMatcher extends Matcher { + private final String pattern; + + TrivialMatcher(String pattern, CharSequence input) { + super(input); + this.pattern = pattern; + } + + public boolean matches() { + if (pattern.equals(input.toString())) { + start = 0; + end = input.length(); + return true; + } else { + return false; + } + } + + public boolean find(int start) { + String p = pattern; + int i = TrivialPattern.indexOf(input, p, start); + if (i >= 0) { + this.start = i; + this.end = i + p.length(); + return true; + } else { + return false; + } + } +} + diff --git a/sgx-jvm/avian/classpath/java/util/regex/TrivialPattern.java b/sgx-jvm/avian/classpath/java/util/regex/TrivialPattern.java new file mode 100644 index 0000000000..5929375238 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/regex/TrivialPattern.java @@ -0,0 +1,112 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.regex; + +import java.util.Iterator; +import java.util.List; +import java.util.LinkedList; + +/** + * This is a work in progress. + * + * @author zsombor and others + * + */ +public class TrivialPattern extends Pattern { + + private final String unescaped; + + TrivialPattern(String pattern, String unescaped, int flags) { + super(pattern, flags); + this.unescaped = unescaped; + } + + public Matcher matcher(CharSequence input) { + return new TrivialMatcher(unescaped, input); + } + + public String[] split(CharSequence input, int limit) { + boolean strip; + if (limit < 0) { + strip = false; + limit = Integer.MAX_VALUE; + } else if (limit == 0) { + strip = true; + limit = Integer.MAX_VALUE; + } else { + strip = false; + } + + List list = new LinkedList(); + int index = 0; + int trailing = 0; + int patternLength = unescaped.length(); + while (index < input.length() && list.size() < limit - 1) { + int i; + if (patternLength == 0) { + if (list.size() == 0) { + i = 0; + } else { + i = index + 1; + } + } else { + i = indexOf(input, unescaped, index); + } + + if (i >= 0) { + if (patternLength != 0 && i == index) { + ++ trailing; + } else { + trailing = 0; + } + + list.add(input.subSequence(index, i)); + index = i + patternLength; + } else { + break; + } + } + + if (strip && index > 0 && index == input.length()) { + ++ trailing; + } else { + trailing = 0; + } + list.add(input.subSequence(index, input.length())); + + String[] result = new String[list.size() - trailing]; + int i = 0; + for (Iterator it = list.iterator(); + it.hasNext() && i < result.length; ++ i) + { + result[i] = it.next().toString(); + } + return result; + } + + static int indexOf(CharSequence haystack, CharSequence needle, int start) { + if (needle.length() == 0) return start; + + for (int i = start; i < haystack.length() - needle.length() + 1; ++i) { + int j = 0; + for (; j < needle.length(); ++j) { + if (haystack.charAt(i + j) != needle.charAt(j)) { + break; + } + } + if (j == needle.length()) { + return i; + } + } + + return -1; + } +} diff --git a/sgx-jvm/avian/classpath/java/util/zip/CRC32.java b/sgx-jvm/avian/classpath/java/util/zip/CRC32.java new file mode 100644 index 0000000000..cd587a4c71 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/zip/CRC32.java @@ -0,0 +1,69 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.zip; + +public class CRC32 { + private static final int Polynomial = 0x04C11DB7; + private static final int Width = 32; + private static final int Top = 1 << (Width - 1); + private static final int InitialRemainder = 0xFFFFFFFF; + private static final long ResultXor = 0xFFFFFFFFL; + + private static final int[] table = new int[256]; + + static { + for (int dividend = 0; dividend < 256; ++ dividend) { + int remainder = dividend << (Width - 8); + for (int bit = 8; bit > 0; --bit) { + remainder = ((remainder & Top) != 0) + ? (remainder << 1) ^ Polynomial + : (remainder << 1); + } + table[dividend] = remainder; + } + } + + private int remainder = InitialRemainder; + + public void reset() { + remainder = InitialRemainder; + } + + public void update(int b) { + remainder = table[reflect(b, 8) ^ (remainder >>> (Width - 8))] + ^ (remainder << 8); + } + + public void update(byte[] array, int offset, int length) { + for (int i = 0; i < length; ++i) { + update(array[offset + i] & 0xFF); + } + } + + public void update(byte[] array) { + update(array, 0, array.length); + } + + public long getValue() { + return (reflect(remainder, Width) ^ ResultXor) & 0xFFFFFFFFL; + } + + private static int reflect(int x, int n) { + int reflection = 0; + for (int i = 0; i < n; ++i) { + if ((x & 1) != 0) { + reflection |= (1 << ((n - 1) - i)); + } + x = (x >>> 1); + } + return reflection; + } +} diff --git a/sgx-jvm/avian/classpath/java/util/zip/DataFormatException.java b/sgx-jvm/avian/classpath/java/util/zip/DataFormatException.java new file mode 100644 index 0000000000..c5a7adbb22 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/zip/DataFormatException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.zip; + +public class DataFormatException extends Exception { + public DataFormatException(String s) { + super(s); + } + + public DataFormatException() { + super(); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/zip/Deflater.java b/sgx-jvm/avian/classpath/java/util/zip/Deflater.java new file mode 100644 index 0000000000..7a86294f77 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/zip/Deflater.java @@ -0,0 +1,162 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.zip; + +public class Deflater { + private static final int DEFAULT_LEVEL = 6; // default compression level (6 is default for gzip) + private static final int Z_OK = 0; + private static final int Z_STREAM_END = 1; + private static final int Z_NEED_DICT = 2; + +// static { +// System.loadLibrary("natives"); +// } + + private long peer; + private byte[] input; + private int offset; + private int length; + private boolean needDictionary; + private boolean finished; + private final boolean nowrap; + private boolean finish; + + public Deflater(int level, boolean nowrap) { + this.nowrap = nowrap; + peer = make(nowrap, level); + } + + public Deflater(int level) { + this(level, false); + } + + public Deflater() { + this(DEFAULT_LEVEL); + } + + private void check() { + if (peer == 0) { + throw new IllegalStateException(); + } + } + + private static native long make(boolean nowrap, int level); + + public boolean finished() { + return finished; + } + + public boolean needsDictionary() { + return needDictionary; + } + + public boolean needsInput() { + return getRemaining() == 0; + } + + public int getRemaining() { + return length; + } + + public void setLevel(int level) throws IllegalArgumentException { + if (level < 0 || level > 9) { + throw new IllegalArgumentException("Valid compression levels are 0-9"); + } + + dispose(peer); + peer = make(nowrap, level); + } + + public void setInput(byte[] input) { + setInput(input, 0, input.length); + } + + public void setInput(byte[] input, int offset, int length) { + this.input = input; + this.offset = offset; + this.length = length; + } + + public void reset() { + dispose(); + peer = make(nowrap, DEFAULT_LEVEL); + input = null; + offset = length = 0; + finish = false; + needDictionary = finished = false; + } + + public int deflate(byte[] output) { + return deflate(output, 0, output.length); + } + + public int deflate(byte[] output, int offset, int length) { + final int zlibResult = 0; + final int inputCount = 1; + final int outputCount = 2; + + if (peer == 0) { + throw new IllegalStateException(); + } + + if (input == null || output == null) { + throw new NullPointerException(); + } + + int[] results = new int[3]; + deflate(peer, + input, this.offset, this.length, + output, offset, length, finish, results); + + if (results[zlibResult] < 0) { + throw new AssertionError(); + } + + switch (results[zlibResult]) { + case Z_NEED_DICT: + needDictionary = true; + break; + + case Z_STREAM_END: + finished = true; + break; + } + + this.offset += results[inputCount]; + this.length -= results[inputCount]; + + return results[outputCount]; + } + + public void finish() { + finish = true; + } + + private static native void deflate + (long peer, + byte[] input, int inputOffset, int inputLength, + byte[] output, int outputOffset, int outputLength, + boolean finish, + int[] results); + + public void end() { + dispose(); + } + + public void dispose() { + if (peer != 0) { + dispose(peer); + peer = 0; + } + } + + private static native void dispose(long peer); +} diff --git a/sgx-jvm/avian/classpath/java/util/zip/DeflaterOutputStream.java b/sgx-jvm/avian/classpath/java/util/zip/DeflaterOutputStream.java new file mode 100644 index 0000000000..dc6320fcbb --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/zip/DeflaterOutputStream.java @@ -0,0 +1,81 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.zip; + +import java.io.OutputStream; +import java.io.FilterOutputStream; +import java.io.IOException; + +public class DeflaterOutputStream extends FilterOutputStream { + protected final Deflater deflater; + protected final byte[] buffer; + + public DeflaterOutputStream(OutputStream out, Deflater deflater, int bufferSize) + { + super(out); + this.deflater = deflater; + this.buffer = new byte[bufferSize]; + } + + public DeflaterOutputStream(OutputStream out, Deflater deflater) { + this(out, deflater, 4 * 1024); + } + + public DeflaterOutputStream(OutputStream out) { + this(out, new Deflater()); + } + + public void write(int b) throws IOException { + byte[] buffer = new byte[1]; + buffer[0] = (byte)(b & 0xff); + write(buffer, 0, 1); + } + + public void write(byte[] b) throws IOException { + write(b, 0, b.length); + } + + public void write(byte[] b, int offset, int length) throws IOException { + // error condition checking + if (deflater.finished()) { + throw new IOException("Already at end of stream"); + } else if (offset < 0) { + throw new IndexOutOfBoundsException("Offset can't be less than zero"); + } else if (length < 0) { + throw new IndexOutOfBoundsException("Length can't be less than zero"); + } else if (b.length - (offset + length) < 0) { + throw new IndexOutOfBoundsException("Offset + Length is larger than the input byte array"); + } else if (length == 0) { + return; + } + + deflater.setInput(b, offset, length); + while (deflater.getRemaining() > 0) { + deflate(); + } + } + + private void deflate() throws IOException { + int len = deflater.deflate(buffer, 0, buffer.length); + if (len > 0) { + out.write(buffer, 0, len); + } + } + + public void close() throws IOException { + deflater.finish(); + while (! deflater.finished()) { + deflate(); + } + out.close(); + deflater.dispose(); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/zip/Inflater.java b/sgx-jvm/avian/classpath/java/util/zip/Inflater.java new file mode 100644 index 0000000000..2c0b7e0466 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/zip/Inflater.java @@ -0,0 +1,142 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.zip; + +public class Inflater { + private static final int Z_OK = 0; + private static final int Z_STREAM_END = 1; + private static final int Z_NEED_DICT = 2; + +// static { +// System.loadLibrary("natives"); +// } + + private long peer; + private byte[] input; + private int offset; + private int length; + private boolean needDictionary; + private boolean finished; + private final boolean nowrap; + + public Inflater(boolean nowrap) { + this.nowrap = nowrap; + peer = make(nowrap); + } + + public Inflater() { + this(false); + } + + private void check() { + if (peer == 0) { + throw new IllegalStateException(); + } + } + + private static native long make(boolean nowrap); + + public boolean finished() { + return finished; + } + + public boolean needsDictionary() { + return needDictionary; + } + + public boolean needsInput() { + return getRemaining() == 0; + } + + public int getRemaining() { + return length; + } + + public void setInput(byte[] input) { + setInput(input, 0, input.length); + } + + public void setInput(byte[] input, int offset, int length) { + this.input = input; + this.offset = offset; + this.length = length; + } + + public void reset() { + dispose(); + peer = make(nowrap); + input = null; + offset = length = 0; + needDictionary = finished = false; + } + + public int inflate(byte[] output) throws DataFormatException { + return inflate(output, 0, output.length); + } + + public int inflate(byte[] output, int offset, int length) + throws DataFormatException + { + final int zlibResult = 0; + final int inputCount = 1; + final int outputCount = 2; + + if (peer == 0) { + throw new IllegalStateException(); + } + + if (input == null || output == null) { + throw new NullPointerException(); + } + + int[] results = new int[3]; + inflate(peer, input, this.offset, this.length, + output, offset, length, results); + + if (results[zlibResult] < 0) { + throw new DataFormatException(); + } + + switch (results[zlibResult]) { + case Z_NEED_DICT: + needDictionary = true; + break; + + case Z_STREAM_END: + finished = true; + break; + } + + this.offset += results[inputCount]; + this.length -= results[inputCount]; + + return results[outputCount]; + } + + private static native void inflate + (long peer, + byte[] input, int inputOffset, int inputLength, + byte[] output, int outputOffset, int outputLength, + int[] results); + + public void end() { + dispose(); + } + + public void dispose() { + if (peer != 0) { + dispose(peer); + peer = 0; + } + } + + private static native void dispose(long peer); +} diff --git a/sgx-jvm/avian/classpath/java/util/zip/InflaterInputStream.java b/sgx-jvm/avian/classpath/java/util/zip/InflaterInputStream.java new file mode 100644 index 0000000000..fcefaec87b --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/zip/InflaterInputStream.java @@ -0,0 +1,81 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.zip; + +import java.io.InputStream; +import java.io.IOException; +import java.io.EOFException; + +public class InflaterInputStream extends InputStream { + private final InputStream in; + private final Inflater inflater; + private final byte[] buffer; + + public InflaterInputStream(InputStream in, Inflater inflater, int bufferSize) + { + this.in = in; + this.inflater = inflater; + this.buffer = new byte[bufferSize]; + } + + public InflaterInputStream(InputStream in, Inflater inflater) { + this(in, inflater, 4 * 1024); + } + + public InflaterInputStream(InputStream in) { + this(in, new Inflater()); + } + + public int read() throws IOException { + byte[] buffer = new byte[1]; + int c = read(buffer); + return (c < 0 ? c : (buffer[0] & 0xFF)); + } + + public int read(byte[] b, int offset, int length) throws IOException { + if (inflater.finished()) { + return -1; + } + + while (true) { + if (inflater.needsInput()) { + int count = in.read(buffer); + if (count > 0) { + inflater.setInput(buffer, 0, count); + } else { + throw new EOFException(); + } + } + + try { + int count = inflater.inflate(b, offset, length); + if (count > 0) { + return count; + } else if (inflater.needsDictionary()) { + throw new IOException("missing dictionary"); + } else if (inflater.finished()) { + return -1; + } + } catch (DataFormatException e) { + throw new IOException(e); + } + } + } + + public int available() throws IOException { + return inflater.finished() ? 0 : 1; + } + + public void close() throws IOException { + in.close(); + inflater.dispose(); + } +} diff --git a/sgx-jvm/avian/classpath/java/util/zip/ZipEntry.java b/sgx-jvm/avian/classpath/java/util/zip/ZipEntry.java new file mode 100644 index 0000000000..482f9d0347 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/zip/ZipEntry.java @@ -0,0 +1,210 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.zip; + +/** + * Class ZipEntry: + * + * Class to store and retrieve information for entries in a zip file + * Contains variables for all standard zip format field as well as + * setter and accessor methods + * + * "name" is used to store the string name of the entrys + * "reqVersion" stores a byte encoded minimum required version to open the zip + * "compressionMethod" stores the method used to compress the zip + * "modTimeDate" stores an MSDOS time field "millisTime" stores time in long format + * "crc" stores crc data for the zip entry + * "compSize" and "uncompSize" store compressed and uncompressed sizes + * "offset" stores data regarding the offset from the start of the zip file + * + * @author Christopher Jordan + * @author David Chau + * @author Aaron Davis + * @author Riley Moses + */ + +import java.util.Calendar; +import java.util.Date; + + +public class ZipEntry { + String name; + short reqVersion = -1; + short compressionMethod = -1; + int modTimeDate = -1; + long millisTime = -1; + int crc = -1; + long compSize = 0; + long uncompSize = 0; + int offset = -1; + + public ZipEntry(String name) { + this.name = name; + setTime(System.currentTimeMillis()); + } + + //Method to return name of the file + public String getName() { + return name; + } + + //Method to check if file is a directory + public boolean isDirectory() { + return getName().endsWith("/"); + } + + /** + * Method setRequiredVersion: + * + * Method to set the minimum version required to open the zip file + * Valid values for the compression method are the numbers 1.0 to 10.0 + * + * @author Christopher Jordan + */ + private boolean setRequiredVersion(float versionFloat){ + //Check for valid version numbers + if (versionFloat < 1 || versionFloat > 100){ + return false; + } + + //Convert to short value for storage + versionFloat = versionFloat * 10; + short versionShort = (short)versionFloat; + + //Set value of version + reqVersion = versionShort; + return true; + } + + //Method to set the compression method for the file + //Valid methods are "stored" = 0 or "deflated" = 8 + public void setMethod(short compMethod){ + if (compMethod == 0 || compMethod == 8){ + this.compressionMethod = compMethod; + } + } + + public int getMethod(){ + return this.compressionMethod; + } + + //Methods to set and get the crc for the entry + public void setCrc(int crc){ + if (crc < 0 || crc > 0xffffffff){ + return; + } + else + this.crc = crc; + } + + public int getCrc(){ + return this.crc; + } + + //Methods to set and get the time and date + public void setTime(long currentTime){ + modTimeDate = computeDOSDateTime(currentTime); + millisTime = currentTime; + } + + public long getTime(){ + return millisTime; + } + + /** + * Method computeDOSDateTime(): + * + * Takes the time from a long and converts to a Calendar object + * + * Time is stored in the MSDOS format 5 bits for hour, 6 bits for min, 5 bits for seconds + * Hours are in military time and must be adjusted to time zone + * Seconds are divided by two before storing and must be multiplied by two to retrieve + * + * Date is stored in the MSDOS format 7 bit for year, 4 bit for month, 5 bits for day of month + * Year is the number of years since 1980 per, the month is stored starting at 0 + * for January. Day of month is stored with nature numbering + * + * Bit masks and shifting are used to build the time and date bytes + * + * @author Christopher Jordan + * @author Aaron Davis + **/ + private static int computeDOSDateTime(long currentTime){ + final int DAY_OF_MONTH = 5; + final int HOUR_OF_DAY = 11; + final int MINUTE = 12; + final int MONTH = 2; + final int SECOND = 13; + final int YEAR = 1; + + //Create calendar object, then set time to value passed in + Calendar modCalendar = Calendar.getInstance(); + modCalendar.setTime(new Date(currentTime)); + + //Hour + int timeBits = modCalendar.get(HOUR_OF_DAY); + timeBits = timeBits - 6; + timeBits = timeBits << 6; + + //Minutes + int minBits = 0x3f & (modCalendar.get(MINUTE));; + timeBits = timeBits ^ minBits; + timeBits = timeBits << 5; + + //Seconds + int secBits = 0x1f & (modCalendar.get(SECOND)); + secBits = secBits >> 1; //Divide by 2 + timeBits = timeBits ^ secBits; + + //Year + int dateBits = (modCalendar.get(YEAR) -1980); + dateBits = dateBits << 4; + + //Month + int month = 0xf & ((modCalendar.get(MONTH)) + 1); + dateBits = dateBits ^ month; + dateBits = dateBits << 5; + + //Day of month + int dayBits = 0x1f & modCalendar.get(DAY_OF_MONTH); + dateBits = dateBits ^ dayBits; + + //Store Date + int storeDate = ((dateBits << 16) ^ (timeBits)); + return storeDate; + } + + //Methods to set and get the uncompressed size of the entry + public void setSize(int size){ + if (size < 0){ + return; + } + else + uncompSize = size; + } + + public long getSize() { + return uncompSize; + } + + //Methods to set and get the compressed size of the entry + public void setCompressedSize(long size){ + if (size < 0){ + return; + } + else + compSize = size; + } + + public long getCompressedSize() { + return compSize; + } +} diff --git a/sgx-jvm/avian/classpath/java/util/zip/ZipFile.java b/sgx-jvm/avian/classpath/java/util/zip/ZipFile.java new file mode 100644 index 0000000000..17159ea7da --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/zip/ZipFile.java @@ -0,0 +1,389 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.zip; + +import java.io.File; +import java.io.RandomAccessFile; +import java.io.InputStream; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Map; +import java.util.HashMap; + +public class ZipFile { + private final RandomAccessFile file; + private final Window window; + private final Map index = new HashMap(); + + public ZipFile(String name) throws IOException { + file = new RandomAccessFile(name, "r"); + window = new Window(file, 4096); + + int fileLength = (int) file.length(); + int pointer = fileLength - 22; + byte[] magic = new byte[] { 0x50, 0x4B, 0x05, 0x06 }; + while (pointer > 0) { + if (equal(window.data, window.seek(pointer, magic.length), + magic, 0, magic.length)) + { + pointer = directoryOffset(window, pointer); + + magic = new byte[] { 0x50, 0x4B, 0x01, 0x02 }; + while (pointer < fileLength) { + if (equal(window.data, window.seek(pointer, magic.length), + magic, 0, magic.length)) + { + index.put(entryName(window, pointer), pointer); + pointer = entryEnd(window, pointer); + } else { + pointer = fileLength; + } + } + pointer = 0; + } else { + -- pointer; + } + } + } + + public ZipFile(File file) throws IOException { + this(file.getAbsolutePath()); + } + + public int size() { + return index.size(); + } + + protected Enumeration makeEnumeration + (EntryFactory factory) + { + return new MyEnumeration(factory, window, index.values().iterator()); + } + + public Enumeration entries() { + return makeEnumeration(ZipEntryFactory.Instance); + } + + protected ZipEntry getEntry(EntryFactory factory, String name) { + while (name.startsWith("/")) { + name = name.substring(1); + } + Integer pointer = index.get(name); + return (pointer == null ? null : factory.makeEntry(window, pointer)); + } + + public ZipEntry getEntry(String name) { + return getEntry(ZipEntryFactory.Instance, name); + } + + public InputStream getInputStream(ZipEntry entry) throws IOException { + final int pointer = ((MyEntry) entry).pointer(); + int method = compressionMethod(window, pointer); + int size = compressedSize(window, pointer); + InputStream in = new MyInputStream(file, fileData(window, pointer), size); + + final int Stored = 0; + final int Deflated = 8; + + switch (method) { + case Stored: + return in; + + case Deflated: + return new InflaterInputStream(in, new Inflater(true)) { + int remaining = uncompressedSize(window, pointer); + + public int read() throws IOException { + byte[] buffer = new byte[1]; + int c = read(buffer); + return (c < 0 ? c : (buffer[0] & 0xFF)); + } + + public int read(byte[] buffer) throws IOException { + return read(buffer, 0, buffer.length); + } + + public int read(byte[] buffer, int offset, int length) + throws IOException + { + int c = super.read(buffer, offset, length); + if (c > 0) { + remaining -= c; + } + return c; + } + + public int available() { + return remaining; + } + }; + + default: + throw new IOException(); + } + } + + private static boolean equal(byte[] a, int aOffset, byte[] b, int bOffset, + int size) + { + for (int i = 0; i < size; ++i) { + if (a[aOffset + i] != b[bOffset + i]) return false; + } + return true; + } + + private static int get2(Window w, int p) throws IOException { + int offset = w.seek(p, 2); + return + ((w.data[offset + 1] & 0xFF) << 8) | + ((w.data[offset ] & 0xFF) ); + } + + private static int get4(Window w, int p) throws IOException { + int offset = w.seek(p, 4); + return + ((w.data[offset + 3] & 0xFF) << 24) | + ((w.data[offset + 2] & 0xFF) << 16) | + ((w.data[offset + 1] & 0xFF) << 8) | + ((w.data[offset ] & 0xFF) ); + } + + private static int directoryOffset(Window w, int p) throws IOException { + return get4(w, p + 16); + } + + private static int entryNameLength(Window w, int p) throws IOException { + return get2(w, p + 28); + } + + protected static String entryName(Window w, int p) throws IOException { + int length = entryNameLength(w, p); + return new String(w.data, w.seek(p + 46, length), length); + } + + private static int compressionMethod(Window w, int p) throws IOException { + return get2(w, p + 10); + } + + protected static int compressedSize(Window w, int p) throws IOException { + return get4(w, p + 20); + } + + protected static int uncompressedSize(Window w, int p) throws IOException { + return get4(w, p + 24); + } + + private static int fileNameLength(Window w, int p) throws IOException { + return get2(w, p + 28); + } + + private static int extraFieldLength(Window w, int p) throws IOException { + return get2(w, p + 30); + } + + private static int commentFieldLength(Window w, int p) throws IOException { + return get2(w, p + 32); + } + + private static int entryEnd(Window w, int p) throws IOException { + final int HeaderSize = 46; + return p + HeaderSize + + fileNameLength(w, p) + + extraFieldLength(w, p) + + commentFieldLength(w, p); + } + + private static int fileData(Window w, int p) throws IOException { + int localHeader = localHeader(w, p); + final int LocalHeaderSize = 30; + return localHeader + + LocalHeaderSize + + localFileNameLength(w, localHeader) + + localExtraFieldLength(w, localHeader); + } + + private static int localHeader(Window w, int p) throws IOException { + return get4(w, p + 42); + } + + private static int localFileNameLength(Window w, int p) throws IOException { + return get2(w, p + 26); + } + + private static int localExtraFieldLength(Window w, int p) + throws IOException + { + return get2(w, p + 28); + } + + public void close() throws IOException { + file.close(); + } + + protected static class Window { + private final RandomAccessFile file; + public final byte[] data; + public int start; + public int length; + + public Window(RandomAccessFile file, int size) { + this.file = file; + data = new byte[size]; + } + + public int seek(int start, int length) throws IOException { + int fileLength = (int) file.length(); + + if (length > data.length) { + throw new IllegalArgumentException + ("length " + length + " greater than buffer length " + data.length); + } + + if (start < 0) { + throw new IllegalArgumentException("negative start " + start); + } + + if (start + length > fileLength) { + throw new IllegalArgumentException + ("end " + (start + length) + " greater than file length " + + fileLength); + } + + if (start < this.start || start + length > this.start + this.length) { + this.length = Math.min(data.length, fileLength); + this.start = start - ((this.length - length) / 2); + if (this.start < 0) { + this.start = 0; + } else if (this.start + this.length > fileLength) { + this.start = fileLength - this.length; + } + file.seek(this.start); + file.readFully(data, 0, this.length); + } + + return start - this.start; + } + } + + protected interface MyEntry { + public int pointer(); + } + + private static class MyZipEntry extends ZipEntry implements MyEntry { + public final Window window; + public final int pointer; + + public MyZipEntry(Window window, int pointer) { + super(null); + this.window = window; + this.pointer = pointer; + } + + public String getName() { + try { + return entryName(window, pointer); + } catch (IOException e) { + return null; + } + } + + public long getCompressedSize() { + try { + return compressedSize(window, pointer); + } catch (IOException e) { + return 0; + } + } + + public long getSize() { + try { + return uncompressedSize(window, pointer); + } catch (IOException e) { + return 0; + } + } + + public int pointer() { + return pointer; + } + } + + protected interface EntryFactory { + public ZipEntry makeEntry(Window window, int pointer); + } + + private static class ZipEntryFactory implements EntryFactory { + public static final ZipEntryFactory Instance = new ZipEntryFactory(); + + public ZipEntry makeEntry(Window window, int pointer) { + return new MyZipEntry(window, pointer); + } + } + + private static class MyEnumeration implements Enumeration { + private final EntryFactory factory; + private final Window window; + private final Iterator iterator; + + public MyEnumeration(EntryFactory factory, Window window, + Iterator iterator) + { + this.factory = factory; + this.window = window; + this.iterator = iterator; + } + + public boolean hasMoreElements() { + return iterator.hasNext(); + } + + public ZipEntry nextElement() { + return factory.makeEntry(window, iterator.next()); + } + } + + private static class MyInputStream extends InputStream { + private RandomAccessFile file; + private int offset; + private int length; + + public MyInputStream(RandomAccessFile file, int start, int length) { + this.file = file; + this.offset = start; + this.length = length; + } + + public int read() throws IOException { + byte[] b = new byte[1]; + int c = read(b); + return (c == -1 ? -1 : b[0] & 0xFF); + } + + public int read(byte[] b, int offset, int length) throws IOException { + if (this.length == 0) return -1; + + if (length > this.length) length = this.length; + + file.seek(this.offset); + file.readFully(b, offset, length); + + this.offset += length; + this.length -= length; + + return length; + } + + public void close() throws IOException { + file = null; + } + } +} diff --git a/sgx-jvm/avian/classpath/java/util/zip/ZipOutputStream.java b/sgx-jvm/avian/classpath/java/util/zip/ZipOutputStream.java new file mode 100644 index 0000000000..e5b929ac72 --- /dev/null +++ b/sgx-jvm/avian/classpath/java/util/zip/ZipOutputStream.java @@ -0,0 +1,246 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.zip; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.CRC32; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.Deflater; + + +/** + * An as simple as possible implementation of ZipOutputStream + * Compression method defaults to DEFLATE + * All hardcoded defaults match the defaults for openJDK, + * including PKZip version, bit flags set, compression level, etc + * + * @author David Chau + * @author Aaron Davis + * @author Christopher Jordan + * @author Riley Moses + * + */ +public class ZipOutputStream extends DeflaterOutputStream { + private static final int SIGNATURE = 0x04034b50; + private static final short VERSION = 0x0014; + private static final short BITFLAG = 0x0008; + private static final short METHOD = 0x0008; + private static final int CENTRAL_FILE_HEADER = 0x02014b50; + private static final int DATA_DESCRIPTER_HEADER = 0x08074b50; + private static final int END_OF_CENTRAL_DIRECTORY_SIG = 0x06054b50; + private static final int DEFAULT_LEVEL = 6; + + private static final int INPUT_BUFFER_SIZE = 1024; + + private List entries; + private CRC32 crc = new CRC32(); + private ZipEntry currentEntry; // holder for current entry + private int bytesWritten; // a counter for total bytes written + private int sizeOfCentralDirectory; // a counter for central dir size + + // these are used for the function write(int b) to provide a speed increase + private byte[] inputBuffer = new byte[INPUT_BUFFER_SIZE]; + private int bufferIndex; + + + public ZipOutputStream(OutputStream outStream) { + super(outStream, new Deflater(DEFAULT_LEVEL, true)); + bytesWritten = 0; + sizeOfCentralDirectory = 0; + entries = new ArrayList(); + } + + public void putNextEntry(ZipEntry e) throws IOException { + e.offset = bytesWritten; + currentEntry = e; + entries.add(e); + writeLocalHeader(e); + } + + public void closeEntry() throws IOException { + // write remainder of buffer if partially full + if (bufferIndex != 0) { + write(inputBuffer, 0, bufferIndex); + bufferIndex = 0; + } + + finish(); + + currentEntry.crc = (int) crc.getValue(); + crc.reset(); + writeDataDescriptor(currentEntry); + } + + @Override + public void write(byte[] b, int offset, int length) throws IOException { + if (offset < 0 || length < 0 || b.length - (offset + length) < 0) + throw new IndexOutOfBoundsException(); + + currentEntry.uncompSize += length; + crc.update(b, offset, length); + currentEntry.crc = (int) crc.getValue(); + + deflater.setInput(b, offset, length); + while (deflater.getRemaining() > 0) + deflate(); + } + + @Override + public void write(int b) throws IOException { + inputBuffer[bufferIndex] = (byte)(b & 0xff); + bufferIndex += 1; + if (bufferIndex == 1024) { + write(inputBuffer, 0, bufferIndex); + bufferIndex = 0; + } + } + + private void deflate() throws IOException { + int len = deflater.deflate(buffer, 0, buffer.length); + currentEntry.compSize += len; + bytesWritten += len; + if (len > 0) + out.write(buffer, 0, len); + } + + private void writeLocalHeader(ZipEntry e) throws IOException { + byte[] tmpBuffer = new byte[30]; + + addFourBytes(SIGNATURE, 0, tmpBuffer); // local header signature + addTwoBytes(VERSION, 4, tmpBuffer); // version used + addTwoBytes(BITFLAG, 6, tmpBuffer); // flags + addTwoBytes(METHOD, 8, tmpBuffer); // compression method + addFourBytes(e.modTimeDate, 10, tmpBuffer); // last mod date and time + addFourBytes(0, 14, tmpBuffer); // CRC is 0 for local header + + // with default flag settings (bit 3 set) the compressed and uncompressed size + // is written here as 0 and written correctly in the data descripter + addFourBytes(0, 18, tmpBuffer); // compressed size + addFourBytes(0, 22, tmpBuffer); // uncompressed size + addTwoBytes(e.name.length(), 26, tmpBuffer); // length of file name + + // extra field length, in this implementation extra field in not used + addTwoBytes(0, 28, tmpBuffer); + + out.write(tmpBuffer, 0, 30); + + // write file name, return the number of bytes written + int len = writeUTF8(e.getName()); + + bytesWritten += 30 + len; + } + + private void writeDataDescriptor(ZipEntry currentEntry) throws IOException { + byte[] tmpBuffer = new byte[16]; + + addFourBytes(DATA_DESCRIPTER_HEADER, 0, tmpBuffer); // data descripter header + addFourBytes(currentEntry.crc, 4, tmpBuffer); // crc value + addFourBytes((int)currentEntry.compSize, 8, tmpBuffer); // compressed size + addFourBytes((int)currentEntry.uncompSize, 12, tmpBuffer);// uncompressed size + out.write(tmpBuffer, 0, 16); + bytesWritten += 16; + } + + private void writeCentralDirectoryHeader(ZipEntry e) throws IOException { + byte[] tmpBuffer = new byte[46]; + + addFourBytes(CENTRAL_FILE_HEADER, 0, tmpBuffer); // central directory header signature + addTwoBytes(VERSION, 4, tmpBuffer); // version made by + addTwoBytes(VERSION, 6, tmpBuffer); // version needed + addTwoBytes(BITFLAG, 8, tmpBuffer); // flags + addTwoBytes(METHOD, 10, tmpBuffer); // compression method + + addFourBytes(e.modTimeDate, 12, tmpBuffer); // last mod date and time + addFourBytes(e.crc, 16, tmpBuffer); // crc + addFourBytes((int)e.compSize, 20, tmpBuffer); // compressed size + addFourBytes((int)e.uncompSize, 24, tmpBuffer); // uncompressed size + + addTwoBytes(e.getName().length(), 28, tmpBuffer); // file name length + + // the following 5 fields are all 0 for a simple default compression + addTwoBytes(0, 30, tmpBuffer); // extra field length (not used) + addTwoBytes(0, 32, tmpBuffer); // comment length (not used) + addTwoBytes(0, 34, tmpBuffer); // disk number start + addTwoBytes(0, 36, tmpBuffer); // internal file attribute + addFourBytes(0, 38, tmpBuffer); // external file attribute + + addFourBytes((int) e.offset, 42, tmpBuffer); // relative offset of local header + + out.write(tmpBuffer, 0, 46); + + int len = writeUTF8(e.getName()); + + bytesWritten += 46 + len; + sizeOfCentralDirectory += 46 + len; + } + + private void writeEndofCentralDirectory(int offset) throws IOException { + byte[] tmpBuffer = new byte[22]; + + short numEntries = (short) entries.size(); + addFourBytes(END_OF_CENTRAL_DIRECTORY_SIG, 0, tmpBuffer); // end of central directory signature + addTwoBytes(0, 4, tmpBuffer); // disk number + addTwoBytes(0, 6, tmpBuffer); // disk number where central dir starts + addTwoBytes(numEntries, 8, tmpBuffer); // number of entries on this disk + addTwoBytes(numEntries, 10, tmpBuffer); // number of entries in central dir + addFourBytes(sizeOfCentralDirectory, 12, tmpBuffer); // length of central directory + addFourBytes(offset, 16, tmpBuffer); // offset of central directory + addTwoBytes(0, 20, tmpBuffer); // length of added comments (not used) + out.write(tmpBuffer, 0, 22); + bytesWritten += 22; + } + + @Override + public void close() throws IOException { + int centralDirOffset = bytesWritten; + for (ZipEntry e : entries) + writeCentralDirectoryHeader(e); + writeEndofCentralDirectory(centralDirOffset); + deflater.dispose(); + out.close(); + } + + @Override + public void flush() throws IOException { + out.write(inputBuffer, 0, inputBuffer.length); + inputBuffer = new byte[INPUT_BUFFER_SIZE]; + } + + private void finish() throws IOException { + deflater.finish(); + while (!deflater.finished()) { + deflate(); + } + deflater.reset(); + } + + private void addTwoBytes(int bytes, int offset, byte[] buffer) throws IOException { + buffer[offset] = (byte) (bytes & 0xff); + buffer[offset + 1] = (byte) ((bytes >> 8) & 0xff); + } + + private void addFourBytes(int bytes, int offset, byte[] buffer) throws IOException { + buffer[offset] = (byte) (bytes & 0xff); + buffer[offset + 1] = (byte) ((bytes >> 8) & 0xff); + buffer[offset + 2] = (byte) ((bytes >> 16) & 0xff); + buffer[offset + 3] = (byte) ((bytes >> 24) & 0xff); + } + + private int writeUTF8(String text) throws IOException { + byte[] bytes = text.getBytes("UTF-8"); + out.write(bytes, 0, bytes.length); + return bytes.length; + } +} \ No newline at end of file diff --git a/sgx-jvm/avian/classpath/jni-util.h b/sgx-jvm/avian/classpath/jni-util.h new file mode 100644 index 0000000000..f77a4104eb --- /dev/null +++ b/sgx-jvm/avian/classpath/jni-util.h @@ -0,0 +1,127 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef JNI_UTIL +#define JNI_UTIL + +#include "stdio.h" +#include "stdlib.h" +#include "string.h" + +#include + +#undef JNIEXPORT + +#ifdef UNUSED +#undef UNUSED +#endif + +#if (defined __MINGW32__) || (defined _MSC_VER) +#define PLATFORM_WINDOWS +#define PATH_SEPARATOR ';' +#define JNIEXPORT __declspec(dllexport) +#else // not (defined __MINGW32__) || (defined _MSC_VER) +#define PLATFORM_POSIX +#define PATH_SEPARATOR ':' +#define JNIEXPORT __attribute__((visibility("default"))) __attribute__((used)) +#endif // not (defined __MINGW32__) || (defined _MSC_VER) + +#ifdef _MSC_VER + +#define UNUSED + +typedef char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef int int32_t; +typedef unsigned int uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; + +#define INT32_MAX 2147483647 + +#define not! +#define or || +#define and && +#define xor ^ + +#ifdef _M_IX86 +#define ARCH_x86_32 +#elif defined _M_X64 +#define ARCH_x86_64 +#endif + +#else // not _MSC_VER + +#define UNUSED __attribute__((unused)) + +#include "stdint.h" +#include "errno.h" + +#ifdef __i386__ +#define ARCH_x86_32 +#elif defined __x86_64__ +#define ARCH_x86_64 +#elif defined __arm__ +#define ARCH_arm +#elif defined __aarch64__ +#define ARCH_arm64 +#endif + +#endif // not _MSC_VER + +inline void throwNew(JNIEnv* e, const char* class_, const char* message, ...) +{ + jclass c = e->FindClass(class_); + if (c) { + if (message) { + static const unsigned BufferSize = 256; + char buffer[BufferSize]; + + va_list list; + va_start(list, message); +#ifdef _MSC_VER + vsnprintf_s(buffer, BufferSize - 1, _TRUNCATE, message, list); +#else + vsnprintf(buffer, BufferSize - 1, message, list); +#endif + va_end(list); + + e->ThrowNew(c, buffer); + } else { + e->ThrowNew(c, 0); + } + e->DeleteLocalRef(c); + } +} + +inline void throwNewErrno(JNIEnv* e, const char* class_) +{ +#ifdef _MSC_VER + const unsigned size = 128; + char buffer[size]; + strerror_s(buffer, size, errno); + throwNew(e, class_, buffer); +#else + throwNew(e, class_, strerror(errno)); +#endif +} + +inline void* allocate(JNIEnv* e, unsigned size) +{ + void* p = malloc(size); + if (p == 0) { + throwNew(e, "java/lang/OutOfMemoryError", 0); + } + return p; +} + +#endif // JNI_UTIL diff --git a/sgx-jvm/avian/classpath/libcore/reflect/AnnotationAccess.java b/sgx-jvm/avian/classpath/libcore/reflect/AnnotationAccess.java new file mode 100644 index 0000000000..750fe9cadf --- /dev/null +++ b/sgx-jvm/avian/classpath/libcore/reflect/AnnotationAccess.java @@ -0,0 +1,8 @@ +package libcore.reflect; + +import java.lang.reflect.AccessibleObject; + +public class AnnotationAccess { + public static native AccessibleObject getEnclosingMethodOrConstructor + (Class c); +} diff --git a/sgx-jvm/avian/classpath/sockets.cpp b/sgx-jvm/avian/classpath/sockets.cpp new file mode 100644 index 0000000000..ddffc18576 --- /dev/null +++ b/sgx-jvm/avian/classpath/sockets.cpp @@ -0,0 +1,205 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +/* + * This file implements a simple cross-platform JNI sockets API + * It is used from different classes of the default Avian classpath + */ + +#ifndef SGX + +#include "sockets.h" + +namespace avian { +namespace classpath { +namespace sockets { + +int last_socket_error() +{ +#ifdef PLATFORM_WINDOWS + int error = WSAGetLastError(); +#else + int error = errno; +#endif + return error; +} + +void init(JNIEnv* ONLY_ON_WINDOWS(e)) +{ +#ifdef PLATFORM_WINDOWS + static bool wsaInitialized = false; + if (not wsaInitialized) { + WSADATA data; + int r = WSAStartup(MAKEWORD(2, 2), &data); + if (r or LOBYTE(data.wVersion) != 2 or HIBYTE(data.wVersion) != 2) { + throwNew(e, "java/io/IOException", "WSAStartup failed"); + } else { + wsaInitialized = true; + } + } +#endif +} + +SOCKET create(JNIEnv* e) +{ + SOCKET sock; + if (INVALID_SOCKET == (sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))) { + char buf[255]; + sprintf( + buf, "Can't create a socket. System error: %d", last_socket_error()); + throwNew(e, "java/io/IOException", buf); + return 0; // This doesn't matter cause we have risen an exception + } + return sock; +} + +void connect(JNIEnv* e, SOCKET sock, long addr, short port) +{ + sockaddr_in adr; + adr.sin_family = AF_INET; +#ifdef PLATFORM_WINDOWS + adr.sin_addr.S_un.S_addr = htonl(addr); +#else + adr.sin_addr.s_addr = htonl(addr); +#endif + adr.sin_port = htons(port); + + if (SOCKET_ERROR == ::connect(sock, (sockaddr*)&adr, sizeof(adr))) { + char buf[255]; + sprintf( + buf, "Can't connect a socket. System error: %d", last_socket_error()); + throwNew(e, "java/io/IOException", buf); + return; + } +} + +void bind(JNIEnv* e, SOCKET sock, long addr, short port) +{ + sockaddr_in adr; + adr.sin_family = AF_INET; +#ifdef PLATFORM_WINDOWS + adr.sin_addr.S_un.S_addr = htonl(addr); +#else + adr.sin_addr.s_addr = htonl(addr); +#endif + adr.sin_port = htons(port); + + if (SOCKET_ERROR == ::bind(sock, (sockaddr*)&adr, sizeof(adr))) { + char buf[255]; + sprintf(buf, "Can't bind a socket. System error: %d", last_socket_error()); + throwNew(e, "java/io/IOException", buf); + return; + } +} + +SOCKET accept(JNIEnv* e, SOCKET sock, long* client_addr, short* client_port) +{ + sockaddr_in adr; + SOCKET client_socket = ::accept(sock, (sockaddr*)&adr, NULL); + if (INVALID_SOCKET == client_socket) { + char buf[255]; + sprintf(buf, + "Can't accept the incoming connection. System error: %d", + last_socket_error()); + throwNew(e, "java/io/IOException", buf); + return INVALID_SOCKET; + } + + if (client_addr != NULL) { +#ifdef PLATFORM_WINDOWS + *client_addr = ntohl(adr.sin_addr.S_un.S_addr); +#else + *client_addr = ntohl(adr.sin_addr.s_addr); +#endif + } + + if (client_port != NULL) { + *client_port = ntohs(adr.sin_port); + } + + return client_socket; +} + +void send(JNIEnv* e, SOCKET sock, const char* buff_ptr, int buff_size) +{ + if (SOCKET_ERROR == ::send(sock, buff_ptr, buff_size, 0)) { + char buf[255]; + sprintf(buf, + "Can't send data through the socket. System error: %d", + last_socket_error()); + throwNew(e, "java/io/IOException", buf); + return; + } +} + +int recv(JNIEnv* e, SOCKET sock, char* buff_ptr, int buff_size) +{ + int length = ::recv(sock, buff_ptr, buff_size, 0); + if (SOCKET_ERROR == length) { + char buf[255]; + sprintf(buf, + "Can't receive data through the socket. System error: %d", + last_socket_error()); + throwNew(e, "java/io/IOException", buf); + return 0; // This doesn't matter cause we have risen an exception + } + return length; +} + +void abort(JNIEnv* e, SOCKET sock) +{ + if (SOCKET_ERROR == ::closesocket(sock)) { + char buf[255]; + sprintf( + buf, "Can't close the socket. System error: %d", last_socket_error()); + throwNew(e, "java/io/IOException", buf); + } +} + +void close(JNIEnv* e, SOCKET sock) +{ + if (SOCKET_ERROR == ::shutdown(sock, SD_BOTH)) { + int errcode = last_socket_error(); + if (errcode != ENOTCONN) { + char buf[255]; + sprintf(buf, "Can't shutdown the socket. System error: %d", errcode); + throwNew(e, "java/io/IOException", buf); + } + } +} + +void close_input(JNIEnv* e, SOCKET sock) +{ + if (SOCKET_ERROR == ::shutdown(sock, SD_RECEIVE)) { + int errcode = last_socket_error(); + if (errcode != ENOTCONN) { + char buf[255]; + sprintf(buf, "Can't shutdown the socket. System error: %d", errcode); + throwNew(e, "java/io/IOException", buf); + } + } +} + +void close_output(JNIEnv* e, SOCKET sock) +{ + if (SOCKET_ERROR == ::shutdown(sock, SD_SEND)) { + int errcode = last_socket_error(); + if (errcode != ENOTCONN) { + char buf[255]; + sprintf(buf, "Can't shutdown the socket. System error: %d", errcode); + throwNew(e, "java/io/IOException", buf); + } + } +} +} +} +} + +#endif \ No newline at end of file diff --git a/sgx-jvm/avian/classpath/sockets.h b/sgx-jvm/avian/classpath/sockets.h new file mode 100644 index 0000000000..31a45f2ce5 --- /dev/null +++ b/sgx-jvm/avian/classpath/sockets.h @@ -0,0 +1,74 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +/* + * This file represents a simple cross-platform JNI sockets API + * It is used from different classes of the default Avian classpath + */ + +#ifndef SOCKETS_H_ +#define SOCKETS_H_ + +#include "jni.h" +#include "jni-util.h" +#include "avian/common.h" + +#ifdef PLATFORM_WINDOWS +#include + +#define ONLY_ON_WINDOWS(x) x + +#ifndef ENOTCONN +#define ENOTCONN WSAENOTCONN +#endif +#else +#include +#include +#include +#include + +#define ONLY_ON_WINDOWS(x) +#define SOCKET int +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 +#define closesocket(x) close(x) + +#define SD_RECEIVE SHUT_RD +#define SD_SEND SHUT_WR +#define SD_BOTH SHUT_RDWR + +#endif + +namespace avian { +namespace classpath { +namespace sockets { + +// Library initialization +void init(JNIEnv* ONLY_ON_WINDOWS(e)); + +// Socket initialization +SOCKET create(JNIEnv* e); +void connect(JNIEnv* e, SOCKET sock, long addr, short port); +void bind(JNIEnv* e, SOCKET sock, long addr, short port); +SOCKET accept(JNIEnv* e, SOCKET sock, long* client_addr, short* client_port); + +// I/O +void send(JNIEnv* e, SOCKET sock, const char* buff_ptr, int buff_size); +int recv(JNIEnv* e, SOCKET sock, char* buff_ptr, int buff_size); + +// Socket closing +void abort(JNIEnv* e, SOCKET sock); +void close(JNIEnv* e, SOCKET sock); +void close_input(JNIEnv* e, SOCKET sock); +void close_output(JNIEnv* e, SOCKET sock); +} +} +} +#endif /* SOCKETS_H_ */ diff --git a/sgx-jvm/avian/classpath/sun/misc/Cleaner.java b/sgx-jvm/avian/classpath/sun/misc/Cleaner.java new file mode 100644 index 0000000000..eaeea86f72 --- /dev/null +++ b/sgx-jvm/avian/classpath/sun/misc/Cleaner.java @@ -0,0 +1,13 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package sun.misc; + +public class Cleaner { } diff --git a/sgx-jvm/avian/classpath/sun/misc/Unsafe.java b/sgx-jvm/avian/classpath/sun/misc/Unsafe.java new file mode 100644 index 0000000000..a8fdedc227 --- /dev/null +++ b/sgx-jvm/avian/classpath/sun/misc/Unsafe.java @@ -0,0 +1,153 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package sun.misc; + +import java.lang.reflect.Field; + +public final class Unsafe { + private void Unsafe() { } + + private static final Unsafe theUnsafe = new Unsafe(); + + public static Unsafe getUnsafe() { + return theUnsafe; + } + + public native long allocateMemory(long bytes); + + public native void setMemory + (Object base, long offset, long count, byte value); + + public native void freeMemory(long address); + + public native byte getByte(long address); + + public native void putByte(long address, byte x); + + public native short getShort(long address); + + public native void putShort(long address, short x); + + public native char getChar(long address); + + public native void putChar(long address, char x); + + public native int getInt(long address); + + public native void putInt(long address, int x); + + public native long getLong(long address); + + public native void putLong(long address, long x); + + public native float getFloat(long address); + + public native void putFloat(long address, float x); + + public native double getDouble(long address); + + public native void putDouble(long address, double x); + + public native boolean getBooleanVolatile(Object o, long offset); + + public native void putBooleanVolatile(Object o, long offset, boolean x); + + public native byte getByteVolatile(Object o, long offset); + + public native void putByteVolatile(Object o, long offset, byte x); + + public native short getShortVolatile(Object o, long offset); + + public native void putShortVolatile(Object o, long offset, short x); + + public native char getCharVolatile(Object o, long offset); + + public native void putCharVolatile(Object o, long offset, char x); + + public native int getIntVolatile(Object o, long offset); + + public native void putIntVolatile(Object o, long offset, int x); + + public native float getFloatVolatile(Object o, long offset); + + public native void putFloatVolatile(Object o, long offset, float x); + + public native double getDoubleVolatile(Object o, long offset); + + public native void putDoubleVolatile(Object o, long offset, double x); + + public native long getLongVolatile(Object o, long offset); + + public native void putLongVolatile(Object o, long offset, long x); + + public long getLong(Object o, long offset) { + return getLongVolatile(o, offset); + } + + public void putLong(Object o, long offset, long x) { + putLongVolatile(o, offset, x); + } + + public double getDouble(Object o, long offset) { + return getDoubleVolatile(o, offset); + } + + public void putDouble(Object o, long offset, double x) { + putDoubleVolatile(o, offset, x); + } + + public native void putOrderedLong(Object o, long offset, long x); + + public native void putOrderedInt(Object o, long offset, int x); + + public native Object getObject(Object o, long offset); + + public native void putObject(Object o, long offset, Object x); + + public native void putObjectVolatile(Object o, long offset, Object x); + + public native void putOrderedObject(Object o, long offset, Object x); + + public native Object getObjectVolatile(Object o, long offset); + + public native long getAddress(long address); + + public native void putAddress(long address, long x); + + public native int arrayBaseOffset(Class arrayClass); + + public native int arrayIndexScale(Class arrayClass); + + public native long objectFieldOffset(Field field); + + public native void park(boolean absolute, long time); + + public native void unpark(Object target); + + public native void copyMemory(Object srcBase, long srcOffset, + Object destBase, long destOffset, + long count); + + public native boolean compareAndSwapInt(Object o, long offset, int old, + int new_); + + public native boolean compareAndSwapLong(Object o, long offset, + long old, long new_); + + public native boolean compareAndSwapObject(Object o, long offset, Object old, + Object new_); + + public void copyMemory(long src, long dst, long count) { + copyMemory(null, src, null, dst, count); + } + + public native void throwException(Throwable t); +} diff --git a/sgx-jvm/avian/classpath/sun/reflect/ConstantPool.java b/sgx-jvm/avian/classpath/sun/reflect/ConstantPool.java new file mode 100644 index 0000000000..ad29977933 --- /dev/null +++ b/sgx-jvm/avian/classpath/sun/reflect/ConstantPool.java @@ -0,0 +1,13 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package sun.reflect; + +public class ConstantPool { } diff --git a/sgx-jvm/avian/cmake/Platform.cmake b/sgx-jvm/avian/cmake/Platform.cmake new file mode 100644 index 0000000000..20116488a7 --- /dev/null +++ b/sgx-jvm/avian/cmake/Platform.cmake @@ -0,0 +1,18 @@ +IF (APPLE) + INCLUDE_DIRECTORIES ( /Developer/Headers/FlatCarbon ) + FIND_LIBRARY(CORE_FOUNDATION_LIBRARY CoreFoundation) + + MARK_AS_ADVANCED (CORE_FOUNDATION_LIBRARY) + + SET(PLATFORM_LIBS ${CORE_FOUNDATION_LIBRARY}) +ENDIF() + +IF (MSVC) + SET(PLATFORM_CXX_FLAGS "/Wall") +ELSE() + SET(PLATFORM_CXX_FLAGS "-Wall -Werror -fno-exceptions -std=c++0x") + SET(PLATFORM_LIBS ${PLATFORM_LIBS} pthread dl) +ENDIF() + +find_package(ZLIB REQUIRED) +include_directories(${ZLIB_INCLUDE_DIRS}) diff --git a/sgx-jvm/avian/docker/Dockerfile b/sgx-jvm/avian/docker/Dockerfile new file mode 100644 index 0000000000..f4aaa75032 --- /dev/null +++ b/sgx-jvm/avian/docker/Dockerfile @@ -0,0 +1,83 @@ +FROM debian:jessie +MAINTAINER Joshua Warner, joshuawarner32@gmail.com + +RUN echo 'deb http://http.debian.net/debian jessie-backports main' >> /etc/apt/sources.list && \ + echo 'deb-src http://http.debian.net/debian jessie-backports main' >> /etc/apt/sources.list && \ + dpkg --add-architecture i386 && \ + apt-get update && \ + mkdir /var/src/ + +# Install base dependencies and build tools, general debugging tools +RUN apt-get install -y \ + build-essential \ + g++-4.9 \ + zlib1g-dev \ + openjdk-8-jdk \ + locales \ + --no-install-recommends && \ + apt-get clean all + +# Fix utf-8 default locale - we'd otherwise have trouble with the Strings and Misc tests +RUN dpkg-reconfigure locales && \ + locale-gen C.UTF-8 && \ + /usr/sbin/update-locale LANG=C.UTF-8 + +ENV LC_ALL C.UTF-8 + +# Set JAVA_HOME for avian's benefit +ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64 + +# Add i386 libraries +RUN apt-get install -y \ + libc6-dev-i386 && \ + apt-get download \ + zlib1g-dev:i386 && \ + dpkg -x *.deb / && \ + rm *.deb && \ + apt-get clean all + +# Install cross-compile toolchain and emulator for testing +RUN apt-get install -y \ + mingw-w64 \ + wget \ + unzip \ + --no-install-recommends && \ + apt-get clean all + +# Download win32 and win64 adjacent to avian +RUN cd /var/src/ && \ + wget https://github.com/ReadyTalk/win32/archive/master.zip -O win32.zip && \ + unzip win32.zip && \ + rm win32.zip && \ + mv win32-* win32 && \ + wget https://github.com/ReadyTalk/win64/archive/master.zip -O win64.zip && \ + unzip win64.zip && \ + rm win64.zip && \ + mv win64-* win64 + +# Add openjdk-src stuff +RUN apt-get install -y \ + libcups2-dev \ + libgconf2-dev && \ + mkdir /var/src/openjdk/ && \ + cd /var/src/openjdk/ && \ + apt-get source openjdk-8 && \ + apt-get clean all && \ + find /var/src/openjdk && \ + rm /var/src/openjdk/*.gz /var/src/openjdk/*.dsc && \ + cd /var/src/openjdk/ && \ + tar -xf /var/src/openjdk/openjdk*/jdk.tar.xz && \ + mv /var/src/openjdk/jdk-*/src /var/src/openjdk-src && \ + rm -rf /var/src/openjdk && \ + apt-get clean all + +# Download/extract lzma source +RUN mkdir /var/src/lzma && \ + cd /var/src/lzma && \ + apt-get install -y p7zip && \ + wget http://www.7-zip.org/a/lzma1507.7z -O lzma.7z && \ + p7zip -d lzma.7z + +# Avian build location +VOLUME /var/src/avian +WORKDIR /var/src/avian diff --git a/sgx-jvm/avian/docker/arm/Dockerfile b/sgx-jvm/avian/docker/arm/Dockerfile new file mode 100644 index 0000000000..69278214e2 --- /dev/null +++ b/sgx-jvm/avian/docker/arm/Dockerfile @@ -0,0 +1,69 @@ +FROM joshuawarner32/avian-build +MAINTAINER Joshua Warner, joshuawarner32@gmail.com + +RUN dpkg --add-architecture armel && \ + apt-get update && \ + mkdir -p /opt/arm && \ + apt-get download libc6-dev:armel \ + linux-headers-3.13-1-all-armel:armel \ + linux-libc-dev:armel \ + libc6:armel \ + zlib1g-dev:armel \ + zlib1g:armel && \ + for x in *.deb; do \ + dpkg -x $x /opt/arm; \ + done && \ + rm *.deb && \ + apt-get install -y \ + wget \ + libgmp-dev \ + libmpfr-dev \ + libmpc-dev \ + libisl-dev && \ + apt-get clean all && \ + for x in $(find /opt/arm -type l); do \ + r=$(readlink "$x" | sed 's,^/,/opt/arm/,g'); \ + rm "$x"; \ + ln -s "$r" "$x"; \ + done + +RUN mkdir -p /var/src + +# Build & install binutils +RUN wget ftp://sourceware.org/pub/binutils/snapshots/binutils-2.23.91.tar.bz2 -O /var/src/binutils.tar.bz2 && \ + cd /var/src/ && tar -xjf binutils.tar.bz2 && rm binutils.tar.bz2 && \ + cd /var/src/binutils* && \ + mkdir build && \ + cd build && \ + ../configure \ + --target=arm-linux-gnueabi \ + --prefix=/opt/arm \ + --disable-multilib \ + --program-prefix=arm-linux-gnueabi- \ + --with-sysroot=/opt/arm \ + --with-headers=/opt/arm/usr/include && \ + make && \ + make install && \ + cd /var/src && \ + rm -rf * + +# build & install gcc +RUN wget http://www.netgull.com/gcc/releases/gcc-4.8.2/gcc-4.8.2.tar.bz2 -O /var/src/gcc.tar.bz2 && \ + cd /var/src/ && tar -xjf gcc.tar.bz2 && rm gcc.tar.bz2 && \ + cd /var/src/gcc* && \ + mkdir build && \ + cd build && \ + ../configure \ + --target=arm-linux-gnueabi \ + --enable-languages=c,c++ \ + --prefix=/opt/arm \ + --disable-multilib \ + --program-prefix=arm-linux-gnueabi- \ + --with-sysroot=/opt/arm \ + --with-headers=/opt/arm/usr/include && \ + make && \ + make install && \ + cd /var/src && \ + rm -rf * + +ENV PATH $PATH:/opt/arm/bin diff --git a/sgx-jvm/avian/docker/arm64/Dockerfile b/sgx-jvm/avian/docker/arm64/Dockerfile new file mode 100644 index 0000000000..c39413a31e --- /dev/null +++ b/sgx-jvm/avian/docker/arm64/Dockerfile @@ -0,0 +1,71 @@ +FROM joshuawarner32/avian-build +MAINTAINER Joshua Warner, joshuawarner32@gmail.com + +RUN dpkg --add-architecture arm64 && \ + apt-get update && \ + mkdir -p /opt/arm64 && \ + apt-get download libc6-dev:arm64 \ + linux-headers-3.16.0-4-all-arm64:arm64 \ + linux-libc-dev:arm64 \ + libc6:arm64 \ + zlib1g-dev:arm64 \ + zlib1g:arm64 && \ + for x in *.deb; do \ + dpkg -x $x /opt/arm64; \ + done && \ + rm *.deb && \ + apt-get install -y \ + wget \ + libgmp-dev \ + libmpfr-dev \ + libmpc-dev \ + libisl-dev && \ + apt-get clean all && \ + for x in $(find /opt/arm64 -type l); do \ + r=$(readlink "$x" | sed 's,^/,/opt/arm64/,g'); \ + rm "$x"; \ + ln -s "$r" "$x"; \ + done + +RUN mkdir -p /var/src + +# Build & install binutils +RUN wget ftp://sourceware.org/pub/binutils/snapshots/binutils-2.23.91.tar.bz2 -O /var/src/binutils.tar.bz2 && \ + cd /var/src/ && tar -xjf binutils.tar.bz2 && rm binutils.tar.bz2 && \ + cd /var/src/binutils* && \ + mkdir build && \ + cd build && \ + ../configure \ + --target=aarch64-linux-gnu \ + --prefix=/opt/arm64 \ + --disable-multilib \ + --program-prefix=aarch64-linux-gnu- \ + --with-sysroot=/opt/arm64 \ + --with-headers=/opt/arm64/usr/include \ + --disable-werror && \ + make && \ + make install && \ + cd /var/src && \ + rm -rf * + +# build & install gcc +RUN wget http://www.netgull.com/gcc/releases/gcc-4.8.2/gcc-4.8.2.tar.bz2 -O /var/src/gcc.tar.bz2 && \ + cd /var/src/ && tar -xjf gcc.tar.bz2 && rm gcc.tar.bz2 && \ + cd /var/src/gcc* && \ + mkdir build && \ + cd build && \ + ../configure \ + --target=aarch64-linux-gnu \ + --enable-languages=c,c++ \ + --prefix=/opt/arm64 \ + --disable-multilib \ + --program-prefix=aarch64-linux-gnu- \ + --with-sysroot=/opt/arm64 \ + --with-headers=/opt/arm64/usr/include \ + --disable-werror && \ + make && \ + make install && \ + cd /var/src && \ + rm -rf * + +ENV PATH $PATH:/opt/arm64/bin diff --git a/sgx-jvm/avian/docker/build.sh b/sgx-jvm/avian/docker/build.sh new file mode 100755 index 0000000000..ef61007dce --- /dev/null +++ b/sgx-jvm/avian/docker/build.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +if test $# -eq 0; then + echo "Usage: $0 [--container ] -- " + echo "Ex: $0 make test" + echo "Ex: $0 ./test/ci.sh" + echo "Ex: $0 --container joshuawarner32/avian-build-windows -- make platform=windows" + exit 1 +fi + +THE_USER="-u $(id -u "${USER}")" + +while test $# -gt 1 ; do + key="$1" + case $key in + -c|--container) + shift + CONTAINER="$1" + shift + ;; + -r|--root) + shift + THE_USER= + ;; + --) + shift + break + ;; + *) + break + ;; + esac +done + +if test -z $CONTAINER; then + CONTAINER=joshuawarner32/avian-build +fi + +DIR=$(cd $(dirname "$0") && cd .. && pwd) + +docker run --rm -i -t -v "${DIR}":/var/src/avian ${THE_USER} "${CONTAINER}" "${@}" diff --git a/sgx-jvm/avian/gradle.properties b/sgx-jvm/avian/gradle.properties new file mode 100644 index 0000000000..fbcdf372fe --- /dev/null +++ b/sgx-jvm/avian/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.daemon=true + +group=com.readytalk.avian +version=1.3.0-SNAPSHOT \ No newline at end of file diff --git a/sgx-jvm/avian/gradle/wrapper/gradle-wrapper.jar b/sgx-jvm/avian/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000..b761216703 Binary files /dev/null and b/sgx-jvm/avian/gradle/wrapper/gradle-wrapper.jar differ diff --git a/sgx-jvm/avian/gradle/wrapper/gradle-wrapper.properties b/sgx-jvm/avian/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..2ce5f17a6f --- /dev/null +++ b/sgx-jvm/avian/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jan 02 11:31:32 MST 2015 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=http\://services.gradle.org/distributions/gradle-2.0-bin.zip diff --git a/sgx-jvm/avian/gradlew b/sgx-jvm/avian/gradlew new file mode 100755 index 0000000000..91a7e269e1 --- /dev/null +++ b/sgx-jvm/avian/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/sgx-jvm/avian/gradlew.bat b/sgx-jvm/avian/gradlew.bat new file mode 100644 index 0000000000..aec99730b4 --- /dev/null +++ b/sgx-jvm/avian/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/sgx-jvm/avian/include/avian/codegen/architecture.h b/sgx-jvm/avian/include/avian/codegen/architecture.h new file mode 100644 index 0000000000..7bdad3eeab --- /dev/null +++ b/sgx-jvm/avian/include/avian/codegen/architecture.h @@ -0,0 +1,164 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_ARCHITECTURE_H +#define AVIAN_CODEGEN_ARCHITECTURE_H + +#include "ir.h" +#include "registers.h" + +namespace vm { +class Zone; +} + +namespace avian { + +namespace util { +class Alloc; +} + +namespace codegen { + +class Assembler; + +class OperandMask { + public: + uint8_t typeMask; + RegisterMask lowRegisterMask; + RegisterMask highRegisterMask; + + OperandMask(uint8_t typeMask, + RegisterMask lowRegisterMask, + RegisterMask highRegisterMask) + : typeMask(typeMask), + lowRegisterMask(lowRegisterMask), + highRegisterMask(highRegisterMask) + { + } + + OperandMask() : typeMask(~0), lowRegisterMask(AnyRegisterMask), highRegisterMask(AnyRegisterMask) + { + } + + void setLowHighRegisterMasks(RegisterMask lowRegisterMask, RegisterMask highRegisterMask) { + this->lowRegisterMask = lowRegisterMask; + this->highRegisterMask = highRegisterMask; + } +}; + +class Architecture { + public: + virtual unsigned floatRegisterSize() = 0; + + virtual const RegisterFile* registerFile() = 0; + + virtual Register scratch() = 0; + virtual Register stack() = 0; + virtual Register thread() = 0; + virtual Register returnLow() = 0; + virtual Register returnHigh() = 0; + virtual Register virtualCallTarget() = 0; + virtual Register virtualCallIndex() = 0; + + virtual ir::TargetInfo targetInfo() = 0; + + virtual bool bigEndian() = 0; + + virtual uintptr_t maximumImmediateJump() = 0; + + virtual bool alwaysCondensed(lir::BinaryOperation op) = 0; + virtual bool alwaysCondensed(lir::TernaryOperation op) = 0; + + virtual bool reserved(Register register_) = 0; + + virtual unsigned frameFootprint(unsigned footprint) = 0; + virtual unsigned argumentFootprint(unsigned footprint) = 0; + virtual bool argumentAlignment() = 0; + virtual bool argumentRegisterAlignment() = 0; + virtual unsigned argumentRegisterCount() = 0; + virtual Register argumentRegister(unsigned index) = 0; + + virtual bool hasLinkRegister() = 0; + + virtual unsigned stackAlignmentInWords() = 0; + + virtual bool matchCall(void* returnAddress, void* target) = 0; + + virtual void updateCall(lir::UnaryOperation op, + void* returnAddress, + void* newTarget) = 0; + + virtual void setConstant(void* dst, uint64_t constant) = 0; + + virtual unsigned alignFrameSize(unsigned sizeInWords) = 0; + + virtual void nextFrame(void* start, + unsigned size, + unsigned footprint, + void* link, + bool mostRecent, + int targetParameterFootprint, + void** ip, + void** stack) = 0; + virtual void* frameIp(void* stack) = 0; + virtual unsigned frameHeaderSize() = 0; + virtual unsigned frameReturnAddressSize() = 0; + virtual unsigned frameFooterSize() = 0; + virtual int returnAddressOffset() = 0; + virtual int framePointerOffset() = 0; + + virtual void plan(lir::UnaryOperation op, + unsigned aSize, + OperandMask& aMask, + bool* thunk) = 0; + + virtual void planSource(lir::BinaryOperation op, + unsigned aSize, + OperandMask& aMask, + unsigned bSize, + bool* thunk) = 0; + + virtual void planDestination(lir::BinaryOperation op, + unsigned aSize, + const OperandMask& aMask, + unsigned bSize, + OperandMask& bMask) = 0; + + virtual void planMove(unsigned size, + OperandMask& src, + OperandMask& tmp, + const OperandMask& dst) = 0; + + virtual void planSource(lir::TernaryOperation op, + unsigned aSize, + OperandMask& aMask, + unsigned bSize, + OperandMask& bMask, + unsigned cSize, + bool* thunk) = 0; + + virtual void planDestination(lir::TernaryOperation op, + unsigned aSize, + const OperandMask& aMask, + unsigned bSize, + const OperandMask& bMask, + unsigned cSize, + OperandMask& cMask) = 0; + + virtual Assembler* makeAssembler(util::Alloc*, vm::Zone*) = 0; + + virtual void acquire() = 0; + virtual void release() = 0; +}; + +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ARCHITECTURE_H diff --git a/sgx-jvm/avian/include/avian/codegen/assembler.h b/sgx-jvm/avian/include/avian/codegen/assembler.h new file mode 100644 index 0000000000..ab6a990a4c --- /dev/null +++ b/sgx-jvm/avian/include/avian/codegen/assembler.h @@ -0,0 +1,116 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_H +#define AVIAN_CODEGEN_ASSEMBLER_H + +#include +#include "avian/zone.h" + +#include +#include +#include + +namespace avian { +namespace codegen { + +class Architecture; + +class OperandInfo { + public: + const unsigned size; + const lir::Operand::Type type; + lir::Operand* const operand; + + inline OperandInfo(unsigned size, + lir::Operand::Type type, + lir::Operand* operand) + : size(size), type(type), operand(operand) + { + } +}; + +#ifdef AVIAN_TAILS +const bool TailCalls = true; +#else +const bool TailCalls = false; +#endif + +#ifdef AVIAN_USE_FRAME_POINTER +const bool UseFramePointer = true; +#else +const bool UseFramePointer = false; +#endif + +class Assembler { + public: + class Client { + public: + virtual Register acquireTemporary(RegisterMask mask = AnyRegisterMask) = 0; + virtual void releaseTemporary(Register r) = 0; + + virtual void save(Register r) = 0; + }; + + class Block { + public: + virtual unsigned resolve(unsigned start, Block* next) = 0; + }; + + virtual void setClient(Client* client) = 0; + + virtual Architecture* arch() = 0; + + virtual void checkStackOverflow(uintptr_t handler, + unsigned stackLimitOffsetFromThread) = 0; + virtual void saveFrame(unsigned stackOffset, unsigned ipOffset) = 0; + virtual void pushFrame(unsigned argumentCount, ...) = 0; + virtual void allocateFrame(unsigned footprint) = 0; + virtual void adjustFrame(unsigned difference) = 0; + virtual void popFrame(unsigned footprint) = 0; + virtual void popFrameForTailCall(unsigned footprint, + int offset, + Register returnAddressSurrogate, + Register framePointerSurrogate) = 0; + virtual void popFrameAndPopArgumentsAndReturn(unsigned frameFootprint, + unsigned argumentFootprint) = 0; + virtual void popFrameAndUpdateStackAndReturn(unsigned frameFootprint, + unsigned stackOffsetFromThread) + = 0; + + virtual void apply(lir::Operation op) = 0; + virtual void apply(lir::UnaryOperation op, OperandInfo a) = 0; + virtual void apply(lir::BinaryOperation op, OperandInfo a, OperandInfo b) = 0; + virtual void apply(lir::TernaryOperation op, + OperandInfo a, + OperandInfo b, + OperandInfo c) = 0; + + virtual void setDestination(uint8_t* dst) = 0; + + virtual void write() = 0; + + virtual Promise* offset(bool forTrace = false) = 0; + + virtual Block* endBlock(bool startNew) = 0; + + virtual void endEvent() = 0; + + virtual unsigned length() = 0; + + virtual unsigned footerSize() = 0; + + virtual void dispose() = 0; +}; + +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_H diff --git a/sgx-jvm/avian/include/avian/codegen/compiler.h b/sgx-jvm/avian/include/avian/codegen/compiler.h new file mode 100644 index 0000000000..03c03ed855 --- /dev/null +++ b/sgx-jvm/avian/include/avian/codegen/compiler.h @@ -0,0 +1,199 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_COMPILER_H +#define AVIAN_CODEGEN_COMPILER_H + +#include +#include +#include "avian/zone.h" +#include "assembler.h" +#include "ir.h" + +namespace avian { +namespace codegen { + +class TraceHandler { + public: + virtual void handleTrace(Promise* address, unsigned argumentIndex) = 0; +}; + +template +class Args { + public: + ir::Value* values[N]; + + template + Args(Ts... ts) +#ifndef _MSC_VER + : values{ts...} +#endif + { +#ifdef _MSC_VER + setArrayElements(values, ts...); +#endif + } + + operator util::Slice() + { + return util::Slice(&values[0], N); + } +}; + +inline Args<0> args() +{ + return Args<0>(); +} + +inline Args<1> args(ir::Value* first) +{ + return Args<1>{first}; +} + +template +inline Args<1 + util::ArgumentCount::Result> args(ir::Value* first, + Ts... rest) +{ + return Args<1 + util::ArgumentCount::Result>{first, rest...}; +} + +class Compiler { + public: + class Client { + public: + virtual intptr_t getThunk(lir::UnaryOperation op, unsigned size) = 0; + virtual intptr_t getThunk(lir::BinaryOperation op, + unsigned size, + unsigned resultSize) = 0; + virtual intptr_t getThunk(lir::TernaryOperation op, + unsigned size, + unsigned resultSize, + bool* threadParameter) = 0; + }; + + static const unsigned Aligned = 1 << 0; + static const unsigned NoReturn = 1 << 1; + static const unsigned TailJump = 1 << 2; + static const unsigned LongJumpOrCall = 1 << 3; + + class State { + }; + + virtual State* saveState() = 0; + virtual void restoreState(State* state) = 0; + + virtual void init(unsigned logicalCodeSize, + unsigned parameterFootprint, + unsigned localFootprint, + unsigned alignedFrameSize) = 0; + + virtual void extendLogicalCode(unsigned more) = 0; + + virtual void visitLogicalIp(unsigned logicalIp) = 0; + virtual void startLogicalIp(unsigned logicalIp) = 0; + + virtual Promise* machineIp(unsigned logicalIp) = 0; + + virtual Promise* poolAppend(intptr_t value) = 0; + virtual Promise* poolAppendPromise(Promise* value) = 0; + + virtual ir::Value* constant(int64_t value, ir::Type type) = 0; + virtual ir::Value* promiseConstant(Promise* value, ir::Type type) = 0; + virtual ir::Value* address(ir::Type type, Promise* address) = 0; + virtual ir::Value* memory(ir::Value* base, + ir::Type type, + int displacement = 0, + ir::Value* index = 0) = 0; + + virtual ir::Value* threadRegister() = 0; + + virtual void push(ir::Type type, ir::Value* value) = 0; + virtual void save(ir::Type type, ir::Value* value) = 0; + virtual ir::Value* pop(ir::Type type) = 0; + virtual void pushed(ir::Type type) = 0; + virtual void popped(unsigned footprint) = 0; + virtual unsigned topOfStack() = 0; + virtual ir::Value* peek(unsigned footprint, unsigned index) = 0; + + virtual ir::Value* nativeCall(ir::Value* address, + unsigned flags, + TraceHandler* traceHandler, + ir::Type resultType, + util::Slice arguments) = 0; + + virtual ir::Value* stackCall(ir::Value* address, + unsigned flags, + TraceHandler* traceHandler, + ir::Type resultType, + util::Slice arguments) = 0; + + virtual void return_(ir::Value* value) = 0; + virtual void return_() = 0; + + virtual void initLocal(unsigned index, ir::Type type) = 0; + virtual void initLocalsFromLogicalIp(unsigned logicalIp) = 0; + virtual void storeLocal(ir::Value* src, unsigned index) = 0; + virtual ir::Value* loadLocal(ir::Type type, unsigned index) = 0; + virtual void saveLocals() = 0; + + virtual void checkBounds(ir::Value* object, + unsigned lengthOffset, + ir::Value* index, + intptr_t handler) = 0; + + virtual ir::Value* truncateThenExtend(ir::ExtendMode extendMode, + ir::Type extendType, + ir::Type truncateType, + ir::Value* src) = 0; + + virtual ir::Value* truncate(ir::Type type, ir::Value* src) = 0; + + virtual void store(ir::Value* src, ir::Value* dst) = 0; + virtual ir::Value* load(ir::ExtendMode extendMode, + ir::Value* src, + ir::Type dstType) = 0; + + virtual void condJump(lir::TernaryOperation op, + ir::Value* a, + ir::Value* b, + ir::Value* address) = 0; + + virtual void jmp(ir::Value* address) = 0; + virtual void exit(ir::Value* address) = 0; + + virtual ir::Value* binaryOp(lir::TernaryOperation op, + ir::Type type, + ir::Value* a, + ir::Value* b) = 0; + virtual ir::Value* unaryOp(lir::BinaryOperation op, ir::Value* a) = 0; + virtual void nullaryOp(lir::Operation op) = 0; + + virtual ir::Value* f2f(ir::Type resType, ir::Value* a) = 0; + virtual ir::Value* f2i(ir::Type resType, ir::Value* a) = 0; + virtual ir::Value* i2f(ir::Type resType, ir::Value* a) = 0; + + virtual void compile(uintptr_t stackOverflowHandler, + unsigned stackLimitOffset) = 0; + virtual unsigned resolve(uint8_t* dst) = 0; + virtual unsigned poolSize() = 0; + virtual void write() = 0; + + virtual void dispose() = 0; +}; + +Compiler* makeCompiler(vm::System* system, + Assembler* assembler, + vm::Zone* zone, + Compiler::Client* client); + +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_COMPILER_H diff --git a/sgx-jvm/avian/include/avian/codegen/ir.h b/sgx-jvm/avian/include/avian/codegen/ir.h new file mode 100644 index 0000000000..ae0f2663aa --- /dev/null +++ b/sgx-jvm/avian/include/avian/codegen/ir.h @@ -0,0 +1,152 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_IR_H +#define AVIAN_CODEGEN_IR_H + +namespace avian { +namespace codegen { +namespace ir { + +class TargetInfo { + public: + unsigned pointerSize; + + explicit TargetInfo(unsigned pointerSize) : pointerSize(pointerSize) + { + } +}; + +class Type { + public: + enum Flavor { + // A GC-visiible reference + Object, + + // GC-invisible types + Integer, + Float, + Address, + + // Represents the lack of a return value + // TODO: remove when possible + Void, + }; + + typedef int16_t TypeDesc; + +#define TY_DESC(flavor, size) ((flavor & 0xff) | ((size & 0xff) << 8)) + // TODO: once we upgrade to c++11, these should become plain constants (rather + // than function calls). + // The constructor will need to be declared 'constexpr'. + static inline Type void_() + { + return TY_DESC(Void, 0); + } + static inline Type object() + { + return TY_DESC(Object, -1); + } + static inline Type iptr() + { + return TY_DESC(Integer, -1); + } + static inline Type i1() + { + return TY_DESC(Integer, 1); + } + static inline Type i2() + { + return TY_DESC(Integer, 2); + } + static inline Type i4() + { + return TY_DESC(Integer, 4); + } + static inline Type i8() + { + return TY_DESC(Integer, 8); + } + static inline Type f4() + { + return TY_DESC(Float, 4); + } + static inline Type f8() + { + return TY_DESC(Float, 8); + } + static inline Type addr() + { + return TY_DESC(Address, -1); + } +#undef TY_DESC + + private: + TypeDesc desc; + + friend class Types; + + // TODO: once we move to c++11, declare this 'constexpr', to allow + // compile-time constants of this type. + /* constexpr */ Type(TypeDesc desc) : desc(desc) + { + } + + public: + inline Flavor flavor() const + { + return (Flavor)(desc & 0xff); + } + + // If the size isn't known without inspecting the TargetInfo, returns -1. + // Otherwise, matches size(TargetInfo). + inline int rawSize() const + { + return desc >> 8; + } + + inline unsigned size(const TargetInfo& t) const + { + int s = rawSize(); + if (s < 0) { + return t.pointerSize; + } + return (unsigned)s; + } + + inline bool operator==(const Type& other) const + { + return desc == other.desc; + } + + inline bool operator!=(const Type& other) const + { + return !(*this == other); + } +}; + +enum class ExtendMode { Signed, Unsigned }; + +enum class CallingConvention { Native, Avian }; + +class Value { + public: + ir::Type type; + + Value(ir::Type type) : type(type) + { + } +}; + +} // namespace ir +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_IR_H diff --git a/sgx-jvm/avian/include/avian/codegen/lir-ops.inc.cpp b/sgx-jvm/avian/include/avian/codegen/lir-ops.inc.cpp new file mode 100644 index 0000000000..cf038c6647 --- /dev/null +++ b/sgx-jvm/avian/include/avian/codegen/lir-ops.inc.cpp @@ -0,0 +1,62 @@ +LIR_OP_0(Return) +LIR_OP_0(LoadBarrier) +LIR_OP_0(StoreStoreBarrier) +LIR_OP_0(StoreLoadBarrier) +LIR_OP_0(Trap) + +LIR_OP_1(Call) +LIR_OP_1(LongCall) +LIR_OP_1(AlignedLongCall) +LIR_OP_1(AlignedCall) +LIR_OP_1(Jump) +LIR_OP_1(LongJump) +LIR_OP_1(AlignedLongJump) +LIR_OP_1(AlignedJump) + +LIR_OP_2(Move) +LIR_OP_2(MoveLow) +LIR_OP_2(MoveHigh) +LIR_OP_2(MoveZ) +LIR_OP_2(Negate) +LIR_OP_2(FloatNegate) +LIR_OP_2(Float2Float) +LIR_OP_2(Float2Int) +LIR_OP_2(Int2Float) +LIR_OP_2(FloatSquareRoot) +LIR_OP_2(FloatAbsolute) +LIR_OP_2(Absolute) + +LIR_OP_3(Add) +LIR_OP_3(Subtract) +LIR_OP_3(Multiply) +LIR_OP_3(Divide) +LIR_OP_3(Remainder) +LIR_OP_3(ShiftLeft) +LIR_OP_3(ShiftRight) +LIR_OP_3(UnsignedShiftRight) +LIR_OP_3(And) +LIR_OP_3(Or) +LIR_OP_3(Xor) +LIR_OP_3(FloatAdd) +LIR_OP_3(FloatSubtract) +LIR_OP_3(FloatMultiply) +LIR_OP_3(FloatDivide) +LIR_OP_3(FloatRemainder) +LIR_OP_3(FloatMax) +LIR_OP_3(FloatMin) +LIR_OP_3(JumpIfLess) +LIR_OP_3(JumpIfGreater) +LIR_OP_3(JumpIfLessOrEqual) +LIR_OP_3(JumpIfGreaterOrEqual) +LIR_OP_3(JumpIfEqual) +LIR_OP_3(JumpIfNotEqual) +LIR_OP_3(JumpIfFloatEqual) +LIR_OP_3(JumpIfFloatNotEqual) +LIR_OP_3(JumpIfFloatLess) +LIR_OP_3(JumpIfFloatGreater) +LIR_OP_3(JumpIfFloatLessOrEqual) +LIR_OP_3(JumpIfFloatGreaterOrEqual) +LIR_OP_3(JumpIfFloatLessOrUnordered) +LIR_OP_3(JumpIfFloatGreaterOrUnordered) +LIR_OP_3(JumpIfFloatLessOrEqualOrUnordered) +LIR_OP_3(JumpIfFloatGreaterOrEqualOrUnordered) diff --git a/sgx-jvm/avian/include/avian/codegen/lir.h b/sgx-jvm/avian/include/avian/codegen/lir.h new file mode 100644 index 0000000000..444a661e81 --- /dev/null +++ b/sgx-jvm/avian/include/avian/codegen/lir.h @@ -0,0 +1,184 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_LIR_H +#define AVIAN_CODEGEN_LIR_H + +#include + +namespace avian { +namespace codegen { +class Promise; + +namespace lir { +enum Operation { +#define LIR_OP_0(x) x, +#define LIR_OP_1(x) +#define LIR_OP_2(x) +#define LIR_OP_3(x) +#include "lir-ops.inc.cpp" +#undef LIR_OP_0 +#undef LIR_OP_1 +#undef LIR_OP_2 +#undef LIR_OP_3 +}; + +const unsigned OperationCount = Trap + 1; + +enum UnaryOperation { +#define LIR_OP_0(x) +#define LIR_OP_1(x) x, +#define LIR_OP_2(x) +#define LIR_OP_3(x) +#include "lir-ops.inc.cpp" +#undef LIR_OP_0 +#undef LIR_OP_1 +#undef LIR_OP_2 +#undef LIR_OP_3 + NoUnaryOperation = -1 +}; + +const unsigned UnaryOperationCount = AlignedJump + 1; + +enum BinaryOperation { +#define LIR_OP_0(x) +#define LIR_OP_1(x) +#define LIR_OP_2(x) x, +#define LIR_OP_3(x) +#include "lir-ops.inc.cpp" +#undef LIR_OP_0 +#undef LIR_OP_1 +#undef LIR_OP_2 +#undef LIR_OP_3 + NoBinaryOperation = -1 +}; + +const unsigned BinaryOperationCount = Absolute + 1; + +enum TernaryOperation { +#define LIR_OP_0(x) +#define LIR_OP_1(x) +#define LIR_OP_2(x) +#define LIR_OP_3(x) x, +#include "lir-ops.inc.cpp" +#undef LIR_OP_0 +#undef LIR_OP_1 +#undef LIR_OP_2 +#undef LIR_OP_3 + NoTernaryOperation = -1 +}; + +const unsigned TernaryOperationCount = JumpIfFloatGreaterOrEqualOrUnordered + 1; + +const unsigned NonBranchTernaryOperationCount = FloatMin + 1; +const unsigned BranchOperationCount = JumpIfFloatGreaterOrEqualOrUnordered + - FloatMin; + +enum ValueType { ValueGeneral, ValueFloat }; + +inline bool isBranch(lir::TernaryOperation op) +{ + return op > FloatMin; +} + +inline bool isFloatBranch(lir::TernaryOperation op) +{ + return op > JumpIfNotEqual; +} + +inline bool isGeneralBranch(lir::TernaryOperation op) +{ + return isBranch(op) && !isFloatBranch(op); +} + +inline bool isGeneralBinaryOp(lir::TernaryOperation op) +{ + return op < FloatAdd; +} + +inline bool isFloatBinaryOp(lir::TernaryOperation op) +{ + return op >= FloatAdd && op <= FloatMin; +} + +inline bool isGeneralUnaryOp(lir::BinaryOperation op) +{ + return op == Negate || op == Absolute; +} + +inline bool isFloatUnaryOp(lir::BinaryOperation op) +{ + return op == FloatNegate || op == FloatSquareRoot || op == FloatAbsolute; +} + +class Operand { +public: + + enum class Type { + Constant, + Address, + RegisterPair, + Memory + }; + + const static unsigned TypeCount = (unsigned)Type::Memory + 1; + + const static unsigned ConstantMask = 1 << (unsigned)Type::Constant; + const static unsigned AddressMask = 1 << (unsigned)Type::Address; + const static unsigned RegisterPairMask = 1 << (unsigned)Type::RegisterPair; + const static unsigned MemoryMask = 1 << (unsigned)Type::Memory; +}; + +class Constant : public Operand { + public: + Constant(Promise* value) : value(value) + { + } + + Promise* value; +}; + +class Address : public Operand { + public: + Address(Promise* address) : address(address) + { + } + + Promise* address; +}; + +class RegisterPair : public Operand { + public: + RegisterPair(Register low, Register high = NoRegister) : low(low), high(high) + { + } + + Register low; + Register high; +}; + +class Memory : public Operand { + public: + Memory(Register base, int offset, Register index = NoRegister, unsigned scale = 1) + : base(base), offset(offset), index(index), scale(scale) + { + } + + Register base; + int offset; + Register index; + unsigned scale; +}; + +} // namespace lir +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_LIR_H diff --git a/sgx-jvm/avian/include/avian/codegen/promise.h b/sgx-jvm/avian/include/avian/codegen/promise.h new file mode 100644 index 0000000000..8bca0dd528 --- /dev/null +++ b/sgx-jvm/avian/include/avian/codegen/promise.h @@ -0,0 +1,185 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_PROMISE_H +#define AVIAN_CODEGEN_PROMISE_H + +#include +#include +#include + +namespace avian { +namespace codegen { + +class Promise { + public: + class Listener { + public: + virtual bool resolve(int64_t value, void** location) = 0; + + Listener* next; + }; + + virtual int64_t value() = 0; + virtual bool resolved() = 0; + virtual Listener* listen(unsigned) + { + return 0; + } +}; + +class ResolvedPromise : public Promise { + public: + ResolvedPromise(int64_t value) : value_(value) + { + } + + virtual int64_t value() + { + return value_; + } + + virtual bool resolved() + { + return true; + } + + int64_t value_; +}; + +class ShiftMaskPromise : public Promise { + public: + ShiftMaskPromise(Promise* base, unsigned shift, int64_t mask) + : base(base), shift(shift), mask(mask) + { + } + + virtual int64_t value() + { + return (base->value() >> shift) & mask; + } + + virtual bool resolved() + { + return base->resolved(); + } + + Promise* base; + unsigned shift; + int64_t mask; +}; + +class CombinedPromise : public Promise { + public: + CombinedPromise(Promise* low, Promise* high) : low(low), high(high) + { + } + + virtual int64_t value() + { + return low->value() | (high->value() << 32); + } + + virtual bool resolved() + { + return low->resolved() and high->resolved(); + } + + Promise* low; + Promise* high; +}; + +class OffsetPromise : public Promise { + public: + OffsetPromise(Promise* base, int64_t offset) : base(base), offset(offset) + { + } + + virtual int64_t value() + { + return base->value() + offset; + } + + virtual bool resolved() + { + return base->resolved(); + } + + Promise* base; + int64_t offset; +}; + +class ListenPromise : public Promise { + public: + ListenPromise(vm::System* s, util::AllocOnly* allocator) + : s(s), allocator(allocator), listener(0) + { + } + + virtual int64_t value() + { + abort(s); + } + + virtual bool resolved() + { + return false; + } + + virtual Listener* listen(unsigned sizeInBytes) + { + Listener* l = static_cast(allocator->allocate(sizeInBytes)); + l->next = listener; + listener = l; + return l; + } + + vm::System* s; + util::AllocOnly* allocator; + Listener* listener; + Promise* promise; +}; + +class DelayedPromise : public ListenPromise { + public: + DelayedPromise(vm::System* s, + util::AllocOnly* allocator, + Promise* basis, + DelayedPromise* next) + : ListenPromise(s, allocator), basis(basis), next(next) + { + } + + virtual int64_t value() + { + abort(s); + } + + virtual bool resolved() + { + return false; + } + + virtual Listener* listen(unsigned sizeInBytes) + { + Listener* l = static_cast(allocator->allocate(sizeInBytes)); + l->next = listener; + listener = l; + return l; + } + + Promise* basis; + DelayedPromise* next; +}; + +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_PROMISE_H diff --git a/sgx-jvm/avian/include/avian/codegen/registers.h b/sgx-jvm/avian/include/avian/codegen/registers.h new file mode 100644 index 0000000000..25420db668 --- /dev/null +++ b/sgx-jvm/avian/include/avian/codegen/registers.h @@ -0,0 +1,212 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_REGISTERS_H +#define AVIAN_CODEGEN_REGISTERS_H + +#include "avian/common.h" + +namespace avian { +namespace codegen { + +class RegisterMask; + +class Register { +private: + int8_t _index; +public: + explicit constexpr Register(int8_t _index) : _index(_index) {} + constexpr Register() : _index(-1) {} + + constexpr bool operator == (Register o) const { + return _index == o._index; + } + + constexpr bool operator != (Register o) const { + return !(*this == o); + } + + constexpr RegisterMask operator | (Register o) const; + + constexpr bool operator < (Register o) const { + return _index < o._index; + } + + constexpr bool operator > (Register o) const { + return _index > o._index; + } + + constexpr bool operator <= (Register o) const { + return _index <= o._index; + } + + constexpr bool operator >= (Register o) const { + return _index >= o._index; + } + + constexpr int index() const { + return _index; + } +}; + +constexpr Register NoRegister; + +class RegisterMask { +private: + uint64_t mask; + + static constexpr unsigned maskStart(uint64_t mask, unsigned offset = 64) { + return mask == 0 ? (offset & 63) : maskStart(mask << 1, offset - 1); + } + + static constexpr unsigned maskLimit(uint64_t mask, unsigned offset = 0) { + return mask == 0 ? offset : maskLimit(mask >> 1, offset + 1); + } +public: + constexpr RegisterMask(uint64_t mask) : mask(mask) {} + constexpr RegisterMask() : mask(0) {} + constexpr RegisterMask(Register reg) : mask(static_cast(1) << reg.index()) {} + + constexpr unsigned begin() const { + return maskStart(mask); + } + + constexpr unsigned end() const { + return maskLimit(mask); + } + + constexpr RegisterMask operator &(RegisterMask o) const { + return RegisterMask(mask & o.mask); + } + + RegisterMask operator &=(RegisterMask o) { + mask &= o.mask; + return *this; + } + + constexpr RegisterMask operator |(RegisterMask o) const { + return RegisterMask(mask | o.mask); + } + + constexpr bool contains(Register reg) const { + return (mask & (static_cast(1) << reg.index())) != 0; + } + + constexpr bool containsExactly(Register reg) const { + return mask == (mask & (static_cast(1) << reg.index())); + } + + constexpr RegisterMask excluding(Register reg) const { + return RegisterMask(mask & ~(static_cast(1) << reg.index())); + } + + constexpr RegisterMask including(Register reg) const { + return RegisterMask(mask | (static_cast(1) << reg.index())); + } + + constexpr explicit operator uint64_t() const { + return mask; + } + + constexpr explicit operator bool() const { + return mask != 0; + } +}; + +constexpr RegisterMask AnyRegisterMask(~static_cast(0)); +constexpr RegisterMask NoneRegisterMask(0); + +constexpr RegisterMask Register::operator | (Register o) const { + return RegisterMask(*this) | o; +} + +class RegisterIterator; + +class BoundedRegisterMask : public RegisterMask { + public: + uint8_t start; + uint8_t limit; + + BoundedRegisterMask(RegisterMask mask) + : RegisterMask(mask), start(mask.begin()), limit(mask.end()) + { + } + + RegisterIterator begin() const; + + RegisterIterator end() const; +}; + +class RegisterIterator { + public: + int index; + int direction; + int limit; + const RegisterMask mask; + + RegisterIterator(int index, int direction, int limit, RegisterMask mask) + : index(index), direction(direction), limit(limit), mask(mask) + { + } + + bool operator !=(const RegisterIterator& o) const { + return index != o.index; + } + + Register operator *() { + return Register(index); + } + + void operator ++ () { + if(index != limit) { + index += direction; + } + while(index != limit && !mask.contains(Register(index))) { + index += direction; + } + } +}; + +inline RegisterIterator BoundedRegisterMask::begin() const { + // We use reverse iteration... for some reason. + return RegisterIterator(limit - 1, -1, start - 1, *this); +} + +inline RegisterIterator BoundedRegisterMask::end() const { + // We use reverse iteration... for some reason. + return RegisterIterator(start - 1, -1, start - 1, *this); +} + +inline RegisterIterator begin(BoundedRegisterMask mask) { + return mask.begin(); +} + +inline RegisterIterator end(BoundedRegisterMask mask) { + return mask.end(); +} + +class RegisterFile { + public: + BoundedRegisterMask allRegisters; + BoundedRegisterMask generalRegisters; + BoundedRegisterMask floatRegisters; + + RegisterFile(RegisterMask generalRegisterMask, RegisterMask floatRegisterMask) + : allRegisters(generalRegisterMask | floatRegisterMask), + generalRegisters(generalRegisterMask), + floatRegisters(floatRegisterMask) + { + } +}; + +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_REGISTERS_H diff --git a/sgx-jvm/avian/include/avian/codegen/runtime.h b/sgx-jvm/avian/include/avian/codegen/runtime.h new file mode 100644 index 0000000000..527d68a525 --- /dev/null +++ b/sgx-jvm/avian/include/avian/codegen/runtime.h @@ -0,0 +1,54 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_RUNTIME_H +#define AVIAN_CODEGEN_RUNTIME_H + +namespace avian { +namespace codegen { +namespace runtime { + +int64_t compareDoublesG(uint64_t bi, uint64_t ai); +int64_t compareDoublesL(uint64_t bi, uint64_t ai); +int64_t compareFloatsG(uint32_t bi, uint32_t ai); +int64_t compareFloatsL(uint32_t bi, uint32_t ai); +int64_t compareLongs(uint64_t b, uint64_t a); +uint64_t addDouble(uint64_t b, uint64_t a); +uint64_t subtractDouble(uint64_t b, uint64_t a); +uint64_t multiplyDouble(uint64_t b, uint64_t a); +uint64_t divideDouble(uint64_t b, uint64_t a); +uint64_t moduloDouble(uint64_t b, uint64_t a); +uint64_t negateDouble(uint64_t a); +uint64_t squareRootDouble(uint64_t a); +uint64_t doubleToFloat(int64_t a); +int64_t doubleToInt(int64_t a); +int64_t doubleToLong(int64_t a); +uint64_t addFloat(uint32_t b, uint32_t a); +uint64_t subtractFloat(uint32_t b, uint32_t a); +uint64_t multiplyFloat(uint32_t b, uint32_t a); +uint64_t divideFloat(uint32_t b, uint32_t a); +uint64_t moduloFloat(uint32_t b, uint32_t a); +uint64_t negateFloat(uint32_t a); +uint64_t absoluteFloat(uint32_t a); +int64_t absoluteLong(int64_t a); +int64_t absoluteInt(int32_t a); +uint64_t floatToDouble(int32_t a); +int64_t floatToInt(int32_t a); +int64_t floatToLong(int32_t a); +uint64_t intToDouble(int32_t a); +uint64_t intToFloat(int32_t a); +uint64_t longToDouble(int64_t a); +uint64_t longToFloat(int64_t a); + +} // namespace runtime +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_RUNTIME_H diff --git a/sgx-jvm/avian/include/avian/codegen/targets.h b/sgx-jvm/avian/include/avian/codegen/targets.h new file mode 100644 index 0000000000..d297c86bab --- /dev/null +++ b/sgx-jvm/avian/include/avian/codegen/targets.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_TARGETS_H +#define AVIAN_CODEGEN_TARGETS_H + +namespace vm { +class System; +} + +namespace avian { +namespace codegen { + +class Architecture; + +Architecture* makeArchitectureNative(vm::System* system, + bool useNativeFeatures); + +Architecture* makeArchitectureX86(vm::System* system, bool useNativeFeatures); +Architecture* makeArchitectureArm(vm::System* system, bool useNativeFeatures); + +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_TARGETS_H diff --git a/sgx-jvm/avian/include/avian/heap/heap.h b/sgx-jvm/avian/include/avian/heap/heap.h new file mode 100644 index 0000000000..5448fa3222 --- /dev/null +++ b/sgx-jvm/avian/include/avian/heap/heap.h @@ -0,0 +1,88 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef HEAP_H +#define HEAP_H + +#include +#include + +namespace vm { + +// an object must survive TenureThreshold + 2 garbage collections +// before being copied to gen2 (must be at least 1): +const unsigned TenureThreshold = 3; + +const unsigned FixieTenureThreshold = TenureThreshold + 2; + +class Heap : public avian::util::Allocator { + public: + enum CollectionType { MinorCollection, MajorCollection }; + + enum Status { Null, Reachable, Unreachable, Tenured }; + + class Visitor { + public: + virtual void visit(void*) = 0; + }; + + class Walker { + public: + virtual bool visit(unsigned) = 0; + }; + + class Client { + public: + virtual void collect(void* context, CollectionType type) = 0; + virtual void visitRoots(Visitor*) = 0; + virtual bool isFixed(void*) = 0; + virtual unsigned sizeInWords(void*) = 0; + virtual unsigned copiedSizeInWords(void*) = 0; + virtual void copy(void*, void*) = 0; + virtual void walk(void*, Walker*) = 0; + }; + + virtual void setClient(Client* client) = 0; + virtual void setImmortalHeap(uintptr_t* start, unsigned sizeInWords) = 0; + virtual unsigned remaining() = 0; + virtual unsigned limit() = 0; + virtual bool limitExceeded(int pendingAllocation = 0) = 0; + virtual void collect(CollectionType type, + unsigned footprint, + int pendingAllocation) = 0; + virtual unsigned fixedFootprint(unsigned sizeInWords, bool objectMask) = 0; + virtual void* allocateFixed(avian::util::Alloc* allocator, + unsigned sizeInWords, + bool objectMask) = 0; + virtual void* allocateImmortalFixed(avian::util::Alloc* allocator, + unsigned sizeInWords, + bool objectMask) = 0; + virtual void mark(void* p, unsigned offset, unsigned count) = 0; + virtual void pad(void* p) = 0; + virtual void* follow(void* p) = 0; + + template + T* follow(T* p) + { + return static_cast(follow(static_cast(p))); + } + + virtual void postVisit() = 0; + virtual Status status(void* p) = 0; + virtual CollectionType collectionType() = 0; + virtual void disposeFixies() = 0; + virtual void dispose() = 0; +}; + +Heap* makeHeap(System* system, unsigned limit); + +} // namespace vm + +#endif // HEAP_H diff --git a/sgx-jvm/avian/include/avian/system/memory.h b/sgx-jvm/avian/include/avian/system/memory.h new file mode 100644 index 0000000000..4b4de4dd7b --- /dev/null +++ b/sgx-jvm/avian/include/avian/system/memory.h @@ -0,0 +1,49 @@ + /* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_SYSTEM_MEMORY_H +#define AVIAN_SYSTEM_MEMORY_H + +#include + +#include + +namespace avian { +namespace system { + +class Memory { + public: + enum Permissions { + Read = 1 << 0, + Write = 1 << 1, + Execute = 1 << 2, + + // Utility munged constants + ReadWrite = Read | Write, + ReadExecute = Read | Execute, + ReadWriteExecute = Read | Write | Execute + }; + + static const size_t PageSize; + + // Allocate a contiguous range of pages. + static util::Slice allocate(size_t sizeInBytes, Permissions perms = ReadWrite); + + // Free a contiguous range of pages. + static void free(util::Slice pages); + + // TODO: In the future: + // static void setPermissions(util::Slice pages, Permissions perms); +}; + +} // namespace system +} // namespace avian + +#endif diff --git a/sgx-jvm/avian/include/avian/system/signal.h b/sgx-jvm/avian/include/avian/system/signal.h new file mode 100644 index 0000000000..a5711e7da7 --- /dev/null +++ b/sgx-jvm/avian/include/avian/system/signal.h @@ -0,0 +1,81 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_SYSTEM_SIGNAL_H +#define AVIAN_SYSTEM_SIGNAL_H + +#include + +namespace avian { +namespace system { + +// Crash the process. +// On posix, the just calls abort. On windows, we dereference a null pointer in +// order to trigger the crash dump logic. +NO_RETURN void crash(); + +// Registrar for unix-like "signals" (implemented with structured exceptions on +// windows). +// TODO: remove dependence on generated code having a well-known "thread" +// register. Use a thread-local variable instead. +class SignalRegistrar { + public: + class Handler { + public: + // This function receives state information about the paused thread. + // Returns whether to resume execution after the failure point. + virtual bool handleSignal(void** ip, + void** frame, + void** stack, + void** thread) = 0; + }; + + enum Signal { + // "Segmentation fault" exceptions (mostly null pointer dereference, but + // generally access to any non-mapped memory) + SegFault, + DivideByZero, + }; + + SignalRegistrar(); + ~SignalRegistrar(); + + // Register a handler for the given signal. + // After this method call, anytime the given signal is raised, it will be + // handled by the given handler. + // Returns true upon success, false upon failure + bool registerHandler(Signal signal, Handler* handler); + + // Unregister a handler for the given signal. + // After this method call, the given signal will no longer be handled (or, + // rather, it go back to being handled by whatever was registered to handle it + // before us). + // Returns true upon success, false upon failure + bool unregisterHandler(Signal signal); + + // Set the directory that a crash dump will be written to should an unhandled + // exception be thrown. + // Note: this only currently does anything on windows. + // TODO: move this out of this class, into a separate "CrashDumper" class or + // somesuch. + void setCrashDumpDirectory(const char* crashDumpDirectory); + + // This is internal, implementation-specific data. It's declared in the + // specific implementation. + struct Data; + + private: + Data* data; +}; + +} // namespace system +} // namespace avian + +#endif diff --git a/sgx-jvm/avian/include/avian/system/system.h b/sgx-jvm/avian/include/avian/system/system.h new file mode 100644 index 0000000000..e2c4ff5022 --- /dev/null +++ b/sgx-jvm/avian/include/avian/system/system.h @@ -0,0 +1,183 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef SYSTEM_H +#define SYSTEM_H + +#include "avian/common.h" +#include +#include + +namespace vm { + +class System : public avian::util::Aborter { + public: + typedef intptr_t Status; + + enum FileType { TypeUnknown, TypeDoesNotExist, TypeFile, TypeDirectory }; + + class Thread { + public: + virtual void interrupt() = 0; + virtual bool getAndClearInterrupted() = 0; + virtual void join() = 0; + virtual void dispose() = 0; + }; + + class ThreadVisitor { + public: + virtual void visit(void* ip, void* stack, void* link) = 0; + }; + + class Runnable { + public: + virtual void attach(Thread*) = 0; + virtual void run() = 0; + virtual bool interrupted() = 0; + virtual void setInterrupted(bool v) = 0; + }; + + class Mutex { + public: + virtual void acquire() = 0; + virtual void release() = 0; + virtual void dispose() = 0; + }; + + class Monitor { + public: + virtual bool tryAcquire(Thread* context) = 0; + virtual void acquire(Thread* context) = 0; + virtual void release(Thread* context) = 0; + virtual void wait(Thread* context, int64_t time) = 0; + virtual bool waitAndClearInterrupted(Thread* context, int64_t time) = 0; + virtual void notify(Thread* context) = 0; + virtual void notifyAll(Thread* context) = 0; + virtual Thread* owner() = 0; + virtual void dispose() = 0; + }; + + class Local { + public: + virtual void* get() = 0; + virtual void set(void* p) = 0; + virtual void dispose() = 0; + }; + + class Region { + public: + virtual const uint8_t* start() = 0; + virtual size_t length() = 0; + virtual void dispose() = 0; + }; + + class Directory { + public: + virtual const char* next() = 0; + virtual void dispose() = 0; + }; + + class Library { + public: + virtual void* resolve(const char* symbol) = 0; + virtual const char* name() = 0; + virtual Library* next() = 0; + virtual void setNext(Library* lib) = 0; + virtual void disposeAll() = 0; + }; + + class MonitorResource { + public: + MonitorResource(System::Thread* t, System::Monitor* m) : t(t), m(m) + { + m->acquire(t); + } + + ~MonitorResource() + { + m->release(t); + } + + private: + System::Thread* t; + System::Monitor* m; + }; + + virtual bool success(Status) = 0; + virtual void* tryAllocate(size_t sizeInBytes) = 0; + virtual void free(const void* p) = 0; + virtual Status attach(Runnable*) = 0; + virtual Status start(Runnable*) = 0; + virtual Status make(Mutex**) = 0; + virtual Status make(Monitor**) = 0; + virtual Status make(Local**) = 0; + + virtual Status visit(Thread* thread, Thread* target, ThreadVisitor* visitor) + = 0; + + virtual Status map(Region**, const char* name) = 0; + virtual FileType stat(const char* name, size_t* length) = 0; + virtual Status open(Directory**, const char* name) = 0; + virtual const char* libraryPrefix() = 0; + virtual const char* librarySuffix() = 0; + virtual Status load(Library**, const char* name) = 0; + virtual char pathSeparator() = 0; + virtual char fileSeparator() = 0; + virtual const char* toAbsolutePath(avian::util::AllocOnly* allocator, + const char* name) = 0; + virtual int64_t now() = 0; + virtual void yield() = 0; + virtual void exit(int code) = 0; + virtual void dispose() = 0; +}; + +inline void* allocate(System* s, size_t size) +{ + void* p = s->tryAllocate(size); + if (p == 0) + s->abort(); + return p; +} + +#define ACQUIRE_MONITOR(t, m) \ + System::MonitorResource MAKE_NAME(monitorResource_)(t, m) + +inline avian::util::Aborter* getAborter(System* s) +{ + return s; +} + +inline void NO_RETURN sysAbort(System* s) +{ + abort(s); +} + +// #ifdef NDEBUG + +// # define assertT(a, b) +// # define vm_assert(a, b) + +// #else // not NDEBUG + +// inline void +// assertT(System* s, bool v) +// { +// expect(s, v); +// } + +// # define vm_assert(a, b) vm::assertT(a, b) + +// #endif // not NDEBUG + +AVIAN_EXPORT System* makeSystem(bool reentrant = false); + +} // namespace vm + +#endif // SYSTEM_H diff --git a/sgx-jvm/avian/include/avian/tools/object-writer/tools.h b/sgx-jvm/avian/include/avian/tools/object-writer/tools.h new file mode 100644 index 0000000000..b73f27ffdc --- /dev/null +++ b/sgx-jvm/avian/include/avian/tools/object-writer/tools.h @@ -0,0 +1,170 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_TOOLS_H_ +#define AVIAN_TOOLS_H_ + +#include + +#include +#include + +#include "avian/environment.h" + +namespace avian { + +namespace tools { + +class OutputStream { + public: + virtual void writeChunk(const void* data, size_t size) = 0; + virtual void write(uint8_t byte); + virtual void writeRepeat(uint8_t byte, size_t size); +}; + +class FileOutputStream : public OutputStream { + private: + FILE* file; + + public: + FileOutputStream(const char* name); + ~FileOutputStream(); + + bool isValid(); + + virtual void writeChunk(const void* data, size_t size); + virtual void write(uint8_t byte); +}; + +class SymbolInfo { + public: + unsigned addr; + util::String name; + + inline SymbolInfo(uint64_t addr, const util::String& name) + : addr(addr), name(name) + { + } + + inline SymbolInfo() : name("") + { + } +}; + +class Buffer { + public: + size_t capacity; + size_t length; + uint8_t* data; + + Buffer(); + ~Buffer(); + + void ensure(size_t more); + void write(const void* d, size_t size); +}; + +class StringTable : public Buffer { + public: + unsigned add(util::String str); +}; + +template +class DynamicArray : public util::Slice { + public: + size_t capacity; + + DynamicArray() : util::Slice((T*)malloc(10 * sizeof(T)), 0), capacity(10) + { + } + ~DynamicArray() + { + free(util::Slice::items); + } + + void ensure(size_t more) + { + if (util::Slice::count + more > capacity) { + capacity = capacity * 2 + more; + util::Slice::items + = (T*)realloc(util::Slice::items, capacity * sizeof(T)); + } + } + + void add(const T& item) + { + ensure(1); + util::Slice::items[util::Slice::count++] = item; + } +}; + +class PlatformInfo { + public: + enum Format { + Elf = AVIAN_FORMAT_ELF, + Pe = AVIAN_FORMAT_PE, + MachO = AVIAN_FORMAT_MACHO, + UnknownFormat = AVIAN_FORMAT_UNKNOWN + }; + + enum Architecture { + x86 = AVIAN_ARCH_X86, + x86_64 = AVIAN_ARCH_X86_64, + Arm = AVIAN_ARCH_ARM, + Arm64 = AVIAN_ARCH_ARM64, + UnknownArch = AVIAN_ARCH_UNKNOWN + }; + + const Format format; + const Architecture arch; + + static Format formatFromString(const char* format); + static Architecture archFromString(const char* arch); + + inline PlatformInfo(Format format, Architecture arch) + : format(format), arch(arch) + { + } + + inline bool operator==(const PlatformInfo& other) + { + return format == other.format && arch == other.arch; + } +}; + +class Platform { + private: + Platform* next; + static Platform* first; + + public: + PlatformInfo info; + + inline Platform(PlatformInfo info) : next(first), info(info) + { + first = this; + } + + enum AccessFlags { Writable = 1 << 0, Executable = 1 << 1 }; + + virtual bool writeObject(OutputStream* out, + util::Slice symbols, + util::Slice data, + unsigned accessFlags, + unsigned alignment) = 0; + + static Platform* getPlatform(PlatformInfo info); +}; + +} // namespace tools + +} // namespace avian + +#endif diff --git a/sgx-jvm/avian/include/avian/util/abort.h b/sgx-jvm/avian/include/avian/util/abort.h new file mode 100644 index 0000000000..c1c1e98010 --- /dev/null +++ b/sgx-jvm/avian/include/avian/util/abort.h @@ -0,0 +1,59 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_UTIL_ABORT_H +#define AVIAN_UTIL_ABORT_H + +// TODO: remove reference back into the source directory! +// Note: this is needed for UNLIKELY +#include + +namespace avian { +namespace util { + +class Aborter { + public: + virtual void NO_RETURN abort() = 0; +}; + +inline Aborter* getAborter(Aborter* a) +{ + return a; +} + +template +inline void NO_RETURN abort(T t) +{ + getAborter(t)->abort(); + ::abort(); +} + +template +inline void expect(T t, bool v) +{ + if (UNLIKELY(!v)) { + abort(t); + } +} + +#ifdef NDEBUG +#define assertT(t, v) +#else +template +inline void assertT(T t, bool v) +{ + expect(t, v); +} +#endif + +} // namespace util +} // namespace avian + +#endif // AVIAN_UTIL_ABORT_H diff --git a/sgx-jvm/avian/include/avian/util/allocator.h b/sgx-jvm/avian/include/avian/util/allocator.h new file mode 100644 index 0000000000..7060a759ef --- /dev/null +++ b/sgx-jvm/avian/include/avian/util/allocator.h @@ -0,0 +1,42 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_UTIL_ALLOCATOR_H +#define AVIAN_UTIL_ALLOCATOR_H + +#include + +namespace avian { +namespace util { + +class AllocOnly { + public: + virtual void* allocate(size_t size) = 0; +}; + +class Alloc : public AllocOnly { + public: + virtual void free(const void* p, size_t size) = 0; +}; + +class Allocator : public Alloc { + public: + virtual void* tryAllocate(size_t size) = 0; +}; + +} // namespace util +} // namespace avian + +inline void* operator new(size_t size, avian::util::AllocOnly* allocator) +{ + return allocator->allocate(size); +} + +#endif // AVIAN_UTIL_ALLOCATOR_H diff --git a/sgx-jvm/avian/include/avian/util/arg-parser.h b/sgx-jvm/avian/include/avian/util/arg-parser.h new file mode 100644 index 0000000000..83f089f983 --- /dev/null +++ b/sgx-jvm/avian/include/avian/util/arg-parser.h @@ -0,0 +1,48 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_UTIL_ARG_PARSER_H +#define AVIAN_UTIL_ARG_PARSER_H + +namespace avian { +namespace util { + +class Arg; + +class ArgParser { + public: + ArgParser(); + + bool parse(int ac, const char* const* av); + void printUsage(const char* exe); + + private: + friend class Arg; + + Arg* first; + Arg** last; +}; + +class Arg { + public: + Arg* next; + bool required; + const char* name; + const char* desc; + + const char* value; + + Arg(ArgParser& parser, bool required, const char* name, const char* desc); +}; + +} // namespace avian +} // namespace util + +#endif // AVIAN_UTIL_ARG_PARSER_H diff --git a/sgx-jvm/avian/include/avian/util/assert.h b/sgx-jvm/avian/include/avian/util/assert.h new file mode 100644 index 0000000000..ab6cee63ed --- /dev/null +++ b/sgx-jvm/avian/include/avian/util/assert.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2008-2013, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_UTIL_ASSERT_H +#define AVIAN_UTIL_ASSERT_H + +#include + +namespace avian { +namespace util { + +#define UNREACHABLE_ ::abort() + +// TODO: print msg in debug mode +#define UNREACHABLE(msg) ::abort() + +#define ASSERT(that) \ + if (!(that)) { \ + UNREACHABLE(#that); \ + } + +} // namespace util +} // namespace avian + +#endif // AVIAN_UTIL_ASSERT_H diff --git a/sgx-jvm/avian/include/avian/util/cpp.h b/sgx-jvm/avian/include/avian/util/cpp.h new file mode 100644 index 0000000000..bbe1ff56f7 --- /dev/null +++ b/sgx-jvm/avian/include/avian/util/cpp.h @@ -0,0 +1,60 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_UTIL_CPP_H +#define AVIAN_UTIL_CPP_H + +#include "allocator.h" +#include "math.h" +#include "assert.h" + +namespace avian { +namespace util { + +template +struct NonConst; + +template +struct NonConst { + typedef T Type; +}; + +template +struct NonConst { + typedef T Type; +}; + +template +struct ArgumentCount; + +template +struct ArgumentCount { + enum { Result = 1 + ArgumentCount::Result }; +}; + +template <> +struct ArgumentCount<> { + enum { Result = 0 }; +}; + +template +void setArrayElements(T*) { +} + +template +void setArrayElements(T* arr, T elem, Ts... ts) { + *arr = elem; + setArrayElements(arr, ts...); +} + +} // namespace util +} // namespace avian + +#endif // AVIAN_UTIL_CPP_H diff --git a/sgx-jvm/avian/include/avian/util/fixed-allocator.h b/sgx-jvm/avian/include/avian/util/fixed-allocator.h new file mode 100644 index 0000000000..fa5a2c66b2 --- /dev/null +++ b/sgx-jvm/avian/include/avian/util/fixed-allocator.h @@ -0,0 +1,43 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_UTIL_FIXED_ALLOCATOR_H +#define AVIAN_UTIL_FIXED_ALLOCATOR_H + +#include "allocator.h" +#include "abort.h" +#include "slice.h" + +namespace avian { +namespace util { + +// An Allocator that allocates, bump-pointer style, out of a pre-defined chunk +// of memory. +class FixedAllocator : public Alloc { + public: + FixedAllocator(Aborter* a, Slice memory); + + virtual void* tryAllocate(size_t size); + + void* allocate(size_t size, unsigned padAlignment); + + virtual void* allocate(size_t size); + + virtual void free(const void* p, size_t size); + + Aborter* a; + Slice memory; + size_t offset; +}; + +} // namespace util +} // namespace avian + +#endif // AVIAN_UTIL_FIXED_ALLOCATOR_H diff --git a/sgx-jvm/avian/include/avian/util/hash.h b/sgx-jvm/avian/include/avian/util/hash.h new file mode 100644 index 0000000000..c9342b5a5f --- /dev/null +++ b/sgx-jvm/avian/include/avian/util/hash.h @@ -0,0 +1,57 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_UTIL_HASH_H +#define AVIAN_UTIL_HASH_H + +#include "slice.h" + +namespace avian { +namespace util { + +inline uint32_t hash(const char* s) +{ + uint32_t h = 0; + for (unsigned i = 0; s[i]; ++i) { + h = (h * 31) + s[i]; + } + return h; +} + +inline uint32_t hash(Slice data) +{ + const uint8_t* s = data.begin(); + uint32_t h = 0; + for (size_t i = 0; i < data.count; ++i) { + h = (h * 31) + s[i]; + } + return h; +} + +inline uint32_t hash(Slice data) +{ + return hash(Slice( + reinterpret_cast(data.begin()), data.count)); +} + +inline uint32_t hash(Slice data) +{ + const uint16_t* s = data.begin(); + uint32_t h = 0; + for (size_t i = 0; i < data.count; ++i) { + h = (h * 31) + s[i]; + } + return h; +} + +} // namespace util +} // namespace avian + +#endif // AVIAN_UTIL_HASH_H diff --git a/sgx-jvm/avian/include/avian/util/list.h b/sgx-jvm/avian/include/avian/util/list.h new file mode 100644 index 0000000000..b268176044 --- /dev/null +++ b/sgx-jvm/avian/include/avian/util/list.h @@ -0,0 +1,44 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_UTIL_LIST_H +#define AVIAN_UTIL_LIST_H + +#include "allocator.h" + +namespace avian { +namespace util { + +template +class List { + public: + List(const T& item, List* next) : item(item), next(next) + { + } + + unsigned count() + { + unsigned count = 0; + List* c = this; + while (c) { + ++count; + c = c->next; + } + return count; + } + + T item; + List* next; +}; + +} // namespace util +} // namespace avian + +#endif // AVIAN_UTIL_LIST_H diff --git a/sgx-jvm/avian/include/avian/util/math.h b/sgx-jvm/avian/include/avian/util/math.h new file mode 100644 index 0000000000..36a4ca0513 --- /dev/null +++ b/sgx-jvm/avian/include/avian/util/math.h @@ -0,0 +1,67 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_UTIL_MATH_H +#define AVIAN_UTIL_MATH_H + +#undef max +#undef min + +namespace avian { +namespace util { + +inline unsigned max(unsigned a, unsigned b) +{ + return (a > b ? a : b); +} + +inline unsigned min(unsigned a, unsigned b) +{ + return (a < b ? a : b); +} + +inline unsigned avg(unsigned a, unsigned b) +{ + return (a + b) / 2; +} + +inline unsigned ceilingDivide(unsigned n, unsigned d) +{ + return (n + d - 1) / d; +} + +inline bool powerOfTwo(unsigned n) +{ + for (; n > 2; n >>= 1) + if (n & 1) + return false; + return true; +} + +inline unsigned nextPowerOfTwo(unsigned n) +{ + unsigned r = 1; + while (r < n) + r <<= 1; + return r; +} + +inline unsigned log(unsigned n) +{ + unsigned r = 0; + for (unsigned i = 1; i < n; ++r) + i <<= 1; + return r; +} + +} // namespace util +} // namespace avian + +#endif // AVIAN_UTIL_MATH_H diff --git a/sgx-jvm/avian/include/avian/util/runtime-array.h b/sgx-jvm/avian/include/avian/util/runtime-array.h new file mode 100644 index 0000000000..d89ad1884d --- /dev/null +++ b/sgx-jvm/avian/include/avian/util/runtime-array.h @@ -0,0 +1,41 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_UTIL_RUNTIME_ARRAY_H +#define AVIAN_UTIL_RUNTIME_ARRAY_H + +#ifdef _MSC_VER + +template +class RuntimeArray { + public: + RuntimeArray(unsigned size) : body(static_cast(malloc(size * sizeof(T)))) + { + } + + ~RuntimeArray() + { + free(body); + } + + T* body; +}; + +#define RUNTIME_ARRAY(type, name, size) RuntimeArray name(size); +#define RUNTIME_ARRAY_BODY(name) name.body + +#else // not _MSC_VER + +#define RUNTIME_ARRAY(type, name, size) type name##_body[size]; +#define RUNTIME_ARRAY_BODY(name) name##_body + +#endif + +#endif // AVIAN_UTIL_RUNTIME_ARRAY_H diff --git a/sgx-jvm/avian/include/avian/util/slice.h b/sgx-jvm/avian/include/avian/util/slice.h new file mode 100644 index 0000000000..b8877819c0 --- /dev/null +++ b/sgx-jvm/avian/include/avian/util/slice.h @@ -0,0 +1,101 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_UTIL_SLICE_H +#define AVIAN_UTIL_SLICE_H + +#include "allocator.h" +#include "math.h" +#include "assert.h" +#include "cpp.h" + +namespace avian { +namespace util { + +template +class Slice { + public: + T* items; + size_t count; + + inline Slice(T* items, size_t count) : items(items), count(count) + { + } + + inline Slice(const Slice::Type>& copy) + : items(copy.items), count(copy.count) + { + } + + inline T& operator[](size_t index) + { + ASSERT(index < count); + return items[index]; + } + + inline T* begin() + { + return items; + } + + inline T* end() + { + return items + count; + } + + inline Slice subslice(size_t begin, size_t count) + { + ASSERT(begin <= this->count); + ASSERT(begin + count <= this->count); + return Slice(this->begin() + begin, count); + } + + static Slice alloc(AllocOnly* a, size_t count) + { + return Slice((T*)a->allocate(sizeof(T) * count), count); + } + + static Slice allocAndSet(AllocOnly* a, size_t count, const T& item) + { + Slice slice(alloc(a, count)); + for (size_t i = 0; i < count; i++) { + slice[i] = item; + } + return slice; + } + + Slice clone(AllocOnly* a, size_t newCount) + { + T* newItems = (T*)a->allocate(newCount * sizeof(T)); + memcpy(newItems, items, min(count, newCount) * sizeof(T)); + return Slice(newItems, newCount); + } + + Slice cloneAndSet(AllocOnly* a, size_t newCount, const T& item) + { + Slice slice(clone(a, newCount)); + for (size_t i = count; i < newCount; i++) { + slice[i] = item; + } + return slice; + } + + void resize(Alloc* a, size_t newCount) + { + Slice slice(clone(a, newCount)); + a->free(items, count); + *this = slice; + } +}; + +} // namespace util +} // namespace avian + +#endif // AVIAN_UTIL_SLICE_H diff --git a/sgx-jvm/avian/include/avian/util/stream.h b/sgx-jvm/avian/include/avian/util/stream.h new file mode 100644 index 0000000000..f957dc90de --- /dev/null +++ b/sgx-jvm/avian/include/avian/util/stream.h @@ -0,0 +1,126 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef STREAM_H +#define STREAM_H + +#include "avian/common.h" + +namespace vm { + +class AbstractStream { + public: + class Client { + public: + virtual void handleError() = 0; + }; + + AbstractStream(Client* client, unsigned size) + : client(client), size(size), position_(0) + { + } + + unsigned position() + { + return position_; + } + + void setPosition(unsigned p) + { + position_ = p; + } + + void skip(unsigned size) + { + if (size > this->size - position_) { + client->handleError(); + } else { + position_ += size; + } + } + + void read(uint8_t* dst, unsigned size) + { + if (size > this->size - position_) { + memset(dst, 0, size); + + client->handleError(); + } else { + copy(dst, position_, size); + position_ += size; + } + } + + uint8_t read1() + { + uint8_t v; + read(&v, 1); + return v; + } + + uint16_t read2() + { + uint16_t a = read1(); + uint16_t b = read1(); + return (a << 8) | b; + } + + uint32_t read4() + { + uint32_t a = read2(); + uint32_t b = read2(); + return (a << 16) | b; + } + + uint64_t read8() + { + uint64_t a = read4(); + uint64_t b = read4(); + return (a << 32) | b; + } + + uint32_t readFloat() + { + return read4(); + } + + uint64_t readDouble() + { + return read8(); + } + + protected: + virtual void copy(uint8_t* dst, unsigned offset, unsigned size) = 0; + + private: + Client* client; + unsigned size; + unsigned position_; +}; + +class Stream : public AbstractStream { + public: + Stream(Client* client, const uint8_t* data, unsigned size) + : AbstractStream(client, size), data(data) + { + } + + private: + virtual void copy(uint8_t* dst, unsigned offset, unsigned size) + { + memcpy(dst, data + offset, size); + } + + const uint8_t* data; +}; + +} // namespace vm + +#endif // STREAM_H diff --git a/sgx-jvm/avian/include/avian/util/string.h b/sgx-jvm/avian/include/avian/util/string.h new file mode 100644 index 0000000000..436bdeacd9 --- /dev/null +++ b/sgx-jvm/avian/include/avian/util/string.h @@ -0,0 +1,36 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_UTIL_STRING_H +#define AVIAN_UTIL_STRING_H + +#include + +namespace avian { +namespace util { + +class String { + public: + const char* text; + size_t length; + + String(const char* text) : text(text), length(strlen(text)) + { + } + + inline String(const char* text, size_t length) : text(text), length(length) + { + } +}; + +} // namespace util +} // namespace avain + +#endif // AVIAN_UTIL_STRING_H diff --git a/sgx-jvm/avian/include/avian/util/tokenizer.h b/sgx-jvm/avian/include/avian/util/tokenizer.h new file mode 100644 index 0000000000..0b790314b5 --- /dev/null +++ b/sgx-jvm/avian/include/avian/util/tokenizer.h @@ -0,0 +1,54 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_UTIL_TOKENIZER_H +#define AVIAN_UTIL_TOKENIZER_H + +#include "string.h" + +namespace avian { +namespace util { + +class Tokenizer { + public: + Tokenizer(const char* s, char delimiter) + : s(s), limit(0), delimiter(delimiter) + { + } + + Tokenizer(String str, char delimiter) + : s(str.text), limit(str.text + str.length), delimiter(delimiter) + { + } + + bool hasMore() + { + while (s != limit and *s == delimiter) + ++s; + return s != limit and *s != 0; + } + + String next() + { + const char* p = s; + while (s != limit and *s and *s != delimiter) + ++s; + return String(p, s - p); + } + + const char* s; + const char* limit; + char delimiter; +}; + +} // namespace util +} // namespace avain + +#endif // AVIAN_UTIL_TOKENIZER_H diff --git a/sgx-jvm/avian/makefile b/sgx-jvm/avian/makefile new file mode 100755 index 0000000000..d7db2484bb --- /dev/null +++ b/sgx-jvm/avian/makefile @@ -0,0 +1,2387 @@ +MAKEFLAGS = -s + +name = avian +version := $(shell grep version gradle.properties | cut -d'=' -f2) + +java-version := $(shell "$(JAVA_HOME)/bin/java" -version 2>&1 \ + | head -n 1 \ + | sed 's/.*version "1.\([^.]*\).*/\1/') + +build-arch := $(shell uname -m \ + | sed 's/^i.86$$/i386/' \ + | sed 's/^x86pc$$/i386/' \ + | sed 's/amd64/x86_64/' \ + | sed 's/^arm.*$$/arm/' \ + | sed 's/aarch64/arm64/') + +build-platform := \ + $(shell uname -s | tr [:upper:] [:lower:] \ + | sed \ + -e 's/^mingw64.*$$/mingw32/' \ + -e 's/^mingw32.*$$/mingw32/' \ + -e 's/^cygwin.*$$/cygwin/' \ + -e 's/^darwin.*$$/macosx/') + +arch = $(build-arch) +target-arch = $(arch) + +bootimage-platform = \ + $(subst cygwin,windows,$(subst mingw32,windows,$(build-platform))) + +platform = $(bootimage-platform) + +codegen-targets = native + +mode = fast +process = compile + +ifneq ($(process),compile) + options := -$(process) +endif +ifneq ($(mode),fast) + options := $(options)-$(mode) +endif +ifneq ($(lzma),) + options := $(options)-lzma +endif +ifeq ($(bootimage),true) + options := $(options)-bootimage + ifeq ($(bootimage-test),true) + # this option indicates that we should AOT-compile the test + # classes as well as the class library + options := $(options)-test + endif +endif +ifeq ($(tails),true) + options := $(options)-tails +endif +ifeq ($(continuations),true) + options := $(options)-continuations +endif +ifeq ($(codegen-targets),all) + options := $(options)-all +endif + +ifeq ($(filter debug debug-fast fast stress stress-major small,$(mode)),) + x := $(error "'$(mode)' is not a valid mode (choose one of: debug debug-fast fast stress stress-major small)") +endif + +ifeq ($(filter compile interpret,$(process)),) + x := $(error "'$(process)' is not a valid process (choose one of: compile interpret)") +endif + +ifeq ($(filter x86_64 i386 arm arm64,$(arch)),) + x := $(error "'$(arch)' is not a supported architecture (choose one of: x86_64 i386 arm arm64)") +endif + +ifeq ($(platform),darwin) + x := $(error "please use 'platform=macosx' or 'platform=ios' instead of 'platform=$(platform)'") +endif + +ifneq ($(ios),) + x := $(error "please use 'platform=ios' instead of 'ios=true'") +endif + +ifeq ($(filter linux windows macosx ios freebsd,$(platform)),) + x := $(error "'$(platform)' is not a supported platform (choose one of: linux windows macosx ios freebsd)") +endif + +ifeq ($(platform),macosx) + ifneq ($(filter arm arm64,$(arch)),) + x := $(error "please use ('arch=arm' or 'arch=arm64') 'platform=ios' to build for ios-arm") + endif +endif + +ifeq ($(platform),ios) + ifeq ($(filter i386 x86_64 arm arm64,$(arch)),) + x := $(error "please specify 'arch=i386', 'arch=x86_64', 'arch=arm', or 'arch=arm64' with 'platform=ios'") + endif +endif + +ifeq ($(bootimage-test),true) + ifneq ($(bootimage),true) + x := $(error "bootimage-test=true only works when bootimage=true") + endif +endif + +aot-only = false +root := $(shell (cd .. && pwd)) +build = build/$(platform)-$(arch)$(options) +host-build-root = $(build)/host +classpath-build = $(build)/classpath +test-build = $(build)/test +src = src +classpath-src = classpath +test = test +unittest = unittest +win32 ?= $(root)/win32 +win64 ?= $(root)/win64 +winrt ?= $(root)/winrt +wp8 ?= $(root)/wp8 + +classpath = avian + +bootimage-classpath = $(classpath-build) + +ifeq ($(bootimage-test),true) + bootimage-classpath = $(classpath-build):$(test-build) +endif + +ifeq ($(use-werror),true) + werror = -Werror +endif + +test-executable = $(shell pwd)/$(executable) +boot-classpath = $(classpath-build) +embed-prefix = /avian-embedded + +native-path = echo + +platform-kernel = $(subst macosx,darwin,$(subst ios,darwin,$1)) + +build-kernel = $(call platform-kernel,$(build-platform)) +kernel = $(call platform-kernel,$(platform)) + +ifeq ($(build-platform),cygwin) + native-path = cygpath -m +endif + +windows-path = echo + +path-separator = : + +ifneq (,$(filter mingw32 cygwin,$(build-platform))) + path-separator = ; +endif + +target-path-separator = : + +ifeq ($(platform),windows) + target-path-separator = ; +endif + +library-path-variable = LD_LIBRARY_PATH + +ifeq ($(build-kernel),darwin) + library-path-variable = DYLD_LIBRARY_PATH +endif + +library-path = $(library-path-variable)=$(build) + + + +ifneq ($(openjdk),) + openjdk-version := $(shell $(openjdk)/bin/java -version 2>&1 \ + | head -n 1 \ + | sed 's/.*version "1.\([^.]*\).*/\1/') + + openjdk-arch = $(arch) + ifeq ($(arch),x86_64) + openjdk-arch = amd64 + endif + + ifneq ($(android),) + x := $(error "android and openjdk are incompatible") + endif + + ifneq ($(openjdk-src),) + include openjdk-src.mk + options := $(options)-openjdk-src + classpath-objects = $(openjdk-objects) $(openjdk-local-objects) + classpath-cflags = -DAVIAN_OPENJDK_SRC -DBOOT_JAVAHOME + openjdk-jar-dep = $(build)/openjdk-jar.dep + classpath-jar-dep = $(openjdk-jar-dep) + javahome = $(embed-prefix)/javahomeJar + javahome-files = lib/currency.data lib/security/java.security \ + lib/security/java.policy lib/security/cacerts + + ifneq (,$(wildcard $(openjdk)/jre/lib/zi)) + javahome-files += lib/zi + endif + + ifneq (,$(wildcard $(openjdk)/jre/lib/tzdb.dat)) + javahome-files += lib/tzdb.dat + endif + + local-policy = lib/security/local_policy.jar + ifneq (,$(wildcard $(openjdk)/jre/$(local-policy))) + javahome-files += $(local-policy) + endif + + export-policy = lib/security/US_export_policy.jar + ifneq (,$(wildcard $(openjdk)/jre/$(export-policy))) + javahome-files += $(export-policy) + endif + + ifeq ($(platform),windows) + javahome-files += lib/tzmappings + endif + javahome-object = $(build)/javahome-jar.o + boot-javahome-object = $(build)/boot-javahome.o + stub-sources = $(src)/openjdk/stubs.cpp + stub-objects = $(call cpp-objects,$(stub-sources),$(src),$(build)) + else + soname-flag = -Wl,-soname -Wl,$(so-prefix)jvm$(so-suffix) + version-script-flag = -Wl,--version-script=openjdk.ld + options := $(options)-openjdk + test-executable = $(shell pwd)/$(executable-dynamic) + ifeq ($(build-kernel),darwin) + library-path = \ + $(library-path-variable)=$(build):$(openjdk)/jre/lib + else + library-path = \ + $(library-path-variable)=$(build):$(openjdk)/jre/lib/$(openjdk-arch) + endif + javahome = "$$($(native-path) "$(openjdk)/jre")" + endif + + classpath = openjdk + boot-classpath := "$(boot-classpath)$(path-separator)$$($(native-path) "$(openjdk)/jre/lib/rt.jar")" + build-javahome = $(openjdk)/jre +endif + +ifneq ($(android),) + options := $(options)-android + classpath-jar-dep = $(build)/android.dep + luni-native = $(android)/libcore/luni/src/main/native + classpath-cflags = -DBOOT_JAVAHOME + android-cflags = -I$(luni-native) \ + -I$(android)/libnativehelper/include/nativehelper \ + -I$(android)/libnativehelper \ + -I$(android)/system/core/include \ + -I$(android)/external/zlib \ + -I$(android)/external/icu4c/i18n \ + -I$(android)/external/icu4c/common \ + -I$(android)/external/expat \ + -I$(android)/external/openssl/include \ + -I$(android)/external/openssl \ + -I$(android)/libcore/include \ + -I$(build)/android-src/external/fdlibm \ + -I$(build)/android-src \ + -fno-exceptions \ + -D_FILE_OFFSET_BITS=64 \ + -DOS_SHARED_LIB_FORMAT_STR="\"$(so-prefix)%s$(so-suffix)\"" \ + -DJNI_JARJAR_PREFIX= \ + -D__DARWIN_UNIX03=1 \ + -D__PROVIDE_FIXMES \ + -DSTATIC_LIB \ + -D__STDC_FORMAT_MACROS=1 \ + -g3 \ + -Wno-shift-count-overflow + + # on Windows (in MinGW-based build) there are neither __BEGIN_DECLS nor __END_DECLS + # defines; we don't want to patch every file that uses them, so we stub them in + # using CFLAGS mechanism + # Also we have off64_t defined in mingw-w64 headers, so let's tell that + ifeq ($(platform),windows) + android-cflags += "-D__BEGIN_DECLS=extern \"C\" {" "-D__END_DECLS=}" "-DHAVE_OFF64_T" + endif + + luni-cpps := $(shell find $(luni-native) -name '*.cpp') + + libziparchive-native := $(android)/system/core/libziparchive + libziparchive-ccs := $(libziparchive-native)/zip_archive.cc + + libutils-native := $(android)/system/core/libutils + libutils-cpps := $(libutils-native)/FileMap.cpp + + libnativehelper-native := $(android)/libnativehelper + libnativehelper-cpps := $(libnativehelper-native)/JniConstants.cpp \ + $(libnativehelper-native)/toStringArray.cpp + + crypto-native := $(android)/external/conscrypt/src/main/native + crypto-cpps := $(crypto-native)/org_conscrypt_NativeCrypto.cpp + + ifeq ($(platform),windows) + android-cflags += -D__STDC_CONSTANT_MACROS -DHAVE_WIN32_FILEMAP -D__STDC_FORMAT_MACROS + blacklist = $(luni-native)/java_io_Console.cpp \ + $(luni-native)/java_lang_ProcessManager.cpp + + icu-libs := $(android)/external/icu4c/lib/libsicuin.a \ + $(android)/external/icu4c/lib/libsicuuc.a \ + $(android)/external/icu4c/lib/sicudt.a + platform-lflags := -lgdi32 -lshlwapi -lwsock32 -static-libgcc -static-libstdc++ -Wl,-Bstatic -lstdc++ -lpthread -Wl,-Bdynamic + else + android-cflags += -fPIC -DHAVE_SYS_UIO_H -DHAVE_POSIX_FILEMAP + blacklist = + + icu-libs := $(android)/external/icu4c/lib/libicui18n.a \ + $(android)/external/icu4c/lib/libicuuc.a \ + $(android)/external/icu4c/lib/libicudata.a + endif + luni-cpps := $(filter-out $(blacklist),$(luni-cpps)) + + classpath-lflags := \ + $(icu-libs) \ + $(android)/external/fdlibm/libfdm.a \ + $(android)/external/expat/.libs/libexpat.a \ + $(android)/openssl-upstream/libssl.a \ + $(android)/openssl-upstream/libcrypto.a \ + $(platform-lflags) \ + -lstdc++ + + ifeq ($(platform),linux) + android-cflags += -DHAVE_OFF64_T + classpath-lflags += -lrt + endif + + classpath-objects = \ + $(call cpp-objects,$(luni-cpps),$(luni-native),$(build)) \ + $(call cpp-objects,$(crypto-cpps),$(crypto-native),$(build)) \ + $(call cpp-objects,$(libnativehelper-cpps),$(libnativehelper-native),$(build)) \ + $(call cc-objects,$(libziparchive-ccs),$(libziparchive-native),$(build)) \ + $(call cpp-objects,$(libutils-cpps),$(libutils-native),$(build)) + luni-java = $(android)/libcore/luni/src/main/java + + luni-blacklist = \ + $(luni-java)/libcore/reflect/AnnotationAccess.java + + luni-javas := \ + $(filter-out $(luni-blacklist),$(shell find $(luni-java) -name '*.java')) + + luni-nonjavas := $(shell find $(luni-java) -not -type d -not -name '*.java') + luni-copied-nonjavas = $(call noop-files,$(luni-nonjavas),$(luni-java),) + + crypto-java = $(android)/external/conscrypt/src/main/java + crypto-javas := $(shell find $(crypto-java) -name '*.java') + + crypto-platform-java = $(android)/external/conscrypt/src/platform/java + crypto-platform-javas := $(shell find $(crypto-platform-java) -name '*.java') + + dalvik-java = $(android)/libcore/dalvik/src/main/java + dalvik-javas := \ + $(dalvik-java)/dalvik/system/DalvikLogHandler.java \ + $(dalvik-java)/dalvik/system/CloseGuard.java \ + $(dalvik-java)/dalvik/system/VMDebug.java \ + $(dalvik-java)/dalvik/system/BlockGuard.java \ + $(dalvik-java)/dalvik/system/SocketTagger.java \ + $(dalvik-java)/dalvik/system/DalvikLogging.java \ + + libart-java = $(android)/libcore/libart/src/main/java + libart-javas := \ + $(libart-java)/dalvik/system/VMRuntime.java \ + $(libart-java)/dalvik/system/VMStack.java \ + $(libart-java)/java/lang/Thread.java \ + $(libart-java)/java/lang/ThreadGroup.java \ + $(libart-java)/java/lang/Enum.java \ + $(libart-java)/java/lang/String.java \ + $(libart-java)/java/lang/ref/Reference.java \ + $(libart-java)/java/lang/reflect/AccessibleObject.java \ + + xml-java = $(android)/libcore/xml/src/main/java + xml-javas := $(shell find $(xml-java) -name '*.java') + + okhttp-android-java = $(android)/external/okhttp/android/main/java + okhttp-android-javas := $(shell find $(okhttp-android-java) -name '*.java') + + okhttp-java = $(android)/external/okhttp/okhttp/src/main/java + okhttp-javas := $(shell find $(okhttp-java) -name '*.java') + + okio-java = $(android)/external/okhttp/okio/src/main/java + okio-javas := $(shell find $(okio-java) -name '*.java') + + bcpkix-java = $(android)/external/bouncycastle/bcpkix/src/main/java + bcpkix-javas := $(shell find $(bcpkix-java) -name '*.java') + + bcprov-java = $(android)/external/bouncycastle/bcprov/src/main/java + bcprov-javas := $(shell find $(bcprov-java) -name '*.java') + + android-classes = \ + $(call java-classes,$(luni-javas),$(luni-java),$(build)/android) \ + $(call java-classes,$(crypto-javas),$(crypto-java),$(build)/android) \ + $(call java-classes,$(crypto-platform-javas),$(crypto-platform-java),$(build)/android) \ + $(call java-classes,$(dalvik-javas),$(dalvik-java),$(build)/android) \ + $(call java-classes,$(libart-javas),$(libart-java),$(build)/android) \ + $(call java-classes,$(xml-javas),$(xml-java),$(build)/android) \ + $(call java-classes,$(okhttp-javas),$(okhttp-java),$(build)/android) \ + $(call java-classes,$(okhttp-android-javas),$(okhttp-android-java),$(build)/android) \ + $(call java-classes,$(okio-javas),$(okio-java),$(build)/android) \ + $(call java-classes,$(bcpkix-javas),$(bcpkix-java),$(build)/android) \ + $(call java-classes,$(bcprov-javas),$(bcprov-java),$(build)/android) + + classpath = android + + javahome-files = tzdata + javahome-object = $(build)/javahome-jar.o + boot-javahome-object = $(build)/boot-javahome.o + build-javahome = $(android)/bionic/libc/zoneinfo + stub-sources = $(src)/android/stubs.cpp + stub-objects = $(call cpp-objects,$(stub-sources),$(src),$(build)) +endif + +ifeq ($(classpath),avian) + jni-sources := $(shell find $(classpath-src) -name '*.cpp') + jni-objects = $(call cpp-objects,$(jni-sources),$(classpath-src),$(build)) + classpath-objects = $(jni-objects) +endif + +input = List + +ifeq ($(use-clang),true) + build-cxx = clang++ -std=c++11 + build-cc = clang +else + build-cxx = g++ + build-cc = gcc +endif + +mflag = +ifneq ($(kernel),darwin) + ifeq ($(arch),i386) + mflag = -m32 + endif + ifeq ($(arch),x86_64) + mflag = -m64 + endif +endif + +target-format = elf + +cxx = $(build-cxx) $(mflag) +cc = $(build-cc) $(mflag) + +ar = ar +ranlib = ranlib +dlltool = dlltool +vg = nice valgrind --num-callers=32 --db-attach=yes --freelist-vol=100000000 +vg += --leak-check=full --suppressions=valgrind.supp +db = gdb --args +javac = "$(JAVA_HOME)/bin/javac" -encoding UTF-8 +javah = "$(JAVA_HOME)/bin/javah" +jar = "$(JAVA_HOME)/bin/jar" +strip = strip +strip-all = --strip-all + +rdynamic = -rdynamic + +cflags_debug = -O0 -g3 +cflags_debug_fast = -O0 -g3 +cflags_stress = -O0 -g3 +cflags_stress_major = -O0 -g3 +ifeq ($(use-clang),true) + cflags_fast = -O3 -g3 + cflags_small = -Oz -g3 +else + cflags_fast = -O3 -g3 + cflags_small = -Os -g3 +endif + +# note that we suppress the non-virtual-dtor warning because we never +# use the delete operator, which means we don't need virtual +# destructors: +warnings = -Wall -Wextra $(werror) -Wunused-parameter -Winit-self \ + -Wno-non-virtual-dtor + +target-cflags = -DTARGET_BYTES_PER_WORD=$(pointer-size) + +common-cflags = $(warnings) -std=c++0x -fno-rtti -fno-exceptions -I$(classpath-src) \ + "-I$(JAVA_HOME)/include" -I$(src) -I$(build) -Iinclude $(classpath-cflags) \ + -D__STDC_LIMIT_MACROS -D_JNI_IMPLEMENTATION_ -DAVIAN_VERSION=\"$(version)\" \ + -DAVIAN_INFO="\"$(info)\"" \ + -DUSE_ATOMIC_OPERATIONS -DAVIAN_JAVA_HOME=\"$(javahome)\" \ + -DAVIAN_EMBED_PREFIX=\"$(embed-prefix)\" $(target-cflags) + +asmflags = $(target-cflags) -I$(src) + +ifneq (,$(filter i386 x86_64,$(arch))) + ifeq ($(use-frame-pointer),true) + common-cflags += -fno-omit-frame-pointer -DAVIAN_USE_FRAME_POINTER + asmflags += -DAVIAN_USE_FRAME_POINTER + endif +endif + +build-cflags = $(common-cflags) -fPIC -fvisibility=hidden \ + "-I$(JAVA_HOME)/include/linux" -I$(src) -pthread + +converter-cflags = -D__STDC_CONSTANT_MACROS -std=c++0x -Iinclude/ -Isrc/ \ + -fno-rtti -fno-exceptions \ + -DAVIAN_TARGET_ARCH=AVIAN_ARCH_UNKNOWN \ + -DAVIAN_TARGET_FORMAT=AVIAN_FORMAT_UNKNOWN \ + -Wall -Wextra $(werror) -Wunused-parameter -Winit-self -Wno-non-virtual-dtor + +cflags = $(build-cflags) + +common-lflags = -lm -lz + +ifeq ($(use-clang),true) + ifeq ($(build-kernel),darwin) + common-lflags += -Wl,-export_dynamic + else + ifneq ($(platform),windows) + common-lflags += -Wl,-E + else + common-lflags += -Wl,--export-all-symbols + endif + endif +endif + +build-lflags = -lz -lpthread -ldl + +lflags = $(common-lflags) -lpthread -ldl + +build-system = posix + +system = posix +asm = x86 + +ifeq ($(system),sgx) + cflags += -DSGX +endif + +pointer-size = 8 + +so-prefix = lib +so-suffix = .so + +static-prefix = lib +static-suffix = .a + +output = -o $(1) +asm-output = -o $(1) +asm-input = -c $(1) +asm-format = S +as = $(cc) +ld = $(cc) +build-ld = $(build-cc) +build-ld-cpp = $(build-cxx) + +default-remote-test-host = localhost +default-remote-test-port = 22 +ifeq ($(remote-test-host),) + remote-test-host = $(default-remote-test-host) +else + remote-test = true +endif +ifeq ($(remote-test-port),) + remote-test-port = $(default-remote-test-port) +else + remote-test = true +endif +remote-test-user = ${USER} +remote-test-dir = /tmp/avian-test-${USER} + +static = -static +shared = -shared + +rpath = -Wl,-rpath=\$$ORIGIN -Wl,-z,origin + +openjdk-extra-cflags = -fvisibility=hidden + +bootimage-cflags = -DTARGET_BYTES_PER_WORD=$(pointer-size) +bootimage-symbols = _binary_bootimage_bin_start:_binary_bootimage_bin_end +codeimage-symbols = _binary_codeimage_bin_start:_binary_codeimage_bin_end + +developer-dir := $(shell if test -d /Developer/Platforms/$(target).platform/Developer/SDKs; then echo /Developer; \ + else echo /Applications/Xcode.app/Contents/Developer; fi) + +ifneq (,$(filter i386 arm,$(arch))) + pointer-size = 4 +endif + +ifneq (,$(filter arm arm64,$(arch))) + asm = arm + + ifneq ($(platform),ios) + ifneq ($(arch),arm64) + no-psabi = -Wno-psabi + cflags += -marm $(no-psabi) + + # By default, assume we can't use armv7-specific instructions on + # non-iOS platforms. Ideally, we'd detect this at runtime. + armv6=true + endif + endif + + ifneq ($(arch),$(build-arch)) + ifneq ($(kernel),darwin) + ifeq ($(arch),arm64) + cxx = aarch64-linux-gnu-g++ + cc = aarch64-linux-gnu-gcc + ar = aarch64-linux-gnu-ar + ranlib = aarch64-linux-gnu-ranlib + strip = aarch64-linux-gnu-strip + else + cxx = arm-linux-gnueabi-g++ + cc = arm-linux-gnueabi-gcc + ar = arm-linux-gnueabi-ar + ranlib = arm-linux-gnueabi-ranlib + strip = arm-linux-gnueabi-strip + endif + endif + endif +endif + +ifeq ($(armv6),true) + cflags += -DAVIAN_ASSUME_ARMV6 +endif + +ifeq ($(platform),ios) + cflags += -DAVIAN_IOS + use-lto = false +endif + +ifeq ($(build-kernel),darwin) + build-cflags = $(common-cflags) -fPIC -fvisibility=hidden -I$(src) + cflags += -Wno-deprecated-declarations + build-lflags += -framework CoreFoundation +endif + +ifeq ($(platform),qnx) + cflags = $(common-cflags) -fPIC -fvisibility=hidden -I$(src) + lflags = $(common-lflags) -lsocket + ifeq ($(build-platform),qnx) + build-cflags = $(common-cflags) -fPIC -fvisibility=hidden -I$(src) + build-lflags = $(common-lflags) + else + ifeq ($(arch),i386) + prefix = i486-pc-nto-qnx6.5.0- + else + prefix = arm-unknown-nto-qnx6.5.0- + endif + endif + cxx = $(prefix)g++ + cc = $(prefix)gcc + ar = $(prefix)ar + ranlib = $(prefix)ranlib + strip = $(prefix)strip + rdynamic = -Wl,--export-dynamic +endif + +ifeq ($(platform),freebsd) +# There is no -ldl on FreeBSD + build-lflags = $(common-lflags) -lz -lpthread + lflags = $(common-lflags) -lpthread +# include/freebsd instead of include/linux + build-cflags = $(common-cflags) -fPIC -fvisibility=hidden \ + "-I$(JAVA_HOME)/include/freebsd" -I$(src) -pthread + cflags = $(build-cflags) +endif +ifeq ($(platform),android) + ifeq ($(build-platform),cygwin) + ndk = "$$(cygpath -u "$(ANDROID_NDK)")" + else + ndk = $(ANDROID_NDK) + endif + + ifeq ($(android-version),) + android-version = 5 + endif + + ifeq ($(android-toolchain),) + android-toolchain = 4.7 + endif + + ifeq ($(arch),arm) + android-toolchain-name = arm-linux-androideabi + android-toolchain-prefix = arm-linux-androideabi- + endif + ifeq ($(arch),i386) + android-toolchain-name = x86 + android-toolchain-prefix = i686-linux-android- + endif + + ifeq ($(android-arm-arch),) + android-arm-arch = armv5 + endif + + options := $(options)-api$(android-version)-$(android-toolchain)-$(android-arm-arch) + + build-cflags = $(common-cflags) -I$(src) + build-lflags = -lz -lpthread + ifeq ($(subst cygwin,windows,$(subst mingw32,windows,$(build-platform))),windows) + toolchain-host-platform = $(subst cygwin,windows,$(subst mingw32,windows,$(build-platform))) + build-system = windows + build-cxx = i686-w64-mingw32-g++ + build-cc = i686-w64-mingw32-gcc + sysroot = "$$(cygpath -w "$(ndk)/platforms/android-$(android-version)/arch-arm")" + build-cflags += "-I$(JAVA_HOME)/include/win32" + else + toolchain-host-platform = $(subst cygwin,windows,$(subst mingw32,windows,$(build-platform)))-* + sysroot = $(ndk)/platforms/android-$(android-version)/arch-arm + build-cflags += "-I$(JAVA_HOME)/include/linux" + build-lflags += -ldl + endif + toolchain = $(ndk)/toolchains/$(android-toolchain-name)-$(android-toolchain)/prebuilt/$(toolchain-host-platform) + cflags = "-I$(sysroot)/usr/include" "-I$(JAVA_HOME)/include/linux" $(common-cflags) "-I$(src)" -std=c++11 $(no-psabi) + lflags = "-L$(sysroot)/usr/lib" $(common-lflags) -llog + target-format = elf + use-lto = false + + ifeq ($(arch),arm) + cflags += -marm -march=$(android-arm-arch) -ftree-vectorize -ffast-math -mfloat-abi=softfp + endif + ifeq ($(arch),i386) + endif + + cxx = $(toolchain)/bin/$(android-toolchain-prefix)g++ --sysroot="$(sysroot)" + cc = $(toolchain)/bin/$(android-toolchain-prefix)gcc --sysroot="$(sysroot)" + as = $(cxx) + ar = $(toolchain)/bin/$(android-toolchain-prefix)ar + ranlib = $(toolchain)/bin/$(android-toolchain-prefix)ranlib + strip = $(toolchain)/bin/$(android-toolchain-prefix)strip +endif + +ifeq ($(kernel),darwin) + target-format = macho + ifeq (${OSX_SDK_SYSROOT},) + OSX_SDK_SYSROOT = 10.6u + endif + ifeq (${OSX_SDK_VERSION},) + OSX_SDK_VERSION = 10.6 + endif + ifneq ($(build-kernel),darwin) + cxx = i686-apple-darwin8-g++ $(mflag) + cc = i686-apple-darwin8-gcc $(mflag) + ar = i686-apple-darwin8-ar + ranlib = i686-apple-darwin8-ranlib + strip = i686-apple-darwin8-strip + sysroot = /opt/mac/SDKs/MacOSX${OSX_SDK_SYSROOT}.sdk + cflags = -I$(sysroot)/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Headers/ \ + $(common-cflags) -fPIC -fvisibility=hidden -I$(src) + endif + + ifneq ($(platform),ios) + platform-dir = $(developer-dir)/Platforms/MacOSX.platform + sdk-dir = $(platform-dir)/Developer/SDKs + + mac-version := $(shell \ + if test -d $(sdk-dir)/MacOSX10.11.sdk; then echo 10.11; \ + elif test -d $(sdk-dir)/MacOSX10.10.sdk; then echo 10.10; \ + elif test -d $(sdk-dir)/MacOSX10.9.sdk; then echo 10.9; \ + elif test -d $(sdk-dir)/MacOSX10.8.sdk; then echo 10.8; \ + elif test -d $(sdk-dir)/MacOSX10.7.sdk; then echo 10.7; \ + elif test -d $(sdk-dir)/MacOSX10.6.sdk; then echo 10.6; \ + else echo; fi) + + sysroot = $(sdk-dir)/MacOSX$(mac-version).sdk + endif + + soname-flag = + version-script-flag = + lflags = $(common-lflags) -ldl -framework CoreFoundation -framework Foundation + + ifeq (,$(shell ld -v 2>&1 | grep cctools)) + lflags += -Wl,-compatibility_version,1.0.0 + endif + + ifneq ($(platform),ios) + lflags += -framework CoreServices -framework SystemConfiguration \ + -framework Security + endif + ifeq ($(bootimage),true) + bootimage-lflags = -Wl,-segprot,__RWX,rwx,rwx + endif + rdynamic = + strip-all = -S -x + so-suffix = .dylib + shared = -dynamiclib + rpath = + + ifeq ($(platform),ios) + ifeq ($(sim),true) + target = iPhoneSimulator + sdk = iphonesimulator$(ios-version) + ifeq ($(arch),i386) + arch-flag = -arch i386 + else + arch-flag = -arch x86_64 + arch = x86_64 + endif + release = Release-iphonesimulator + else + target = iPhoneOS + sdk = iphoneos$(ios-version) + ifeq ($(arch),arm) + arch-flag = -arch armv7 + else + arch-flag = -arch arm64 + arch = arm64 + endif + release = Release-iphoneos + endif + + platform-dir = $(developer-dir)/Platforms/$(target).platform + sdk-dir = $(platform-dir)/Developer/SDKs + + ios-version := $(shell for x in 10.0 9.3 9.2 9.1 9.0 8.3 8.2 8.1 8.0; \ + do if test -d $(sdk-dir)/$(target)$$x.sdk \ + -o -L $(sdk-dir)/$(target)$$x.sdk; \ + then echo $$x; break; fi; done) + + ifeq ($(ios-version),) + x := $(error "couldn't find SDK in $(sdk-dir)") + endif + + sysroot = $(sdk-dir)/$(target)$(ios-version).sdk + +# apparently, the header files we need are part of the simulator SDK +# but not the device SDK, so we copy them from the former even if +# we're targeting the latter + + header-sysroot := $(subst iPhoneOS,iPhoneSimulator,$(sysroot)) + + ios-bin = $(platform-dir)/Developer/usr/bin + + found-gcc = $(shell if test -f $(ios-bin)/gcc; then echo true; else echo false; fi) + + ifeq ($(found-gcc),false) + use-clang = true + endif + + ifeq ($(use-clang),true) + cxx = clang -std=c++11 + cc = clang + else + cxx = $(ios-bin)/g++ + cc = $(ios-bin)/gcc + endif + + flags = -isysroot $(sdk-dir)/$(target)$(ios-version).sdk \ + $(arch-flag) + + classpath-extra-cflags += $(flags) + cflags += $(flags) + asmflags += $(flags) + lflags += $(flags) + + ios-version-min=$(ios-version) + ifdef ios_deployment_target + ios-version-min=ios_deployment_target + endif + + ifeq ($(sim),true) + ifeq ($(arch),x86_64) + classpath-extra-cflags += \ + -arch x86_64 -miphoneos-version-min=$(ios-version-min) + cflags += -arch x86_64 -miphoneos-version-min=$(ios-version-min) + asmflags += -arch x86_64 -miphoneos-version-min=$(ios-version-min) + lflags += -arch x86_64 -miphoneos-version-min=$(ios-version-min) + else + classpath-extra-cflags += \ + -arch i386 -miphoneos-version-min=$(ios-version-min) + cflags += -arch i386 -miphoneos-version-min=$(ios-version-min) + asmflags += -arch i386 -miphoneos-version-min=$(ios-version-min) + lflags += -arch i386 -miphoneos-version-min=$(ios-version-min) + endif + else + ifeq ($(arch),arm64) + classpath-extra-cflags += \ + -arch arm64 -miphoneos-version-min=$(ios-version-min) + cflags += -arch arm64 -miphoneos-version-min=$(ios-version-min) + asmflags += -arch arm64 -miphoneos-version-min=$(ios-version-min) + lflags += -arch arm64 -miphoneos-version-min=$(ios-version-min) + else + classpath-extra-cflags += \ + -arch armv7 -miphoneos-version-min=$(ios-version-min) + cflags += -arch armv7 -miphoneos-version-min=$(ios-version-min) + asmflags += -arch armv7 -miphoneos-version-min=$(ios-version-min) + lflags += -arch armv7 -miphoneos-version-min=$(ios-version-min) + endif + endif + else # not ios + ifeq ($(arch),i386) + classpath-extra-cflags += \ + -arch i386 -mmacosx-version-min=${OSX_SDK_VERSION} + cflags += -arch i386 -mmacosx-version-min=${OSX_SDK_VERSION} + asmflags += -arch i386 -mmacosx-version-min=${OSX_SDK_VERSION} + lflags += -arch i386 -mmacosx-version-min=${OSX_SDK_VERSION} + endif + + ifeq ($(arch),x86_64) + classpath-extra-cflags += -arch x86_64 + cflags += -arch x86_64 + asmflags += -arch x86_64 + lflags += -arch x86_64 + endif + endif + cflags += -I$(JAVA_HOME)/include/darwin +endif + +openjdk-extra-cflags += $(classpath-extra-cflags) + +find-tool = $(shell if ( command -v "$(1)$(2)" >/dev/null ); then (echo "$(1)$(2)") else (echo "$(2)"); fi) + +ifeq ($(platform),windows) + target-format = pe + + inc = "$(win32)/include" + lib = "$(win32)/lib" + + embed-prefix = c:/avian-embedded + system = windows + + so-prefix = + so-suffix = .dll + exe-suffix = .exe + rpath = + + lflags = -L$(lib) $(common-lflags) -lws2_32 -lversion -luuid -liphlpapi \ + -lmswsock -mconsole + + cflags = -I$(inc) $(common-cflags) -DWINVER=0x0500 -U__STRICT_ANSI__ + + ifeq (,$(filter mingw32 cygwin,$(build-platform))) + openjdk-extra-cflags += -I$(src)/openjdk/caseSensitive + prefix := $(shell i686-w64-mingw32-gcc --version >/dev/null 2>&1 \ + && echo i686-w64-mingw32- || echo x86_64-w64-mingw32-) + cxx = $(prefix)g++ -m32 + cc = $(prefix)gcc -m32 + dlltool = $(prefix)dlltool -mi386 --as-flags=--32 + ar = $(prefix)ar + ranlib = $(prefix)ranlib + strip = $(prefix)strip --strip-all + else + build-system = windows + static-on-windows = -static + common-cflags += "-I$(JAVA_HOME)/include/win32" + build-cflags = $(common-cflags) -I$(src) -I$(inc) -mthreads \ + -D_WIN32_WINNT=0x0500 + openjdk-extra-cflags = + build-lflags = -L$(lib) $(common-lflags) + ifeq ($(build-platform),cygwin) + build-cxx = i686-w64-mingw32-g++ + build-cc = i686-w64-mingw32-gcc + dlltool = i686-w64-mingw32-dlltool + ar = i686-w64-mingw32-ar + ranlib = i686-w64-mingw32-ranlib + strip = i686-w64-mingw32-strip + endif + endif + + ifeq ($(arch),x86_64) + ifeq ($(build-platform),cygwin) + build-cxx = x86_64-w64-mingw32-g++ + build-cc = x86_64-w64-mingw32-gcc + endif + cxx = x86_64-w64-mingw32-g++ $(mflag) + cc = x86_64-w64-mingw32-gcc $(mflag) + dlltool = $(call find-tool,x86_64-w64-mingw32-,dlltool) + ar = $(call find-tool,x86_64-w64-mingw32-,ar) + ranlib = $(call find-tool,x86_64-w64-mingw32-,ranlib) + strip = $(call find-tool,x86_64-w64-mingw32-,strip) + inc = "$(win64)/include" + lib = "$(win64)/lib" + else + shared += -Wl,--add-stdcall-alias + endif + + embed = $(build-embed)/embed$(exe-suffix) + embed-loader = $(build-embed-loader)/embed-loader$(exe-suffix) + embed-loader-o = $(build-embed)/embed-loader.o +endif + +ifeq ($(platform),wp8) + ifeq ($(shell uname -s | grep -i -c WOW64),1) + programFiles = Program Files (x86) + else + programFiles = Program Files + endif + ifeq ($(MSVS_ROOT),) + # Environment variable MSVS_ROOT not found. It should be something like + # "C:\$(programFiles)\Microsoft Visual Studio 11.0" + MSVS_ROOT = C:\$(programFiles)\Microsoft Visual Studio 11.0 + endif + ifeq ($(MSVC_ROOT),) + # Environment variable MSVC_ROOT not found. It should be something like + # "C:\$(programFiles)\Microsoft Visual Studio 11.0\VC" + MSVC_ROOT = $(MSVS_ROOT)\VC + endif + ifeq ($(WP80_SDK),) + # Environment variable WP8_SDK not found. It should be something like + # "C:\Program Files[ (x86)]\Microsoft Visual Studio 11.0\VC\WPSDK\WP80" + # TODO: Lookup in SOFTWARE\Microsoft\Microsoft SDKs\WindowsPhone\v8.0 + WP80_SDK = $(MSVS_ROOT)\VC\WPSDK\WP80 + endif + ifeq ($(WP80_KIT),) + # Environment variable WP8_KIT not found. It should be something like + # "c:\Program Files[ (x86)]\Windows Phone Kits\8.0" + # TODO: Lookup in SOFTWARE\Microsoft\Microsoft SDKs\WindowsPhone\v8.0 + WP80_KIT = C:\$(programFiles)\Windows Phone Kits\8.0 + endif + ifeq ($(WIN8_KIT),) + # Environment variable WIN8_KIT not found. It should be something like + # "c:\Program Files[ (x86)]\Windows Kits\8.0" + WIN8_KIT = C:\$(programFiles)\Windows Kits\8.0 + endif + ifeq ($(build-platform),cygwin) + windows-path = cygpath -w + else + windows-path = $(native-path) + endif + windows-java-home := $(shell $(windows-path) "$(JAVA_HOME)") + target-format = pe + ms_cl_compiler = wp8 + use-lto = false + supports_avian_executable = false + aot-only = true + ifneq ($(bootimage),true) + x := $(error Windows Phone 8 target requires bootimage=true) + endif + system = windows + build-system = windows + static-prefix = + static-suffix = .lib + so-prefix = + so-suffix = .dll + exe-suffix = .exe + manifest-flags = -MANIFEST:NO + + ifeq ($(arch),arm) + wp8_arch = \x86_arm + vc_arch = \arm + w8kit_arch = arm + deps_arch = ARM + as = "$$(cygpath -u "$(WP80_SDK)\bin\x86_arm\armasm.exe")" + cxx = "$$(cygpath -u "$(WP80_SDK)\bin\x86_arm\cl.exe")" + ld = "$$(cygpath -u "$(WP80_SDK)\bin\x86_arm\link.exe")" + asmflags = -machine ARM -32 + asm-output = -o $(1) + asm-input = $(1) + machine_type = ARM + bootimage-symbols = binary_bootimage_bin_start:binary_bootimage_bin_end + codeimage-symbols = binary_codeimage_bin_start:binary_codeimage_bin_end + endif + ifeq ($(arch),i386) + wp8_arch = + vc_arch = + w8kit_arch = x86 + deps_arch = x86 + asmflags = $(target-cflags) -safeseh -nologo -Gd + as = "$$(cygpath -u "$(WP80_SDK)\bin\ml.exe")" + cxx = "$$(cygpath -u "$(WP80_SDK)\bin\cl.exe")" + ld = "$$(cygpath -u "$(WP80_SDK)\bin\link.exe")" + ifeq ($(mode),debug) + asmflags += -Zd + endif + ifeq ($(mode),debug-fast) + asmflags += -Zd + endif + asm-output = $(output) + machine_type = X86 + endif + + PATH := $(shell cygpath -u "$(MSVS_ROOT)\Common7\IDE"):$(shell cygpath -u "$(WP80_SDK)\bin$(wp8_arch)"):$(shell cygpath -u "$(WP80_SDK)\bin"):${PATH} + + build-cflags = $(common-cflags) -I$(src) -I$(inc) -mthreads + build-lflags = -lz -lpthread + + cflags = -nologo \ + -AI"$(WP80_KIT)\Windows Metadata" \ + -I"$(WP80_SDK)\include" -I"$(WP80_KIT)\Include" -I"$(WP80_KIT)\Include\minwin" -I"$(WP80_KIT)\Include\mincore" \ + -DWINAPI_FAMILY=WINAPI_FAMILY_PHONE_APP -D_USRDLL -D_WINDLL \ + -DAVIAN_VERSION=\"$(version)\" -D_JNI_IMPLEMENTATION_ \ + -DUSE_ATOMIC_OPERATIONS -DAVIAN_JAVA_HOME=\"$(javahome)\" \ + -DAVIAN_EMBED_PREFIX=\"$(embed-prefix)\" \ + -I"$(shell $(windows-path) "$(wp8)/zlib/upstream")" -I"$(shell $(windows-path) "$(wp8)/interop/avian-interop-client")" \ + -I"$(shell $(windows-path) "$(wp8)/include")" -I$(src) -I$(classpath-src) \ + -I"$(build)" \ + -I"$(windows-java-home)/include" -I"$(windows-java-home)/include/win32" \ + -DTARGET_BYTES_PER_WORD=$(pointer-size) \ + -Gd -EHsc + + common-lflags = + + ifeq ($(mode),debug) + build-type = Debug + endif + ifeq ($(mode),debug-fast) + build-type = Debug + endif + ifeq ($(mode),stress-major) + build-type = Release + endif + ifeq ($(mode),fast) + build-type = Release + endif + ifeq ($(mode),fast) + build-type = Release + endif + ifeq ($(mode),small) + build-type = Release + endif + + arflags = -MACHINE:$(machine_type) + lflags = $(common-lflags) -nologo \ + -MACHINE:$(machine_type) \ + -LIBPATH:"$(WP80_KIT)\lib\$(w8kit_arch)" -LIBPATH:"$(WP80_SDK)\lib$(vc_arch)" -LIBPATH:"$(WIN8_KIT)\Lib\win8\um\$(w8kit_arch)" \ + ws2_32.lib \ + "$(shell $(windows-path) "$(wp8)\lib\$(deps_arch)\$(build-type)\zlib.lib")" "$(shell $(windows-path) "$(wp8)\lib\$(deps_arch)\$(build-type)\ThreadEmulation.lib")" \ + "$(shell $(windows-path) "$(wp8)\lib\$(deps_arch)\$(build-type)\AvianInteropClient.lib")" + lflags += -NXCOMPAT -DYNAMICBASE -SUBSYSTEM:CONSOLE -TLBID:1 + lflags += -NODEFAULTLIB:"ole32.lib" -NODEFAULTLIB:"kernel32.lib" + lflags += PhoneAppModelHost.lib WindowsPhoneCore.lib -WINMD -WINMDFILE:$(subst $(so-suffix),.winmd,$(@)) + + cc = $(cxx) + asm-format = masm + shared = -dll + ar = "$$(cygpath -u "$(WP80_SDK)\bin\lib.exe")" + arflags += -nologo + ifeq ($(build-platform),cygwin) + build-cxx = i686-w64-mingw32-g++ + build-cc = i686-w64-mingw32-gcc + dlltool = i686-w64-mingw32-dlltool + ranlib = + strip = + endif + output = -Fo$(1) + + #TODO: -MT or -ZW? + cflags_debug = -Od -Zi -MDd + cflags_debug_fast = -Od -Zi -MDd + cflags_stress = -O0 -g3 -MD + cflags_stress_major = -O0 -g3 -MD + cflags_fast = -O2 -Zi -MD + cflags_small = -O1s -Zi -MD + # -GL [whole program optimization] in 'fast' and 'small' breaks compilation for some reason + + ifeq ($(mode),debug) + cflags += + lflags += + endif + ifeq ($(mode),debug-fast) + cflags += -DNDEBUG + lflags += + endif + ifeq ($(mode),stress-major) + cflags += + lflags += + endif + ifeq ($(mode),fast) + cflags += + lflags += + endif + # -LTCG is needed only if -GL is used + ifeq ($(mode),fast) + cflags += -DNDEBUG + lflags += -LTCG + arflags += + endif + ifeq ($(mode),small) + cflags += -DNDEBUG + lflags += -LTCG + arflags += + endif + + strip = : +endif + +ifdef msvc + target-format = pe + windows-path = $(native-path) + windows-java-home := $(shell $(windows-path) "$(JAVA_HOME)") + zlib := $(shell $(windows-path) "$(win32)/msvc") + ms_cl_compiler = regular + as = $(build-cc) + cxx = "$(msvc)/BIN/cl.exe" + cc = $(cxx) + ld = "$(msvc)/BIN/link.exe" + mt = "mt.exe" + ar = "$(msvc)/BIN/lib.exe" + manifest-flags = -MANIFEST -MANIFESTFILE:$(@).manifest + cflags = -nologo -DAVIAN_VERSION=\"$(version)\" -D_JNI_IMPLEMENTATION_ \ + -DUSE_ATOMIC_OPERATIONS -DAVIAN_JAVA_HOME=\"$(javahome)\" \ + -DAVIAN_EMBED_PREFIX=\"$(embed-prefix)\" \ + -Fd$(build)/$(name).pdb -I"$(zlib)/include" -I$(src) -I$(classpath-src) \ + -I"$(build)" -Iinclude \ + -I"$(windows-java-home)/include" -I"$(windows-java-home)/include/win32" \ + -DTARGET_BYTES_PER_WORD=$(pointer-size) + + ifneq ($(lzma),) + cflags += -I$(shell $(windows-path) "$(lzma)") + endif + + shared = -dll + lflags = -nologo -LIBPATH:"$(zlib)/lib" -DEFAULTLIB:ws2_32 \ + -DEFAULTLIB:zlib -DEFAULTLIB:user32 -MANIFEST -debug + output = -Fo$(1) + + cflags_debug = -Od -Zi -MDd + cflags_debug_fast = -Od -Zi -DNDEBUG + cflags_fast = -O2 -GL -Zi -DNDEBUG + cflags_small = -O1s -Zi -GL -DNDEBUG + ifeq ($(mode),fast) + lflags += -LTCG + endif + ifeq ($(mode),small) + lflags += -LTCG + endif + + use-lto = false + strip = : +endif + +ifeq ($(mode),debug) + optimization-cflags = $(cflags_debug) + converter-cflags += $(cflags_debug) + strip = : +endif +ifeq ($(mode),debug-fast) + optimization-cflags = $(cflags_debug_fast) -DNDEBUG + strip = : +endif +ifeq ($(mode),stress) + optimization-cflags = $(cflags_stress) -DVM_STRESS + strip = : +endif +ifeq ($(mode),stress-major) + optimization-cflags = $(cflags_stress_major) -DVM_STRESS -DVM_STRESS_MAJOR + strip = : +endif +ifeq ($(mode),fast) + optimization-cflags = $(cflags_fast) -DNDEBUG +endif +ifeq ($(mode),small) + optimization-cflags = $(cflags_small) -DNDEBUG +endif + +ifeq ($(use-lto),true) + ifeq ($(use-clang),true) + optimization-cflags += -flto + lflags += $(optimization-cflags) + else +# only try to use LTO when GCC 4.6.0 or greater is available + gcc-major := $(shell $(cc) -dumpversion | cut -f1 -d.) + gcc-minor := $(shell $(cc) -dumpversion | cut -f2 -d.) + ifeq ($(shell expr 4 \< $(gcc-major) \ + \| \( 4 \<= $(gcc-major) \& 6 \<= $(gcc-minor) \)),1) + optimization-cflags += -flto + no-lto = -fno-lto + lflags += $(optimization-cflags) + endif + endif +endif + +cflags += $(optimization-cflags) + +ifndef ms_cl_compiler +ifneq ($(kernel),darwin) +ifeq ($(arch),i386) +# this is necessary to support __sync_bool_compare_and_swap: + cflags += -march=i586 + lflags += -march=i586 +endif +endif +endif + +c-objects = $(foreach x,$(1),$(patsubst $(2)/%.c,$(3)/%.o,$(x))) +cpp-objects = $(foreach x,$(1),$(patsubst $(2)/%.cpp,$(3)/%.o,$(x))) +cc-objects = $(foreach x,$(1),$(patsubst $(2)/%.cc,$(3)/%.o,$(x))) +asm-objects = $(foreach x,$(1),$(patsubst $(2)/%.$(asm-format),$(3)/%-asm.o,$(x))) +java-classes = $(foreach x,$(1),$(patsubst $(2)/%.java,$(3)/%.class,$(x))) +noop-files = $(foreach x,$(1),$(patsubst $(2)/%,$(3)/%,$(x))) + +generated-code = \ + $(build)/type-enums.cpp \ + $(build)/type-declarations.cpp \ + $(build)/type-constructors.cpp \ + $(build)/type-initializations.cpp \ + $(build)/type-java-initializations.cpp \ + $(build)/type-name-initializations.cpp \ + $(build)/type-maps.cpp + +vm-depends := $(generated-code) \ + $(shell find src include -name '*.h' -or -name '*.inc.cpp') + +vm-sources = \ + $(src)/system/$(system).cpp \ + $(wildcard $(src)/system/$(system)/*.cpp) \ + $(src)/finder.cpp \ + $(src)/machine.cpp \ + $(src)/util.cpp \ + $(src)/heap/heap.cpp \ + $(src)/$(process).cpp \ + $(src)/classpath-$(classpath).cpp \ + $(src)/builtin.cpp \ + $(src)/jnienv.cpp \ + $(src)/process.cpp \ + $(src)/heapdump.cpp + +vm-asm-sources = $(src)/$(arch).$(asm-format) + +target-asm = $(asm) + +build-embed = $(build)/embed +build-embed-loader = $(build)/embed-loader + +embed-loader-sources = $(src)/embedded-loader.cpp +embed-loader-objects = $(call cpp-objects,$(embed-loader-sources),$(src),$(build-embed-loader)) + +embed-sources = $(src)/embed.cpp +embed-objects = $(call cpp-objects,$(embed-sources),$(src),$(build-embed)) + +compiler-sources = \ + $(src)/codegen/compiler.cpp \ + $(wildcard $(src)/codegen/compiler/*.cpp) \ + $(src)/debug-util.cpp \ + $(src)/codegen/runtime.cpp \ + $(src)/codegen/targets.cpp \ + $(src)/util/fixed-allocator.cpp + +x86-assembler-sources = $(wildcard $(src)/codegen/target/x86/*.cpp) + +arm-assembler-sources = $(wildcard $(src)/codegen/target/arm/*.cpp) + +all-assembler-sources = \ + $(x86-assembler-sources) \ + $(arm-assembler-sources) + +native-assembler-sources = $($(target-asm)-assembler-sources) + +all-codegen-target-sources = \ + $(compiler-sources) \ + $(native-assembler-sources) + +ifeq ($(process),compile) + vm-sources += $(compiler-sources) + + ifeq ($(codegen-targets),native) + vm-sources += $(native-assembler-sources) + endif + ifeq ($(codegen-targets),all) + ifneq (,$(filter arm arm64,$(arch))) + # The x86 jit has a dependency on the x86 assembly code, + # and thus can't be successfully built on non-x86 platforms. + vm-sources += $(native-assembler-sources) + else + vm-sources += $(all-assembler-sources) + endif + endif + + vm-asm-sources += $(src)/compile-$(arch).$(asm-format) +endif +ifeq ($(aot-only),true) + cflags += -DAVIAN_AOT_ONLY +endif + +vm-cpp-objects = $(call cpp-objects,$(vm-sources),$(src),$(build)) +all-codegen-target-objects = $(call cpp-objects,$(all-codegen-target-sources),$(src),$(build)) +vm-asm-objects = $(call asm-objects,$(vm-asm-sources),$(src),$(build)) +vm-objects = $(vm-cpp-objects) $(vm-asm-objects) + +heapwalk-sources = $(src)/heapwalk.cpp +heapwalk-objects = \ + $(call cpp-objects,$(heapwalk-sources),$(src),$(build)) + +unittest-objects = $(call cpp-objects,$(unittest-sources),$(unittest),$(build)/unittest) + +vm-heapwalk-objects = $(heapwalk-objects) + +ifeq ($(tails),true) + cflags += -DAVIAN_TAILS +endif + +ifeq ($(continuations),true) + cflags += -DAVIAN_CONTINUATIONS + asmflags += -DAVIAN_CONTINUATIONS +endif + +bootimage-generator-sources = $(src)/tools/bootimage-generator/main.cpp $(src)/util/arg-parser.cpp $(stub-sources) + +ifneq ($(lzma),) + bootimage-generator-sources += $(src)/lzma-encode.cpp +endif + +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)/$(so-prefix)jvm$(so-suffix) + +bootimage-object = $(build)/bootimage-bin.o +codeimage-object = $(build)/codeimage-bin.o + +ifeq ($(bootimage),true) + vm-classpath-objects = $(bootimage-object) $(codeimage-object) + cflags += -DBOOT_IMAGE -DAVIAN_CLASSPATH=\"\" +else + vm-classpath-objects = $(classpath-object) + cflags += -DBOOT_CLASSPATH=\"[classpathJar]\" \ + -DAVIAN_CLASSPATH=\"[classpathJar]\" +endif + +cflags += $(extra-cflags) +lflags += $(extra-lflags) + +openjdk-cflags += $(extra-cflags) + +driver-source = $(src)/main.cpp +driver-object = $(build)/main.o +driver-dynamic-objects = \ + $(build)/main-dynamic.o + +boot-source = $(src)/boot.cpp +boot-object = $(build)/boot.o + +generator-depends := $(wildcard $(src)/*.h) +generator-sources = \ + $(src)/tools/type-generator/main.cpp \ + $(src)/system/$(build-system).cpp \ + $(wildcard $(src)/system/$(build-system)/*.cpp) \ + $(src)/finder.cpp \ + $(src)/util/arg-parser.cpp + +ifneq ($(lzma),) + common-cflags += -I$(lzma) -DAVIAN_USE_LZMA + + vm-sources += \ + $(src)/lzma-decode.cpp + + generator-sources += \ + $(src)/lzma-decode.cpp + + lzma-decode-sources = \ + $(lzma)/C/LzmaDec.c + + lzma-decode-objects = \ + $(call c-objects,$(lzma-decode-sources),$(lzma)/C,$(build)) + + lzma-encode-sources = \ + $(lzma)/C/LzmaEnc.c \ + $(lzma)/C/LzFind.c + + lzma-encode-objects = \ + $(call c-objects,$(lzma-encode-sources),$(lzma)/C,$(build)) + + lzma-encoder = $(build)/lzma/lzma + + lzma-build-cflags = -D_7ZIP_ST -D__STDC_CONSTANT_MACROS \ + -fno-exceptions -fPIC -I$(lzma)/C + + lzma-cflags = $(lzma-build-cflags) $(classpath-extra-cflags) + + lzma-encoder-sources = \ + $(src)/lzma/main.cpp + + lzma-encoder-objects = \ + $(call cpp-objects,$(lzma-encoder-sources),$(src),$(build)) + + lzma-encoder-lzma-sources = $(lzma-encode-sources) $(lzma-decode-sources) + + lzma-encoder-lzma-objects = \ + $(call generator-c-objects,$(lzma-encoder-lzma-sources),$(lzma)/C,$(build)) + + lzma-loader = $(build)/lzma/load.o + + lzma-library = $(build)/libavian-lzma.a +endif + +generator-cpp-objects = \ + $(foreach x,$(1),$(patsubst $(2)/%.cpp,$(3)/%-build.o,$(x))) +generator-c-objects = \ + $(foreach x,$(1),$(patsubst $(2)/%.c,$(3)/%-build.o,$(x))) +generator-objects = \ + $(call generator-cpp-objects,$(generator-sources),$(src),$(build)) +generator-lzma-objects = \ + $(call generator-c-objects,$(lzma-decode-sources),$(lzma)/C,$(build)) +generator = $(build)/generator + +all-depends = $(shell find include -name '*.h') + +object-writer-depends = $(shell find $(src)/tools/object-writer -name '*.h') +object-writer-sources = $(shell find $(src)/tools/object-writer -name '*.cpp') +object-writer-objects = $(call cpp-objects,$(object-writer-sources),$(src),$(build)) + +binary-to-object-depends = $(shell find $(src)/tools/binary-to-object/ -name '*.h') +binary-to-object-sources = $(shell find $(src)/tools/binary-to-object/ -name '*.cpp') +binary-to-object-objects = $(call cpp-objects,$(binary-to-object-sources),$(src),$(build)) + +converter-sources = $(object-writer-sources) + +converter-tool-depends = $(binary-to-object-depends) $(all-depends) +converter-tool-sources = $(binary-to-object-sources) + +converter-objects = $(call cpp-objects,$(converter-sources),$(src),$(build)) +converter-tool-objects = $(call cpp-objects,$(converter-tool-sources),$(src),$(build)) +converter = $(build)/binaryToObject/binaryToObject + +static-library = $(build)/$(static-prefix)$(name)$(static-suffix) +executable = $(build)/$(name)${exe-suffix} +dynamic-library = $(build)/$(so-prefix)jvm$(so-suffix) +executable-dynamic = $(build)/$(name)-dynamic$(exe-suffix) + +unittest-executable = $(build)/$(name)-unittest${exe-suffix} + +ifneq ($(classpath),avian) +# Assembler, ConstantPool, and Stream are not technically needed for a +# working build, but we include them since our Subroutine test uses +# them to synthesize a class: + classpath-sources := \ + $(classpath-src)/avian/Addendum.java \ + $(classpath-src)/avian/AnnotationInvocationHandler.java \ + $(classpath-src)/avian/Assembler.java \ + $(classpath-src)/avian/Callback.java \ + $(classpath-src)/avian/Cell.java \ + $(classpath-src)/avian/ClassAddendum.java \ + $(classpath-src)/avian/Classes.java \ + $(classpath-src)/avian/Code.java \ + $(classpath-src)/avian/ConstantPool.java \ + $(classpath-src)/avian/Continuations.java \ + $(classpath-src)/avian/FieldAddendum.java \ + $(classpath-src)/avian/Function.java \ + $(classpath-src)/avian/IncompatibleContinuationException.java \ + $(classpath-src)/avian/InnerClassReference.java \ + $(classpath-src)/avian/Machine.java \ + $(classpath-src)/avian/MethodAddendum.java \ + $(classpath-src)/avian/Pair.java \ + $(classpath-src)/avian/Singleton.java \ + $(classpath-src)/avian/Stream.java \ + $(classpath-src)/avian/SystemClassLoader.java \ + $(classpath-src)/avian/Traces.java \ + $(classpath-src)/avian/VMClass.java \ + $(classpath-src)/avian/VMField.java \ + $(classpath-src)/avian/VMMethod.java \ + $(classpath-src)/avian/avianvmresource/Handler.java \ + $(classpath-src)/avian/file/Handler.java \ + $(classpath-src)/java/lang/invoke/MethodHandle.java \ + $(classpath-src)/java/lang/invoke/MethodHandles.java \ + $(classpath-src)/java/lang/invoke/MethodType.java \ + $(classpath-src)/java/lang/invoke/LambdaMetafactory.java \ + $(classpath-src)/java/lang/invoke/LambdaConversionException.java \ + $(classpath-src)/java/lang/invoke/CallSite.java + + ifeq ($(openjdk),) + classpath-sources := $(classpath-sources) \ + $(classpath-src)/dalvik/system/BaseDexClassLoader.java \ + $(classpath-src)/libcore/reflect/AnnotationAccess.java \ + $(classpath-src)/sun/reflect/ConstantPool.java \ + $(classpath-src)/java/net/ProtocolFamily.java \ + $(classpath-src)/java/net/StandardProtocolFamily.java \ + $(classpath-src)/sun/misc/Cleaner.java \ + $(classpath-src)/sun/misc/Unsafe.java \ + $(classpath-src)/java/lang/Object.java \ + $(classpath-src)/java/lang/Class.java \ + $(classpath-src)/java/lang/ClassLoader.java \ + $(classpath-src)/java/lang/Package.java \ + $(classpath-src)/java/lang/reflect/Proxy.java \ + $(classpath-src)/java/lang/reflect/Field.java \ + $(classpath-src)/java/lang/reflect/SignatureParser.java \ + $(classpath-src)/java/lang/reflect/Constructor.java \ + $(classpath-src)/java/lang/reflect/AccessibleObject.java \ + $(classpath-src)/java/lang/reflect/Method.java + endif +else + classpath-sources := $(shell find $(classpath-src) -name '*.java') +endif + +classpath-classes = \ + $(call java-classes,$(classpath-sources),$(classpath-src),$(classpath-build)) +classpath-object = $(build)/classpath-jar.o +classpath-dep = $(classpath-build).dep + +vm-classes = \ + avian/*.class \ + avian/resource/*.class + +test-support-sources = $(shell find $(test)/avian/ -name '*.java') +test-sources := $(wildcard $(test)/*.java) + +# HACK ALERT!! +# This test fails regularly on travis, but nowhere else. We have yet to spend the time to investigate that test, so we disable it on PR builds. +# Note: travis set TRAVIS_PULL_REQUEST environment variable to either the PR number or "false", as appropriate +ifeq (false,$(TRAVIS_PULL_REQUEST)) +else +ifeq (,$(TRAVIS_PULL_REQUEST)) +else + test-sources := $(subst $(test)/Trace.java,,$(test-sources)) +endif +endif + +ifeq (7,$(java-version)) + test-sources := $(subst $(test)/InvokeDynamic.java,,$(test-sources)) + test-sources := $(subst $(test)/Interfaces.java,,$(test-sources)) +endif + +test-cpp-sources = $(wildcard $(test)/*.cpp) +test-sources += $(test-support-sources) +test-support-classes = $(call java-classes, $(test-support-sources),$(test),$(test-build)) +test-classes = $(call java-classes,$(test-sources),$(test),$(test-build)) +test-cpp-objects = $(call cpp-objects,$(test-cpp-sources),$(test),$(test-build)) +test-library = $(build)/$(so-prefix)test$(so-suffix) +test-dep = $(test-build).dep + +test-extra-sources = $(wildcard $(test)/extra/*.java) +test-extra-classes = \ + $(call java-classes,$(test-extra-sources),$(test),$(test-build)) +test-extra-dep = $(test-build)-extra.dep + +unittest-sources = \ + $(wildcard $(unittest)/*.cpp) \ + $(wildcard $(unittest)/util/*.cpp) \ + $(wildcard $(unittest)/codegen/*.cpp) + +unittest-depends = \ + $(wildcard $(unittest)/*.h) + +ifeq ($(continuations),true) + continuation-tests = \ + extra.ComposableContinuations \ + extra.Continuations \ + extra.Coroutines \ + extra.DynamicWind +endif + +ifeq ($(tails),true) + tail-tests = \ + extra.Tails +endif + +ifeq ($(target-arch),i386) + cflags += -DAVIAN_TARGET_ARCH=AVIAN_ARCH_X86 +endif + +ifeq ($(target-arch),x86_64) + cflags += -DAVIAN_TARGET_ARCH=AVIAN_ARCH_X86_64 +endif + +ifeq ($(target-arch),arm) + cflags += -DAVIAN_TARGET_ARCH=AVIAN_ARCH_ARM +endif + +ifeq ($(target-arch),arm64) + cflags += -DAVIAN_TARGET_ARCH=AVIAN_ARCH_ARM64 +endif + +ifeq ($(target-format),elf) + cflags += -DAVIAN_TARGET_FORMAT=AVIAN_FORMAT_ELF +endif + +ifeq ($(target-format),pe) + cflags += -DAVIAN_TARGET_FORMAT=AVIAN_FORMAT_PE +endif + +ifeq ($(target-format),macho) + cflags += -DAVIAN_TARGET_FORMAT=AVIAN_FORMAT_MACHO +endif + +class-name = $(patsubst $(1)/%.class,%,$(2)) +class-names = $(foreach x,$(2),$(call class-name,$(1),$(x))) + +test-flags = -Djava.library.path=$(build) \ + -cp '$(build)/test$(target-path-separator)$(build)/extra-dir' + +test-args = $(test-flags) $(input) + +ifneq ($(filter linux windows macosx,$(platform)),) +eclipse-exec-env = eclipse-ee +eclipse-jdk-dir = $(build)/eclipse/jdk +eclipse-ee-file = $(eclipse-jdk-dir)/avian.ee +eclipse-bin-dir = $(eclipse-jdk-dir)/bin +eclipse-lib-dir = $(eclipse-jdk-dir)/jre/lib +eclipse-src-dir = $(eclipse-jdk-dir)/src +define eclipse-ee-descriptor +# An Eclipse execution environment for the Avian JVM\ +\n-Dee.executable=bin/java${exe-suffix}\ +\n-Dee.bootclasspath=jre/lib/rt.jar\ +\n-Dee.language.level=1.7\ +\n-Dee.name=$(name)-$(version)-$(platform)-$(arch)$(options)\ +\n-Dee.src=src\ +\n-Dee.javadoc=file://$${ee.home}/doc\ +\n-Djava.home=$${ee.home}\n +endef +else +eclipse-exec-env = +endif + +.PHONY: build +ifneq ($(supports_avian_executable),false) +build: $(static-library) $(executable) $(dynamic-library) $(lzma-library) \ + $(lzma-encoder) $(executable-dynamic) $(classpath-dep) $(test-dep) \ + $(test-extra-dep) $(embed) $(build)/classpath.jar $(eclipse-exec-env) +else +build: $(static-library) $(dynamic-library) $(lzma-library) \ + $(lzma-encoder) $(classpath-dep) $(test-dep) \ + $(test-extra-dep) $(embed) $(build)/classpath.jar +endif + +$(test-dep): $(classpath-dep) + +$(test-extra-dep): $(classpath-dep) + +.PHONY: run +run: build + $(library-path) $(test-executable) $(test-args) + +.PHONY: debug +debug: build + $(library-path) $(db) $(test-executable) $(test-args) + +.PHONY: vg +vg: build + $(library-path) $(vg) $(test-executable) $(test-args) + +.PHONY: test +test: build-test run-test + +.PHONY: build-test +build-test: build $(build)/run-tests.sh $(build)/test.sh $(unittest-executable) + +.PHONY: run-test +run-test: +ifneq ($(remote-test),true) + /bin/sh $(build)/run-tests.sh +else + @echo "running tests on $(remote-test-user)@$(remote-test-host):$(remote-test-port), in $(remote-test-dir)" + rsync $(build) -rav --exclude '*.o' --rsh="ssh -p$(remote-test-port)" $(remote-test-user)@$(remote-test-host):$(remote-test-dir) + ssh -p$(remote-test-port) $(remote-test-user)@$(remote-test-host) sh "$(remote-test-dir)/$(platform)-$(arch)$(options)/run-tests.sh" +endif + +.PHONY: jdk-test +jdk-test: $(test-dep) $(build)/classpath.jar $(build)/jdk-run-tests.sh $(build)/test.sh + /bin/sh $(build)/jdk-run-tests.sh + +.PHONY: tarball +tarball: + @echo "creating build/avian-$(version).tar.bz2" + @mkdir -p build + (cd .. && tar --exclude=build --exclude=cmake-build --exclude=distrib \ + --exclude=lib --exclude='.*' --exclude='*~' \ + -cjf avian/build/avian-$(version).tar.bz2 avian) + +.PHONY: clean-current +clean-current: + @echo "removing $(build)" + rm -rf $(build) + +.PHONY: clean +clean: + @echo "removing build directories" + rm -rf build cmake-build distrib lib + +.PHONY: eclipse-ee +ifneq ($(strip $(eclipse-exec-env)),) +eclipse-ee: $(eclipse-ee-file) $(eclipse-lib-dir)/rt.jar $(eclipse-bin-dir)/java${exe-suffix} $(eclipse-src-dir) + +$(eclipse-bin-dir): + @mkdir -p $(@) + +$(eclipse-lib-dir): + @mkdir -p $(@) + +$(eclipse-jdk-dir): + @mkdir -p $(@) + +$(eclipse-ee-file): $(eclipse-jdk-dir) + @echo "writing eclipse execution environment descriptor to $(@)" + @printf '${eclipse-ee-descriptor}' > $(@) + +$(eclipse-src-dir): $(eclipse-jdk-dir) + @echo "symlinking classpath for $(@)" + @ln -sf ../../../../classpath $(@) + +$(eclipse-bin-dir)/java$(exe-suffix): $(eclipse-bin-dir) $(executable) + @echo "symlinking $(executable) for $(@)" + @ln -sf ../../../$(name)${exe-suffix} $(@) + +$(eclipse-lib-dir)/rt.jar: $(eclipse-lib-dir) $(build)/classpath.jar + @echo "symlinking $(build)/classpath.jar for $(@)" + @ln -sf ../../../../classpath.jar $(@) +else +eclipse-ee: + $(error "Eclipse execution environment for platform '$(platform)' is not supported") +endif + +ifeq ($(continuations),true) +$(build)/compile-x86-asm.o: $(src)/continuations-x86.$(asm-format) +endif + +$(build)/run-tests.sh: $(test-classes) makefile $(build)/extra-dir/multi-classpath-test.txt $(build)/test/multi-classpath-test.txt + echo 'cd $$(dirname $$0)' > $(@) + echo "sh ./test.sh 2>/dev/null \\" >> $(@) + echo "$(shell echo $(library-path) | sed 's|$(build)|\.|g') ./$(name)-unittest${exe-suffix} ./$(notdir $(test-executable)) $(mode) \"-Djava.library.path=. -cp test$(target-path-separator)extra-dir\" \\" >> $(@) + echo "$(call class-names,$(test-build),$(filter-out $(test-support-classes), $(test-classes))) \\" >> $(@) + echo "$(continuation-tests) $(tail-tests)" >> $(@) + +$(build)/jdk-run-tests.sh: $(test-classes) makefile $(build)/extra-dir/multi-classpath-test.txt $(build)/test/multi-classpath-test.txt + echo 'cd $$(dirname $$0)' > $(@) + echo "sh ./test.sh 2>/dev/null \\" >> $(@) + echo "'' true $(JAVA_HOME)/bin/java $(mode) \"-Xmx128m -Djava.library.path=. -cp test$(path-separator)extra-dir$(path-separator)classpath\" \\" >> $(@) + echo "$(call class-names,$(test-build),$(filter-out $(test-support-classes), $(test-classes))) \\" >> $(@) + echo "$(continuation-tests) $(tail-tests)" >> $(@) + +$(build)/extra-dir/multi-classpath-test.txt: + mkdir -p $(build)/extra-dir + echo "$@" > $@ + +$(build)/test/multi-classpath-test.txt: + echo "$@" > $@ + +$(build)/test.sh: $(test)/test.sh + cp $(<) $(@) + +gen-arg = $(shell echo $(1) | sed -e 's:$(build)/type-\(.*\)\.cpp:\1:') +$(generated-code): %.cpp: $(src)/types.def $(generator) $(classpath-dep) + @echo "generating $(@)" + @mkdir -p $(dir $(@)) + $(generator) -cp $(boot-classpath) -i $(<) -o $(@) -t $(call gen-arg,$(@)) + +$(classpath-dep): $(classpath-sources) $(classpath-jar-dep) + @echo "compiling classpath classes" + @mkdir -p $(classpath-build) + $(javac) -source 1.$(java-version) -target 1.$(java-version) \ + -d $(classpath-build) -bootclasspath $(boot-classpath) \ + $(classpath-sources) + @touch $(@) + +$(build)/android-src/%.cpp: $(luni-native)/%.cpp + cp $(<) $(@) + +$(build)/android-src/%.cpp: $(libnativehelper-native)/%.cpp + cp $(<) $(@) + +$(build)/android-src/%.cpp: $(crypto-native)/%.cpp + cp $(<) $(@) + +$(build)/android-src/%.cpp: $(libziparchive-native)/%.cc + cp $(<) $(@) + +$(build)/android-src/%.cpp: $(libutils-native)/%.cpp + cp $(<) $(@) + +$(build)/%.o: $(build)/android-src/%.cpp $(build)/android.dep + @echo "compiling $(@)" + @mkdir -p $(dir $(@)) + $(cxx) $(android-cflags) $(classpath-extra-cflags) -c \ + $$($(windows-path) $(<)) $(call output,$(@)) + +$(build)/android.dep: $(luni-javas) $(dalvik-javas) $(libart-javas) \ + $(xml-javas) $(okhttp-android-javas) $(okhttp-javas) $(okio-javas) \ + $(bcpkix-javas) $(bcprov-javas) $(luni-nonjavas) $(crypto-javas) $(crypto-platform-javas) + @echo "compiling luni classes" + @mkdir -p $(classpath-build) + @mkdir -p $(build)/android + @mkdir -p $(build)/android-src/external/fdlibm + @mkdir -p $(build)/android-src/libexpat + cp $(android)/external/fdlibm/fdlibm.h $(build)/android-src/external/fdlibm/ + cp $(android)/external/expat/lib/expat*.h $(build)/android-src/libexpat/ + cp -a $(luni-java)/* $(xml-java)/* $(okhttp-android-java)/* $(okhttp-java)/* $(okio-java)/* $(bcpkix-java)/* $(bcprov-java)/* $(build)/android-src/ + rm $(call noop-files,$(luni-blacklist),$(luni-java),$(build)/android-src) + (cd $(dalvik-java) && \ + $(jar) c $(call noop-files,$(dalvik-javas),$(dalvik-java),.)) \ + | (cd $(build)/android-src && $(jar) x) + (cd $(libart-java) && \ + $(jar) c $(call noop-files,$(libart-javas),$(libart-java),.)) \ + | (cd $(build)/android-src && $(jar) x) + (cd $(crypto-java) && \ + $(jar) c $(call noop-files,$(crypto-javas),$(crypto-java),.)) \ + | (cd $(build)/android-src && $(jar) x) + (cd $(crypto-platform-java) && \ + $(jar) c $(call noop-files,$(crypto-platform-javas),$(crypto-platform-java),.)) \ + | (cd $(build)/android-src && $(jar) x) + (cd $(classpath-src) && \ + $(jar) c $(call noop-files,$(classpath-sources),$(classpath-src),.)) \ + | (cd $(build)/android-src && $(jar) x) +# (cd android && $(jar) c *) | (cd $(build)/android-src && $(jar) x) + find $(build)/android-src -name '*.java' > $(build)/android.txt + $(javac) -Xmaxerrs 1000 -d $(build)/android @$(build)/android.txt + rm $(build)/android/sun/misc/Unsafe* \ + $(build)/android/java/lang/reflect/Proxy* + for x in $(luni-copied-nonjavas); \ + do cp $(luni-java)$${x} $(build)/android$${x} ; \ + done + # fix security.properties - get rid of "com.android" in front of classes starting with "org" + sed -i -e 's/\(.*=\)com\.android\.\(org\..*\)/\1\2/g' \ + $(build)/android/java/security/security.properties + chmod +w $(build)/android/java/security/security.properties + cp -r $(build)/android/* $(classpath-build) + @touch $(@) + +$(test-build)/%.class: $(test)/%.java + @echo $(<) + +$(test-dep): $(test-sources) $(test-library) + @echo "compiling test classes" + @mkdir -p $(test-build) + files="$(shell $(MAKE) -s --no-print-directory build=$(build) $(test-classes))"; \ + if test -n "$${files}"; then \ + $(javac) -source 1.$(java-version) -target 1.$(java-version) \ + -classpath $(test-build) -d $(test-build) -bootclasspath $(boot-classpath) $${files}; \ + fi + $(javac) -source 1.2 -target 1.1 -XDjsrlimit=0 -d $(test-build) \ + -bootclasspath $(boot-classpath) test/Subroutine.java + @touch $(@) + +$(test-extra-dep): $(test-extra-sources) + @echo "compiling extra test classes" + @mkdir -p $(test-build) + files="$(shell $(MAKE) -s --no-print-directory build=$(build) $(test-extra-classes))"; \ + if test -n "$${files}"; then \ + $(javac) -source 1.$(java-version) -target 1.$(java-version) \ + -d $(test-build) -bootclasspath $(boot-classpath) $${files}; \ + fi + @touch $(@) + +define compile-object + @echo "compiling $(@)" + @mkdir -p $(dir $(@)) + $(cxx) $(cflags) -c $$($(windows-path) $(<)) $(call output,$(@)) +endef + +define compile-asm-object + @echo "compiling $(@)" + @mkdir -p $(dir $(@)) + $(as) $(asmflags) $(call asm-output,$(@)) $(call asm-input,$(<)) +endef + +define compile-unittest-object + @echo "compiling $(@)" + @mkdir -p $(dir $(@)) + $(cxx) $(cflags) -c $$($(windows-path) $(<)) -I$(unittest) $(call output,$(@)) +endef + +$(vm-cpp-objects): $(build)/%.o: $(src)/%.cpp $(vm-depends) + $(compile-object) + +ifeq ($(process),interpret) +$(all-codegen-target-objects): $(build)/%.o: $(src)/%.cpp $(vm-depends) + $(compile-object) +endif + +$(unittest-objects): $(build)/unittest/%.o: $(unittest)/%.cpp $(vm-depends) $(unittest-depends) + $(compile-unittest-object) + +$(test-cpp-objects): $(test-build)/%.o: $(test)/%.cpp $(vm-depends) + $(compile-object) + +$(test-library): $(test-cpp-objects) + @echo "linking $(@)" +ifdef ms_cl_compiler + $(ld) $(shared) $(lflags) $(^) -out:$(@) \ + -debug -PDB:$(subst $(so-suffix),.pdb,$(@)) \ + -IMPLIB:$(test-build)/$(name).lib $(manifest-flags) +ifdef mt + $(mt) -nologo -manifest $(@).manifest -outputresource:"$(@);2" +endif +else + $(ld) $(^) $(shared) $(lflags) -o $(@) +endif + +ifdef embed +$(embed): $(embed-objects) $(embed-loader-o) + @echo "building $(embed)" +ifdef ms_cl_compiler + $(ld) $(lflags) $(^) -out:$(@) \ + -debug -PDB:$(subst $(exe-suffix),.pdb,$(@)) $(manifest-flags) +ifdef mt + $(mt) -nologo -manifest $(@).manifest -outputresource:"$(@);1" +endif +else + $(cxx) $(^) $(lflags) $(static) $(call output,$(@)) +endif + +$(build-embed)/%.o: $(src)/%.cpp + @echo "compiling $(@)" + @mkdir -p $(dir $(@)) + $(cxx) $(cflags) -c $(<) $(call output,$(@)) + +$(embed-loader-o): $(embed-loader) $(converter) + @mkdir -p $(dir $(@)) + $(converter) $(<) $(@) _binary_loader_start \ + _binary_loader_end $(target-format) $(arch) + +$(embed-loader): $(embed-loader-objects) $(vm-objects) $(classpath-objects) \ + $(heapwalk-objects) $(lzma-decode-objects) +ifdef ms_cl_compiler + $(ld) $(lflags) $(^) -out:$(@) \ + -debug -PDB:$(subst $(exe-suffix),.pdb,$(@)) $(manifest-flags) +ifdef mt + $(mt) -nologo -manifest $(@).manifest -outputresource:"$(@);1" +endif +else + $(dlltool) -z $(addsuffix .def,$(basename $(@))) $(^) + $(dlltool) -d $(addsuffix .def,$(basename $(@))) -e $(addsuffix .exp,$(basename $(@))) + $(ld) $(addsuffix .exp,$(basename $(@))) $(^) \ + $(lflags) $(classpath-lflags) $(bootimage-lflags) -o $(@) +endif + $(strip) $(strip-all) $(@) + +$(build-embed-loader)/%.o: $(src)/%.cpp + @echo "compiling $(@)" + @mkdir -p $(dir $(@)) + $(cxx) $(cflags) -c $(<) $(call output,$(@)) +endif + +$(build)/%.o: $(lzma)/C/%.c + @echo "compiling $(@)" + @mkdir -p $(dir $(@)) + $(cc) $(lzma-cflags) -c $$($(windows-path) $(<)) $(call output,$(@)) + +$(vm-asm-objects): $(build)/%-asm.o: $(src)/%.$(asm-format) + $(compile-asm-object) + +$(bootimage-generator-objects): $(build)/%.o: $(src)/%.cpp $(vm-depends) + $(compile-object) + +$(heapwalk-objects): $(build)/%.o: $(src)/%.cpp $(vm-depends) + $(compile-object) + +$(driver-object): $(driver-source) + $(compile-object) + +$(build)/main-dynamic.o: $(driver-source) + @echo "compiling $(@)" + @mkdir -p $(dir $(@)) + $(cxx) $(cflags) -DBOOT_LIBRARY=\"$(so-prefix)jvm$(so-suffix)\" \ + -c $(<) $(call output,$(@)) + +$(boot-object): $(boot-source) + $(compile-object) + +$(boot-javahome-object): $(src)/boot-javahome.cpp + $(compile-object) + +$(object-writer-objects) $(binary-to-object-objects): $(build)/%.o: $(src)/%.cpp $(binary-to-object-depends) $(object-writer-depends) $(all-depends) + @mkdir -p $(dir $(@)) + $(build-cxx) $(converter-cflags) -c $(<) -o $(@) + +$(converter): $(converter-objects) $(converter-tool-objects) + @mkdir -p $(dir $(@)) + $(build-cc) $(^) -g -o $(@) + +$(lzma-encoder-objects): $(build)/lzma/%.o: $(src)/lzma/%.cpp + @mkdir -p $(dir $(@)) + $(build-cxx) $(lzma-build-cflags) -c $(<) -o $(@) + +$(lzma-encoder): $(lzma-encoder-objects) $(lzma-encoder-lzma-objects) + $(build-cc) $(^) -g -o $(@) + +$(lzma-library): $(lzma-loader) $(lzma-decode-objects) + @echo "creating $(@)" + @rm -rf $(build)/libavian-lzma + @mkdir -p $(build)/libavian-lzma + rm -rf $(@) + for x in $(^); \ + do cp $${x} $(build)/libavian-lzma/$$(echo $${x} | sed s:/:_:g); \ + done +ifdef ms_cl_compiler + $(ar) $(arflags) $(build)/libavian-lzma/*.o -out:$(@) +else + $(ar) cru $(@) $(build)/libavian-lzma/*.o + $(ranlib) $(@) +endif + +$(lzma-loader): $(src)/lzma/load.cpp + $(compile-object) + +$(build)/classpath.jar: $(classpath-dep) $(classpath-jar-dep) + @echo "creating $(@)" + (wd=$$(pwd) && \ + cd $(classpath-build) && \ + $(jar) c0f "$$($(native-path) "$${wd}/$(@)")" .) + +$(classpath-object): $(build)/classpath.jar $(converter) + @echo "creating $(@)" + $(converter) $(<) $(@) _binary_classpath_jar_start \ + _binary_classpath_jar_end $(target-format) $(arch) + +$(build)/javahome.jar: + @echo "creating $(@)" + (wd=$$(pwd) && \ + cd "$(build-javahome)" && \ + $(jar) c0f "$$($(native-path) "$${wd}/$(@)")" $(javahome-files)) + +$(javahome-object): $(build)/javahome.jar $(converter) + @echo "creating $(@)" + $(converter) $(<) $(@) _binary_javahome_jar_start \ + _binary_javahome_jar_end $(target-format) $(arch) + +define compile-generator-object + @echo "compiling $(@)" + @mkdir -p $(dir $(@)) + $(build-cxx) -DPOINTER_SIZE=$(pointer-size) -O0 -g3 $(build-cflags) \ + -c $(<) -o $(@) +endef + +$(generator-objects): $(generator-depends) +$(generator-objects): $(build)/%-build.o: $(src)/%.cpp + $(compile-generator-object) + +$(build)/%-build.o: $(lzma)/C/%.c + @echo "compiling $(@)" + @mkdir -p $(dir $(@)) + $(build-cc) -DPOINTER_SIZE=$(pointer-size) -O0 -g3 $(lzma-build-cflags) \ + -c $(<) -o $(@) + +$(jni-objects): $(build)/%.o: $(classpath-src)/%.cpp $(vm-depends) + $(compile-object) + +$(static-library): $(vm-objects) $(classpath-objects) $(vm-heapwalk-objects) \ + $(javahome-object) $(boot-javahome-object) $(lzma-decode-objects) + @echo "creating $(@)" + @rm -rf $(build)/libavian + @mkdir -p $(build)/libavian + rm -rf $(@) + for x in $(^); \ + do cp $${x} $(build)/libavian/$$(echo $${x} | sed s:/:_:g); \ + done +ifdef ms_cl_compiler + $(ar) $(arflags) $(build)/libavian/*.o -out:$(@) +else + $(ar) cru $(@) $(build)/libavian/*.o + $(ranlib) $(@) +endif + +$(bootimage-object) $(codeimage-object): $(bootimage-generator) \ + $(classpath-jar-dep) $(test-dep) + @echo "generating bootimage and codeimage binaries from $(classpath-build) using $(<)" + $(<) -cp $(bootimage-classpath) -bootimage $(bootimage-object) -codeimage $(codeimage-object) \ + -bootimage-symbols $(bootimage-symbols) \ + -codeimage-symbols $(codeimage-symbols) \ + -hostvm $(host-vm) + +executable-objects = $(vm-objects) $(classpath-objects) $(driver-object) \ + $(vm-heapwalk-objects) $(boot-object) $(vm-classpath-objects) \ + $(javahome-object) $(boot-javahome-object) $(lzma-decode-objects) + +unittest-executable-objects = $(unittest-objects) $(vm-objects) \ + $(vm-heapwalk-objects) $(build)/util/arg-parser.o $(stub-objects) \ + $(lzma-decode-objects) + +ifeq ($(process),interpret) + unittest-executable-objects += $(all-codegen-target-objects) +endif + +# apparently, make does poorly with ifs inside of defines, and indented defines. +# I suggest re-indenting the following before making edits (and unindenting afterwards): +ifneq ($(platform),windows) +define link-executable + @echo linking $(@) + $(ld) $(^) $(rdynamic) $(lflags) $(classpath-lflags) $(bootimage-lflags) \ + -o $(@) +endef +else +ifdef ms_cl_compiler +ifdef mt +define link-executable + @echo linking $(@) + $(ld) $(lflags) $(^) -out:$(@) \ + -debug -PDB:$(subst $(exe-suffix),.pdb,$(@)) $(manifest-flags) + $(mt) -nologo -manifest $(@).manifest -outputresource:"$(@);1" +endef +else +define link-executable + @echo linking $(@) + $(mt) -nologo -manifest $(@).manifest -outputresource:"$(@);1" +endef +endif +else +define link-executable + @echo linking $(@) + $(dlltool) -z $(@).def $(^) + $(dlltool) -d $(@).def -e $(@).exp + $(ld) $(@).exp $(^) $(lflags) $(classpath-lflags) -o $(@) +endef +endif +endif + +$(executable): $(executable-objects) + $(link-executable) + +$(unittest-executable): $(unittest-executable-objects) + $(link-executable) + +$(bootimage-generator): $(bootimage-generator-objects) $(vm-objects) + echo building $(bootimage-generator) arch=$(build-arch) platform=$(bootimage-platform) + $(MAKE) process=interpret \ + bootimage= \ + bootimage-test= \ + mode=$(mode) \ + platform=$(bootimage-platform) \ + arch=$(build-arch) + $(MAKE) mode=$(mode) \ + build=$(host-build-root) \ + arch=$(build-arch) \ + aot-only=false \ + target-arch=$(arch) \ + armv6=$(armv6) \ + platform=$(bootimage-platform) \ + target-format=$(target-format) \ + android=$(android) \ + openjdk=$(openjdk) \ + openjdk-src=$(openjdk-src) \ + bootimage-generator= \ + build-bootimage-generator=$(bootimage-generator) \ + target-cflags="$(bootimage-cflags)" \ + target-asm=$(asm) \ + $(bootimage-generator) + +$(build-bootimage-generator): \ + $(vm-objects) $(classpath-object) \ + $(heapwalk-objects) $(bootimage-generator-objects) $(converter-objects) \ + $(lzma-decode-objects) $(lzma-encode-objects) + @echo "linking $(@)" +ifeq ($(platform),windows) +ifdef ms_cl_compiler + $(ld) $(bootimage-generator-lflags) $(lflags) $(^) -out:$(@) \ + -debug -PDB:$(subst $(exe-suffix),.pdb,$(@)) $(manifest-flags) +ifdef mt + $(mt) -nologo -manifest $(@).manifest -outputresource:"$(@);1" +endif +else + $(dlltool) -z $(@).def $(^) + $(dlltool) -d $(@).def -e $(@).exp + $(ld) $(@).exp $(^) $(bootimage-generator-lflags) $(lflags) -o $(@) +endif +else + $(ld) $(^) $(rdynamic) $(bootimage-generator-lflags) $(lflags) -o $(@) +endif + +$(dynamic-library): $(vm-objects) $(dynamic-object) $(classpath-objects) \ + $(vm-heapwalk-objects) $(boot-object) $(vm-classpath-objects) \ + $(classpath-libraries) $(javahome-object) $(boot-javahome-object) \ + $(lzma-decode-objects) + @echo "linking $(@)" +ifdef ms_cl_compiler + $(ld) $(shared) $(lflags) $(^) -out:$(@) \ + -debug -PDB:$(subst $(so-suffix),.pdb,$(@)) \ + -IMPLIB:$(subst $(so-suffix),.lib,$(@)) $(manifest-flags) +ifdef mt + $(mt) -nologo -manifest $(@).manifest -outputresource:"$(@);2" +endif +else + $(ld) $(^) $(version-script-flag) $(soname-flag) \ + $(shared) $(lflags) $(classpath-lflags) $(bootimage-lflags) \ + -o $(@) +endif + $(strip) $(strip-all) $(@) + +# todo: the $(no-lto) flag below is due to odd undefined reference errors on +# Ubuntu 11.10 which may be fixable without disabling LTO. +$(executable-dynamic): $(driver-dynamic-objects) $(dynamic-library) + @echo "linking $(@)" +ifdef ms_cl_compiler + $(ld) $(lflags) -LIBPATH:$(build) -DEFAULTLIB:$(name) \ + -debug -PDB:$(subst $(exe-suffix),.pdb,$(@)) \ + $(driver-dynamic-objects) -out:$(@) $(manifest-flags) +ifdef mt + $(mt) -nologo -manifest $(@).manifest -outputresource:"$(@);1" +endif +else + $(ld) $(driver-dynamic-objects) -L$(build) -ljvm $(lflags) $(no-lto) $(rpath) -o $(@) +endif + $(strip) $(strip-all) $(@) + +$(generator): $(generator-objects) $(generator-lzma-objects) + @echo "linking $(@)" + $(build-ld-cpp) $(^) $(build-lflags) $(static-on-windows) -o $(@) + +$(openjdk-objects): $(build)/openjdk/%-openjdk.o: $(openjdk-src)/%.c \ + $(openjdk-headers-dep) + @echo "compiling $(@)" + @mkdir -p $(dir $(@)) + sed 's/^static jclass ia_class;//' < $(<) > $(build)/openjdk/$(notdir $(<)) +ifeq ($(platform),ios) + sed \ + -e 's/^#ifndef __APPLE__/#if 1/' \ + -e 's/^#ifdef __APPLE__/#if 0/' \ + < "$(openjdk-src)/solaris/native/java/lang/ProcessEnvironment_md.c" \ + > $(build)/openjdk/ProcessEnvironment_md.c + sed \ + -e 's/^#ifndef __APPLE__/#if 1/' \ + -e 's/^#ifdef __APPLE__/#if 0/' \ + < "$(openjdk-src)/solaris/native/java/lang/UNIXProcess_md.c" \ + > $(build)/openjdk/UNIXProcess_md.c + if [ -e "$(openjdk-src)/solaris/native/java/lang/childproc.h" ]; then \ + sed \ + -e 's/^#ifndef __APPLE__/#if 1/' \ + -e 's/^#ifdef __APPLE__/#if 0/' \ + < "$(openjdk-src)/solaris/native/java/lang/childproc.h" \ + > $(build)/openjdk/childproc.h; \ + fi +endif +ifneq (7,$(openjdk-version)) + if [ -f openjdk-patches/$(notdir $(<)).8.patch ]; then \ + ( cd $(build) && patch -p0 ) < openjdk-patches/$(notdir $(<)).8.patch; \ + fi + if [ -f openjdk-patches/$(notdir $(<)).8.$(platform).patch ]; then \ + ( cd $(build) && patch -p0 ) < openjdk-patches/$(notdir $(<)).8.$(platform).patch; \ + fi +endif + if [ -f openjdk-patches/$(notdir $(<)).patch ]; then \ + ( cd $(build) && patch -p0 ) < openjdk-patches/$(notdir $(<)).patch; \ + fi + $(cc) -fPIC $(openjdk-extra-cflags) $(openjdk-cflags) \ + $(optimization-cflags) -w -c $(build)/openjdk/$(notdir $(<)) \ + $(call output,$(@)) -Wno-return-type + +$(openjdk-local-objects): $(build)/openjdk/%-openjdk.o: $(src)/openjdk/%.c \ + $(openjdk-headers-dep) + @echo "compiling $(@)" + @mkdir -p $(dir $(@)) + $(cc) -fPIC $(openjdk-extra-cflags) $(openjdk-cflags) \ + $(optimization-cflags) -w -c $(<) $(call output,$(@)) + +$(openjdk-headers-dep): + @echo "generating openjdk headers" + @mkdir -p $(dir $(@)) + $(javah) -d $(build)/openjdk -bootclasspath $(boot-classpath) \ + $(openjdk-headers-classes) +ifeq ($(platform),windows) + sed 's/^#ifdef _WIN64/#if 1/' \ + < "$(openjdk-src)/windows/native/java/net/net_util_md.h" \ + > $(build)/openjdk/net_util_md.h + sed \ + -e 's/\(^#include "net_util.h"\)/\1\n#if (defined _INC_NLDEF) || (defined _WS2DEF_)\n#define HIDE(x) hide_##x\n#else\n#define HIDE(x) x\n#define _WINSOCK2API_\n#endif/' \ + -e 's/\(IpPrefix[a-zA-Z_]*\)/HIDE(\1)/' \ + -e 's/\(IpSuffix[a-zA-Z_]*\)/HIDE(\1)/' \ + -e 's/\(IpDad[a-zA-Z_]*\)/HIDE(\1)/' \ + -e 's/\(ScopeLevel[a-zA-Z_]*\)/HIDE(\1)/' \ + -e 's/\(SCOPE_LEVEL[a-zA-Z_]*\)/HIDE(\1)/' \ + < "$(openjdk-src)/windows/native/java/net/NetworkInterface.h" \ + > $(build)/openjdk/NetworkInterface.h + echo 'static int getAddrsFromAdapter(IP_ADAPTER_ADDRESSES *ptr, netaddr **netaddrPP);' >> $(build)/openjdk/NetworkInterface.h +endif + +ifeq ($(kernel),darwin) + mkdir -p $(build)/openjdk/netinet + for file in \ + $(header-sysroot)/usr/include/netinet/ip.h \ + $(header-sysroot)/usr/include/netinet/in_systm.h \ + $(header-sysroot)/usr/include/netinet/ip_icmp.h \ + $(header-sysroot)/usr/include/netinet/in_var.h \ + $(header-sysroot)/usr/include/netinet/icmp6.h \ + $(header-sysroot)/usr/include/netinet/ip_var.h; do \ + if [ ! -f "$(build)/openjdk/netinet/$$(basename $${file})" ]; then \ + ln "$${file}" "$(build)/openjdk/netinet/$$(basename $${file})"; \ + fi; \ + done + mkdir -p $(build)/openjdk/netinet6 + for file in \ + $(header-sysroot)/usr/include/netinet6/in6_var.h; do \ + if [ ! -f "$(build)/openjdk/netinet6/$$(basename $${file})" ]; then \ + ln "$${file}" "$(build)/openjdk/netinet6/$$(basename $${file})"; \ + fi; \ + done + mkdir -p $(build)/openjdk/net + for file in \ + $(header-sysroot)/usr/include/net/if_arp.h; do \ + if [ ! -f "$(build)/openjdk/net/$$(basename $${file})" ]; then \ + ln "$${file}" "$(build)/openjdk/net/$$(basename $${file})"; \ + fi; \ + done + mkdir -p $(build)/openjdk/sys + for file in \ + $(header-sysroot)/usr/include/sys/kern_event.h \ + $(header-sysroot)/usr/include/sys/sys_domain.h; do \ + if [ ! -f "$(build)/openjdk/sys/$$(basename $${file})" ]; then \ + ln "$${file}" "$(build)/openjdk/sys/$$(basename $${file})"; \ + fi; \ + done +endif + @touch $(@) + +$(openjdk-jar-dep): + @echo "extracting openjdk classes" + @mkdir -p $(dir $(@)) + @mkdir -p $(classpath-build) + (cd $(classpath-build) && \ + $(jar) xf "$$($(native-path) "$(openjdk)/jre/lib/rt.jar")" && \ + $(jar) xf "$$($(native-path) "$(openjdk)/jre/lib/jsse.jar")" && \ + $(jar) xf "$$($(native-path) "$(openjdk)/jre/lib/jce.jar")" && \ + $(jar) xf "$$($(native-path) "$(openjdk)/jre/lib/charsets.jar")" && \ + $(jar) xf "$$($(native-path) "$(openjdk)/jre/lib/ext/sunjce_provider.jar")" && \ + $(jar) xf "$$($(native-path) "$(openjdk)/jre/lib/resources.jar")") + @touch $(@) diff --git a/sgx-jvm/avian/openjdk-patches/Inet4AddressImpl.c.8.patch b/sgx-jvm/avian/openjdk-patches/Inet4AddressImpl.c.8.patch new file mode 100644 index 0000000000..56e99af3bc --- /dev/null +++ b/sgx-jvm/avian/openjdk-patches/Inet4AddressImpl.c.8.patch @@ -0,0 +1,24 @@ +--- openjdk/Inet4AddressImpl.c ++++ openjdk/Inet4AddressImpl.c +@@ -461,6 +461,21 @@ + return JNI_FALSE; + } + ++#ifdef WIN32 ++DWORD WINAPI IcmpSendEcho2Ex(HANDLE, ++ HANDLE, ++ LPVOID, ++ PVOID, ++ IPAddr, ++ IPAddr, ++ LPVOID, ++ WORD, ++ LPVOID, ++ LPVOID, ++ DWORD, ++ DWORD); ++#endif ++ + /** + * ping implementation. + * Send a ICMP_ECHO_REQUEST packet every second until either the timeout diff --git a/sgx-jvm/avian/openjdk-patches/Inet6AddressImpl.c.8.patch b/sgx-jvm/avian/openjdk-patches/Inet6AddressImpl.c.8.patch new file mode 100644 index 0000000000..d2994ea21f --- /dev/null +++ b/sgx-jvm/avian/openjdk-patches/Inet6AddressImpl.c.8.patch @@ -0,0 +1,17 @@ +--- openjdk/Inet6AddressImpl.c ++++ openjdk/Inet6AddressImpl.c +@@ -360,6 +360,13 @@ + + #ifdef AF_INET6 + ++#ifdef __MINGW32__ ++typedef struct icmpv6_echo_reply_lh { ++ IPV6_ADDRESS_EX Address; ++ ULONG Status; ++ unsigned int RoundTripTime; ++} ICMPV6_ECHO_REPLY, *PICMPV6_ECHO_REPLY; ++#endif + + /** + * ping implementation. + diff --git a/sgx-jvm/avian/openjdk-patches/java_props_macosx.c.patch b/sgx-jvm/avian/openjdk-patches/java_props_macosx.c.patch new file mode 100644 index 0000000000..42622f6fda --- /dev/null +++ b/sgx-jvm/avian/openjdk-patches/java_props_macosx.c.patch @@ -0,0 +1,15 @@ +--- openjdk/java_props_macosx.c ++++ openjdk/java_props_macosx.c +@@ -37,11 +37,7 @@ + + // need dlopen/dlsym trick to avoid pulling in JavaRuntimeSupport before libjava.dylib is loaded + static void *getJRSFramework() { +- static void *jrsFwk = NULL; +- if (jrsFwk == NULL) { +- jrsFwk = dlopen("/System/Library/Frameworks/JavaVM.framework/Frameworks/JavaRuntimeSupport.framework/JavaRuntimeSupport", RTLD_LAZY | RTLD_LOCAL); +- } +- return jrsFwk; ++ return NULL; + } + + static char *getPosixLocale(int cat) { diff --git a/sgx-jvm/avian/openjdk-patches/java_props_md.c.8.windows.patch b/sgx-jvm/avian/openjdk-patches/java_props_md.c.8.windows.patch new file mode 100644 index 0000000000..0211421383 --- /dev/null +++ b/sgx-jvm/avian/openjdk-patches/java_props_md.c.8.windows.patch @@ -0,0 +1,24 @@ +--- openjdk/java_props_md.c ++++ openjdk/java_props_md.c +@@ -212,17 +212,17 @@ + * SHELL32 DLL is delay load DLL and we can use the trick with + * __try/__except block. + */ +- __try { ++ /* __try { */ + /* + * For Windows Vista and later (or patched MS OS) we need to use + * [SHGetKnownFolderPath] call to avoid MAX_PATH length limitation. + * Shell32.dll (version 6.0.6000 or later) + */ + hr = SHGetKnownFolderPath(&FOLDERID_Profile, KF_FLAG_DONT_VERIFY, NULL, &u_path); +- } __except(EXCEPTION_EXECUTE_HANDLER) { ++ /* } __except(EXCEPTION_EXECUTE_HANDLER) { */ + /* Exception: no [SHGetKnownFolderPath] entry */ +- hr = E_FAIL; +- } ++ /* hr = E_FAIL; */ ++ /* } */ + + if (FAILED(hr)) { + WCHAR path[MAX_PATH+1]; diff --git a/sgx-jvm/avian/openjdk-src.mk b/sgx-jvm/avian/openjdk-src.mk new file mode 100644 index 0000000000..f4ec22b9d0 --- /dev/null +++ b/sgx-jvm/avian/openjdk-src.mk @@ -0,0 +1,386 @@ +openjdk-sources = \ + $(openjdk-src)/share/native/common/check_code.c \ + $(openjdk-src)/share/native/common/check_format.c \ + $(openjdk-src)/share/native/common/check_version.c \ + $(openjdk-src)/share/native/common/jdk_util.c \ + $(openjdk-src)/share/native/common/jio.c \ + $(openjdk-src)/share/native/common/jni_util.c \ + $(openjdk-src)/share/native/common/verify_stub.c \ + $(openjdk-src)/share/native/java/io/FileInputStream.c \ + $(openjdk-src)/share/native/java/io/io_util.c \ + $(openjdk-src)/share/native/java/io/ObjectInputStream.c \ + $(openjdk-src)/share/native/java/io/ObjectOutputStream.c \ + $(openjdk-src)/share/native/java/io/ObjectStreamClass.c \ + $(openjdk-src)/share/native/java/io/RandomAccessFile.c \ + $(openjdk-src)/share/native/java/lang/Class.c \ + $(openjdk-src)/share/native/java/lang/ClassLoader.c \ + $(openjdk-src)/share/native/java/lang/Compiler.c \ + $(openjdk-src)/share/native/java/lang/Double.c \ + $(openjdk-src)/share/native/java/lang/Float.c \ + $(openjdk-src)/share/native/java/lang/Object.c \ + $(openjdk-src)/share/native/java/lang/Package.c \ + $(wildcard $(openjdk-src)/share/native/java/lang/ref/Finalizer.c) \ + $(openjdk-src)/share/native/java/lang/reflect/Array.c \ + $(openjdk-src)/share/native/java/lang/reflect/Proxy.c \ + $(wildcard $(openjdk-src)/share/native/java/lang/ResourceBundle.c) \ + $(openjdk-src)/share/native/java/lang/Runtime.c \ + $(openjdk-src)/share/native/java/lang/SecurityManager.c \ + $(openjdk-src)/share/native/java/lang/Shutdown.c \ + $(openjdk-src)/share/native/java/lang/StrictMath.c \ + $(openjdk-src)/share/native/java/lang/String.c \ + $(openjdk-src)/share/native/java/lang/System.c \ + $(openjdk-src)/share/native/java/lang/Thread.c \ + $(openjdk-src)/share/native/java/lang/Throwable.c \ + $(wildcard $(openjdk-src)/share/native/java/lang/fdlibm/src/*.c) \ + $(openjdk-src)/share/native/java/net/DatagramPacket.c \ + $(openjdk-src)/share/native/java/net/InetAddress.c \ + $(openjdk-src)/share/native/java/net/Inet4Address.c \ + $(openjdk-src)/share/native/java/net/Inet6Address.c \ + $(openjdk-src)/share/native/java/nio/Bits.c \ + $(openjdk-src)/share/native/java/security/AccessController.c \ + $(wildcard $(openjdk-src)/share/native/java/sql/DriverManager.c) \ + $(openjdk-src)/share/native/java/util/concurrent/atomic/AtomicLong.c \ + $(openjdk-src)/share/native/java/util/TimeZone.c \ + $(openjdk-src)/share/native/java/util/zip/Adler32.c \ + $(openjdk-src)/share/native/java/util/zip/CRC32.c \ + $(openjdk-src)/share/native/java/util/zip/Deflater.c \ + $(openjdk-src)/share/native/java/util/zip/Inflater.c \ + $(openjdk-src)/share/native/java/util/zip/ZipFile.c \ + $(openjdk-src)/share/native/java/util/zip/zip_util.c \ + $(openjdk-src)/share/native/sun/management/VMManagementImpl.c \ + $(openjdk-src)/share/native/sun/misc/GC.c \ + $(openjdk-src)/share/native/sun/misc/MessageUtils.c \ + $(openjdk-src)/share/native/sun/misc/NativeSignalHandler.c \ + $(openjdk-src)/share/native/sun/misc/Signal.c \ + $(openjdk-src)/share/native/sun/misc/Version.c \ + $(openjdk-src)/share/native/sun/misc/VM.c \ + $(openjdk-src)/share/native/sun/misc/VMSupport.c \ + $(openjdk-src)/share/native/sun/reflect/ConstantPool.c \ + $(openjdk-src)/share/native/sun/reflect/NativeAccessors.c \ + $(openjdk-src)/share/native/sun/reflect/Reflection.c + +openjdk-headers-classes = \ + java.io.Console \ + java.io.FileDescriptor \ + java.io.FileInputStream \ + java.io.FileOutputStream \ + java.io.FileSystem \ + java.io.ObjectInputStream \ + java.io.ObjectOutputStream \ + java.io.ObjectStreamClass \ + java.io.RandomAccessFile \ + java.lang.Class \ + java.lang.ClassLoader \ + java.lang.Compiler \ + java.lang.Double \ + java.lang.Float \ + java.lang.Integer \ + java.lang.Long \ + java.lang.Object \ + java.lang.Package \ + java.lang.Runtime \ + java.lang.SecurityManager \ + java.lang.Shutdown \ + java.lang.StrictMath \ + java.lang.String \ + java.lang.System \ + java.lang.Thread \ + java.lang.Throwable \ + java.lang.ref.Finalizer \ + java.lang.reflect.Array \ + java.lang.reflect.Proxy \ + java.net.InetAddress \ + java.net.Inet4Address \ + java.net.Inet6Address \ + java.net.DatagramPacket \ + java.net.SocketOptions \ + java.net.InetAddressImplFactory \ + java.net.Inet4AddressImpl \ + java.net.Inet6AddressImpl \ + java.net.NetworkInterface \ + java.net.PlainSocketImpl \ + java.net.SocketInputStream \ + java.net.SocketOutputStream \ + java.nio.MappedByteBuffer \ + java.security.AccessController \ + java.util.ResourceBundle \ + java.util.TimeZone \ + java.util.concurrent.atomic.AtomicLong \ + java.util.jar.JarFile \ + java.util.zip.Adler32 \ + java.util.zip.CRC32 \ + java.util.zip.Deflater \ + java.util.zip.Inflater \ + java.util.zip.ZipEntry \ + java.util.zip.ZipFile \ + sun.management.VMManagementImpl \ + sun.misc.GC \ + sun.misc.MessageUtils \ + sun.misc.NativeSignalHandler \ + sun.misc.Signal \ + sun.misc.VM \ + sun.misc.VMSupport \ + sun.misc.Version \ + sun.misc.URLClassPath \ + sun.net.spi.DefaultProxySelector \ + sun.nio.ch.FileKey \ + sun.nio.ch.FileChannelImpl \ + sun.nio.ch.FileDispatcherImpl \ + sun.nio.ch.DatagramChannelImpl \ + sun.nio.ch.DatagramDispatcher \ + sun.nio.ch.IOStatus \ + sun.nio.ch.IOUtil \ + sun.nio.ch.Net \ + sun.nio.ch.ServerSocketChannelImpl \ + sun.nio.ch.SocketChannelImpl \ + sun.nio.ch.SocketDispatcher \ + sun.nio.ch.PollArrayWrapper \ + sun.nio.ch.NativeThread \ + sun.reflect.ConstantPool \ + sun.reflect.NativeConstructorAccessorImpl \ + sun.reflect.NativeMethodAccessorImpl \ + sun.reflect.Reflection \ + sun.security.provider.NativeSeedGenerator + +ifneq (7,$(openjdk-version)) + openjdk-sources += \ + $(openjdk-src)/share/native/sun/misc/URLClassPath.c +endif + +# todo: set properties according to architecture targeted and OpenJDK +# version used: +openjdk-cflags = \ + "-I$(src)/openjdk" \ + "-I$(build)/openjdk" \ + "-I$(openjdk-src)/share/javavm/export" \ + "-I$(openjdk-src)/share/native/common" \ + "-I$(openjdk-src)/share/native/java/io" \ + "-I$(openjdk-src)/share/native/java/lang" \ + "-I$(openjdk-src)/share/native/java/lang/fdlibm/include" \ + "-I$(openjdk-src)/share/native/java/net" \ + "-I$(openjdk-src)/share/native/java/util/zip" \ + "-I$(openjdk-src)/share/native/sun/management" \ + "-I$(openjdk-src)/share/native/sun/nio/ch" \ + "-I$(openjdk-src)/share/javavm/include" \ + -D_LITTLE_ENDIAN \ + -DARCHPROPNAME=\"x86\" \ + -DRELEASE=\"1.6.0\" \ + -DJDK_MAJOR_VERSION=\"1\" \ + -DJDK_MINOR_VERSION=\"6\" \ + -DJDK_MICRO_VERSION=\"0\" \ + -DJDK_BUILD_NUMBER=\"0\" \ + -D_GNU_SOURCE + +ifeq ($(kernel),darwin) + openjdk-cflags += \ + -D_LFS_LARGEFILE=1 \ + -D_ALLBSD_SOURCE +endif + +ifeq ($(platform),windows) + openjdk-sources += \ + $(openjdk-src)/windows/native/common/jni_util_md.c \ + $(openjdk-src)/windows/native/java/io/canonicalize_md.c \ + $(openjdk-src)/windows/native/java/io/Console_md.c \ + $(openjdk-src)/windows/native/java/io/FileDescriptor_md.c \ + $(openjdk-src)/windows/native/java/io/FileInputStream_md.c \ + $(openjdk-src)/windows/native/java/io/FileOutputStream_md.c \ + $(openjdk-src)/windows/native/java/io/io_util_md.c \ + $(openjdk-src)/windows/native/java/io/RandomAccessFile_md.c \ + $(openjdk-src)/windows/native/java/io/WinNTFileSystem_md.c \ + $(openjdk-src)/windows/native/java/lang/java_props_md.c \ + $(openjdk-src)/windows/native/java/lang/ProcessEnvironment_md.c \ + $(openjdk-src)/windows/native/java/lang/ProcessImpl_md.c \ + $(openjdk-src)/windows/native/java/net/net_util_md.c \ + $(openjdk-src)/windows/native/java/net/ExtendedOptionsImpl.c \ + $(openjdk-src)/windows/native/java/net/DualStackPlainSocketImpl.c \ + $(openjdk-src)/windows/native/java/net/InetAddressImplFactory.c \ + $(openjdk-src)/windows/native/java/net/Inet4AddressImpl.c \ + $(openjdk-src)/windows/native/java/net/Inet6AddressImpl.c \ + $(openjdk-src)/windows/native/java/net/NetworkInterface.c \ + $(openjdk-src)/windows/native/java/net/NetworkInterface_winXP.c \ + $(openjdk-src)/windows/native/java/net/SocketInputStream.c \ + $(openjdk-src)/windows/native/java/net/SocketOutputStream.c \ + $(openjdk-src)/windows/native/java/net/TwoStacksPlainDatagramSocketImpl.c \ + $(openjdk-src)/windows/native/java/net/TwoStacksPlainSocketImpl.c \ + $(openjdk-src)/windows/native/java/util/WindowsPreferences.c \ + $(openjdk-src)/windows/native/java/util/logging.c \ + $(openjdk-src)/windows/native/java/util/TimeZone_md.c \ + $(openjdk-src)/windows/native/sun/io/Win32ErrorMode.c \ + $(openjdk-src)/windows/native/sun/nio/ch/DatagramChannelImpl.c \ + $(openjdk-src)/windows/native/sun/nio/ch/DatagramDispatcher.c \ + $(openjdk-src)/windows/native/sun/nio/ch/FileChannelImpl.c \ + $(openjdk-src)/windows/native/sun/nio/ch/FileDispatcherImpl.c \ + $(openjdk-src)/windows/native/sun/nio/ch/FileKey.c \ + $(openjdk-src)/windows/native/sun/nio/ch/IOUtil.c \ + $(openjdk-src)/windows/native/sun/nio/ch/Net.c \ + $(openjdk-src)/windows/native/sun/nio/ch/ServerSocketChannelImpl.c \ + $(openjdk-src)/windows/native/sun/nio/ch/SocketChannelImpl.c \ + $(openjdk-src)/windows/native/sun/nio/ch/SocketDispatcher.c \ + $(openjdk-src)/windows/native/sun/nio/ch/WindowsSelectorImpl.c \ + $(openjdk-src)/windows/native/sun/nio/fs/WindowsNativeDispatcher.c \ + $(openjdk-src)/windows/native/sun/security/provider/WinCAPISeedGenerator.c + + ifeq (7,$(openjdk-version)) + openjdk-sources += \ + $(openjdk-src)/windows/native/java/io/FileSystem_md.c \ + $(openjdk-src)/windows/native/java/io/Win32FileSystem_md.c + endif + + openjdk-headers-classes += \ + java.net.DualStackPlainSocketImpl \ + java.net.SocketImpl \ + java.net.TwoStacksPlainDatagramSocketImpl \ + java.net.TwoStacksPlainSocketImpl \ + java.lang.ProcessImpl \ + sun.io.Win32ErrorMode \ + sun.nio.ch.WindowsSelectorImpl \ + sun.nio.fs.WindowsNativeDispatcher \ + + openjdk-cflags += \ + "-I$(openjdk-src)/windows/javavm/export" \ + "-I$(openjdk-src)/windows/native/common" \ + "-I$(openjdk-src)/windows/native/java/io" \ + "-I$(openjdk-src)/windows/native/java/net" \ + "-I$(openjdk-src)/windows/native/java/util" \ + "-I$(openjdk-src)/windows/native/sun/nio/ch" \ + "-I$(openjdk-src)/windows/javavm/include" \ + -DLOCALE_SNAME=0x0000005c \ + -DLOCALE_SISO3166CTRYNAME2=0x00000068 \ + -DLOCALE_SISO639LANGNAME2=0x00000067 \ + -D_JNI_IMPLEMENTATION_ \ + -D_JAVASOFT_WIN32_TYPEDEF_MD_H_ \ + -D_WIN32_WINNT=0x0600 \ + -Ds6_words=_s6_words \ + -Ds6_bytes=_s6_bytes + + ifeq ($(arch),x86_64) + openjdk-cflags += "-I$(root)/win64/include" + else + openjdk-cflags += "-I$(root)/win32/include" + endif +else + openjdk-sources += \ + $(shell find $(openjdk-src)/solaris/native/common -name '*.c') \ + $(openjdk-src)/solaris/native/java/io/canonicalize_md.c \ + $(openjdk-src)/solaris/native/java/io/Console_md.c \ + $(openjdk-src)/solaris/native/java/io/FileDescriptor_md.c \ + $(openjdk-src)/solaris/native/java/io/FileInputStream_md.c \ + $(openjdk-src)/solaris/native/java/io/FileOutputStream_md.c \ + $(wildcard $(openjdk-src)/solaris/native/java/io/FileSystem_md.c) \ + $(openjdk-src)/solaris/native/java/io/io_util_md.c \ + $(openjdk-src)/solaris/native/java/io/RandomAccessFile_md.c \ + $(openjdk-src)/solaris/native/java/io/UnixFileSystem_md.c \ + $(openjdk-src)/solaris/native/java/lang/java_props_md.c \ + $(wildcard $(openjdk-src)/solaris/native/java/lang/childproc.c) \ + $(openjdk-src)/solaris/native/java/lang/ProcessEnvironment_md.c \ + $(openjdk-src)/solaris/native/java/lang/UNIXProcess_md.c \ + $(openjdk-src)/solaris/native/java/net/net_util_md.c \ + $(openjdk-src)/solaris/native/java/net/ExtendedOptionsImpl.c \ + $(openjdk-src)/solaris/native/java/net/InetAddressImplFactory.c \ + $(openjdk-src)/solaris/native/java/net/Inet4AddressImpl.c \ + $(openjdk-src)/solaris/native/java/net/Inet6AddressImpl.c \ + $(openjdk-src)/solaris/native/java/net/NetworkInterface.c \ + $(openjdk-src)/solaris/native/java/net/PlainSocketImpl.c \ + $(openjdk-src)/solaris/native/java/net/PlainDatagramSocketImpl.c \ + $(openjdk-src)/solaris/native/java/net/SocketInputStream.c \ + $(openjdk-src)/solaris/native/java/net/SocketOutputStream.c \ + $(openjdk-src)/solaris/native/java/nio/MappedByteBuffer.c \ + $(openjdk-src)/solaris/native/java/util/FileSystemPreferences.c \ + $(openjdk-src)/solaris/native/java/util/logging.c \ + $(openjdk-src)/solaris/native/java/util/TimeZone_md.c \ + $(openjdk-src)/solaris/native/sun/net/dns/ResolverConfigurationImpl.c \ + $(openjdk-src)/solaris/native/sun/net/spi/DefaultProxySelector.c \ + $(openjdk-src)/solaris/native/sun/nio/ch/DatagramChannelImpl.c \ + $(openjdk-src)/solaris/native/sun/nio/ch/DatagramDispatcher.c \ + $(openjdk-src)/solaris/native/sun/nio/ch/FileChannelImpl.c \ + $(openjdk-src)/solaris/native/sun/nio/ch/FileDispatcherImpl.c \ + $(openjdk-src)/solaris/native/sun/nio/ch/FileKey.c \ + $(openjdk-src)/solaris/native/sun/nio/ch/IOUtil.c \ + $(openjdk-src)/solaris/native/sun/nio/ch/Net.c \ + $(openjdk-src)/solaris/native/sun/nio/ch/ServerSocketChannelImpl.c \ + $(openjdk-src)/solaris/native/sun/nio/ch/SocketChannelImpl.c \ + $(openjdk-src)/solaris/native/sun/nio/ch/SocketDispatcher.c \ + $(openjdk-src)/solaris/native/sun/nio/ch/PollArrayWrapper.c \ + $(openjdk-src)/solaris/native/sun/nio/ch/InheritedChannel.c \ + $(openjdk-src)/solaris/native/sun/nio/ch/NativeThread.c \ + $(openjdk-src)/solaris/native/sun/nio/fs/UnixNativeDispatcher.c \ + + openjdk-headers-classes += \ + java.net.PlainDatagramSocketImpl \ + java.io.UnixFileSystem \ + sun.nio.ch.InheritedChannel \ + sun.nio.fs.UnixNativeDispatcher \ + + ifneq (7,$(openjdk-version)) + openjdk-headers-classes += \ + jdk.net.SocketFlow + endif + + openjdk-cflags += \ + "-I$(openjdk-src)/solaris/javavm/export" \ + "-I$(openjdk-src)/solaris/native/common" \ + "-I$(openjdk-src)/solaris/native/java/io" \ + "-I$(openjdk-src)/solaris/native/java/lang" \ + "-I$(openjdk-src)/solaris/native/java/net" \ + "-I$(openjdk-src)/solaris/native/java/util" \ + "-I$(openjdk-src)/solaris/native/sun/management" \ + "-I$(openjdk-src)/solaris/native/sun/nio/ch" \ + "-I$(openjdk-src)/solaris/javavm/include" \ + "-I$(openjdk-src)/solaris/hpi/include" \ + "-I$(openjdk-src)/solaris/native/common/deps" \ + "-I$(openjdk-src)/solaris/native/common/deps/fontconfig2" \ + "-I$(openjdk-src)/solaris/native/common/deps/gconf2" \ + "-I$(openjdk-src)/solaris/native/common/deps/glib2" \ + "-I$(openjdk-src)/solaris/native/common/deps/gtk2" \ + "-DX11_PATH=\"/usr/X11R6\"" + + ifeq ($(platform),linux) + openjdk-sources += \ + $(openjdk-src)/solaris/native/java/net/linux_close.c \ + $(openjdk-src)/solaris/native/sun/nio/ch/EPollArrayWrapper.c + + openjdk-headers-classes += \ + sun.nio.ch.EPollArrayWrapper + + openjdk-cflags += \ + "-I$(openjdk-src)/solaris/native/common/deps/glib2" \ + "-I$(openjdk-src)/solaris/native/common/deps/gconf2" \ + "-I$(openjdk-src)/solaris/native/common/deps/fontconfig2" \ + "-I$(openjdk-src)/solaris/native/common/deps/gtk2" \ + $(shell pkg-config --cflags glib-2.0) \ + $(shell pkg-config --cflags gconf-2.0) + endif + + ifeq ($(kernel),darwin) + openjdk-sources += \ + $(openjdk-src)/solaris/native/java/net/bsd_close.c \ + $(openjdk-src)/macosx/native/sun/nio/ch/KQueueArrayWrapper.c + + ifeq ($(platform),ios) + openjdk-local-sources += \ + $(src)/openjdk/my_java_props_macosx.c + else + openjdk-sources += \ + $(openjdk-src)/solaris/native/java/lang/java_props_macosx.c + endif + + openjdk-cflags += \ + -DMACOSX -x objective-c + endif +endif + +openjdk-local-sources += \ + $(src)/openjdk/my_net_util.c \ + $(src)/openjdk/my_management.c + +openjdk-c-objects = \ + $(foreach x,$(1),$(patsubst $(2)/%.c,$(3)/%-openjdk.o,$(x))) + +openjdk-objects = \ + $(call openjdk-c-objects,$(openjdk-sources),$(openjdk-src),$(build)/openjdk) + +openjdk-local-objects = \ + $(call openjdk-c-objects,$(openjdk-local-sources),$(src)/openjdk,$(build)/openjdk) + +openjdk-headers-dep = $(build)/openjdk/headers.dep diff --git a/sgx-jvm/avian/openjdk.ld b/sgx-jvm/avian/openjdk.ld new file mode 100644 index 0000000000..915cdde6e2 --- /dev/null +++ b/sgx-jvm/avian/openjdk.ld @@ -0,0 +1,294 @@ +# +# @(#)mapfile-vers-product 1.19 08/02/12 10:56:37 +# + +# +# Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Define public interface. + +SUNWprivate_1.1 { + global: + # JNI + JNI_CreateJavaVM; + JNI_GetCreatedJavaVMs; + JNI_GetDefaultJavaVMInitArgs; + + # JVM + JVM_Accept; + JVM_ActiveProcessorCount; + JVM_AllocateNewArray; + JVM_AllocateNewObject; + JVM_ArrayCopy; + JVM_AssertionStatusDirectives; + JVM_Available; + JVM_Bind; + JVM_ClassDepth; + JVM_ClassLoaderDepth; + JVM_Clone; + JVM_Close; + JVM_CX8Field; + JVM_CompileClass; + JVM_CompileClasses; + JVM_CompilerCommand; + JVM_Connect; + JVM_ConstantPoolGetClassAt; + JVM_ConstantPoolGetClassAtIfLoaded; + JVM_ConstantPoolGetDoubleAt; + JVM_ConstantPoolGetFieldAt; + JVM_ConstantPoolGetFieldAtIfLoaded; + JVM_ConstantPoolGetFloatAt; + JVM_ConstantPoolGetIntAt; + JVM_ConstantPoolGetLongAt; + JVM_ConstantPoolGetMethodAt; + JVM_ConstantPoolGetMethodAtIfLoaded; + JVM_ConstantPoolGetMemberRefInfoAt; + JVM_ConstantPoolGetSize; + JVM_ConstantPoolGetStringAt; + JVM_ConstantPoolGetUTF8At; + JVM_CountStackFrames; + JVM_CurrentClassLoader; + JVM_CurrentLoadedClass; + JVM_CurrentThread; + JVM_CurrentTimeMillis; + JVM_DefineClass; + JVM_DefineClassWithSource; + JVM_DefineClassWithSourceCond; + JVM_DesiredAssertionStatus; + JVM_DisableCompiler; + JVM_DoPrivileged; + JVM_DTraceGetVersion; + JVM_DTraceActivate; + JVM_DTraceIsProbeEnabled; + JVM_DTraceIsSupported; + JVM_DTraceDispose; + JVM_DumpAllStacks; + JVM_DumpThreads; + JVM_EnableCompiler; + JVM_Exit; + JVM_FillInStackTrace; + JVM_FindClassFromCaller; + JVM_FindClassFromClass; + JVM_FindClassFromClassLoader; + JVM_FindClassFromBootLoader; + JVM_FindLibraryEntry; + JVM_FindLoadedClass; + JVM_FindPrimitiveClass; + JVM_FindSignal; + JVM_FreeMemory; + JVM_GC; + JVM_GetAllThreads; + JVM_GetArrayElement; + JVM_GetArrayLength; + JVM_GetCPClassNameUTF; + JVM_GetCPFieldClassNameUTF; + JVM_GetCPFieldModifiers; + JVM_GetCPFieldNameUTF; + JVM_GetCPFieldSignatureUTF; + JVM_GetCPMethodClassNameUTF; + JVM_GetCPMethodModifiers; + JVM_GetCPMethodNameUTF; + JVM_GetCPMethodSignatureUTF; + JVM_GetCallerClass; + JVM_GetClassAccessFlags; + JVM_GetClassAnnotations; + JVM_GetClassCPEntriesCount; + JVM_GetClassCPTypes; + JVM_GetClassConstantPool; + JVM_GetClassContext; + JVM_GetClassDeclaredConstructors; + JVM_GetClassDeclaredFields; + JVM_GetClassDeclaredMethods; + JVM_GetClassFieldsCount; + JVM_GetClassInterfaces; + JVM_GetClassLoader; + JVM_GetClassMethodsCount; + JVM_GetClassModifiers; + JVM_GetClassName; + JVM_GetClassNameUTF; + JVM_GetClassSignature; + JVM_GetClassSigners; + JVM_GetClassTypeAnnotations; + JVM_GetComponentType; + JVM_GetDeclaredClasses; + JVM_GetDeclaringClass; + JVM_GetEnclosingMethodInfo; + JVM_GetFieldAnnotations; + JVM_GetFieldIxModifiers; + JVM_GetFieldTypeAnnotations; + JVM_GetHostName; + JVM_GetInheritedAccessControlContext; + JVM_GetInterfaceVersion; + JVM_GetLastErrorString; + JVM_GetManagement; + JVM_GetMethodAnnotations; + JVM_GetMethodDefaultAnnotationValue; + JVM_GetMethodIxArgsSize; + JVM_GetMethodIxByteCode; + JVM_GetMethodIxByteCodeLength; + JVM_GetMethodIxExceptionIndexes; + JVM_GetMethodIxExceptionTableEntry; + JVM_GetMethodIxExceptionTableLength; + JVM_GetMethodIxExceptionsCount; + JVM_GetMethodIxLocalsCount; + JVM_GetMethodIxMaxStack; + JVM_GetMethodIxModifiers; + JVM_GetMethodIxNameUTF; + JVM_GetMethodIxSignatureUTF; + JVM_GetMethodParameterAnnotations; + JVM_GetPrimitiveArrayElement; + JVM_GetProtectionDomain; + JVM_GetResourceLookupCacheURLs; + JVM_GetSockName; + JVM_GetSockOpt; + JVM_GetStackAccessControlContext; + JVM_GetStackTraceDepth; + JVM_GetStackTraceElement; + JVM_GetSystemPackage; + JVM_GetSystemPackages; + JVM_GetThreadStateNames; + JVM_GetThreadStateValues; + JVM_GetVersionInfo; + JVM_Halt; + JVM_HoldsLock; + JVM_IHashCode; + JVM_InitAgentProperties; + JVM_InitProperties; + JVM_InitializeCompiler; + JVM_InitializeSocketLibrary; + JVM_InternString; + JVM_Interrupt; + JVM_InvokeMethod; + JVM_IsArrayClass; + JVM_IsConstructorIx; + JVM_IsInterface; + JVM_IsInterrupted; + JVM_IsNaN; + JVM_IsPrimitiveClass; + JVM_IsSameClassPackage; + JVM_IsSilentCompiler; + JVM_IsSupportedJNIVersion; + JVM_IsThreadAlive; + JVM_IsVMGeneratedMethodIx; + JVM_LatestUserDefinedLoader; + JVM_Listen; + JVM_LoadClass0; + JVM_LoadLibrary; + JVM_Lseek; + JVM_MaxObjectInspectionAge; + JVM_MaxMemory; + JVM_MonitorNotify; + JVM_MonitorNotifyAll; + JVM_MonitorWait; + JVM_NanoTime; + JVM_NativePath; + JVM_NewArray; + JVM_NewInstanceFromConstructor; + JVM_NewMultiArray; + JVM_OnExit; + JVM_Open; + JVM_PrintStackTrace; + JVM_RaiseSignal; + JVM_RawMonitorCreate; + JVM_RawMonitorDestroy; + JVM_RawMonitorEnter; + JVM_RawMonitorExit; + JVM_Read; + JVM_Recv; + JVM_RecvFrom; + JVM_RegisterSignal; + JVM_ReleaseUTF; + JVM_ResolveClass; + JVM_ResumeThread; + JVM_Send; + JVM_SendTo; + JVM_SetArrayElement; + JVM_SetClassSigners; + JVM_SetLength; + JVM_SetNativeThreadName; + JVM_SetPrimitiveArrayElement; + JVM_SetProtectionDomain; + JVM_SetSockOpt; + JVM_SetThreadPriority; + JVM_Sleep; + JVM_Socket; + JVM_SocketAvailable; + JVM_SocketClose; + JVM_SocketShutdown; + JVM_StartThread; + JVM_StopThread; + JVM_SuspendThread; + JVM_SupportsCX8; + JVM_Sync; + JVM_Timeout; + JVM_TotalMemory; + JVM_TraceInstructions; + JVM_TraceMethodCalls; + JVM_UnloadLibrary; + JVM_Write; + JVM_Yield; + JVM_handle_linux_signal; + + # Old reflection routines + # These do not need to be present in the product build in JDK 1.4 + # but their code has not been removed yet because there will not + # be a substantial code savings until JVM_InvokeMethod and + # JVM_NewInstanceFromConstructor can also be removed; see + # reflectionCompat.hpp. + JVM_GetClassConstructor; + JVM_GetClassConstructors; + JVM_GetClassField; + JVM_GetClassFields; + JVM_GetClassMethod; + JVM_GetClassMethods; + JVM_GetField; + JVM_GetPrimitiveField; + JVM_NewInstance; + JVM_SetField; + JVM_SetPrimitiveField; + + # Needed for dropping VM into JDK 1.3.x, 1.4 + _JVM_native_threads; + jdk_sem_init; + jdk_sem_post; + jdk_sem_wait; + jdk_pthread_sigmask; + jdk_waitpid; + + # miscellaneous functions + jio_fprintf; + jio_printf; + jio_snprintf; + jio_vfprintf; + jio_vsnprintf; + fork1; + numa_warn; + numa_error; + + # Needed because there is no JVM interface for this. + sysThreadAvailableStackWithSlack; + + # This is for Forte Analyzer profiling support. + AsyncGetCallTrace; +}; diff --git a/sgx-jvm/avian/openjdk.pro b/sgx-jvm/avian/openjdk.pro new file mode 100644 index 0000000000..2393930fff --- /dev/null +++ b/sgx-jvm/avian/openjdk.pro @@ -0,0 +1,334 @@ +# proguard include file (http://proguard.sourceforge.net) + +# This file is for use in combination with vm.pro when ProGuarding +# OpenJDK-based builds + +# the following methods and fields are refered to by name in the VM: + +-keepclassmembers class java.lang.Thread { + public void run(); + } + +-keepclassmembers class java.lang.ThreadGroup { + void threadTerminated(java.lang.Thread); + } + +-keep class java.lang.System { + private static void initializeSystemClass(); + public static void setProperties(java.util.Properties); + } + +-keep class sun.misc.Launcher { + public static sun.misc.Launcher getLauncher(); + } + +-keep class java.lang.ClassLoader { + private static java.lang.ClassLoader scl; + private static boolean sclSet; + + protected ClassLoader(java.lang.ClassLoader); + } + +-keep class avian.SystemClassLoader { + protected java.net.URL findResource(java.lang.String); + } + +-keepnames class java.lang.ClassLoader { + public java.lang.Class loadClass(java.lang.String); + static void loadLibrary(java.lang.Class, java.lang.String, boolean); + private static java.net.URL getBootstrapResource(java.lang.String); + private static java.util.Enumeration getBootstrapResources(java.lang.String); + } + +-keep class java.util.Properties { + public java.lang.Object setProperty(java.lang.String, java.lang.String); + public java.lang.String getProperty(java.lang.String); + } + +-keep class java.util.Hashtable { + public java.lang.Object remove(java.lang.Object); + } + +-keep class avian.OpenJDK { + ; + } + +-keepclassmembers public class java.security.PrivilegedAction { + public java.lang.Object run(); + } + +-keepclassmembers public class * implements java.security.PrivilegedAction { + public java.lang.Object run(); + } + +-keepclassmembers public class java.security.PrivilegedExceptionAction { + public java.lang.Object run(); + } + +-keepclassmembers public class * implements java.security.PrivilegedExceptionAction { + public java.lang.Object run(); + } + +-keep public class java.security.PrivilegedActionException { + public PrivilegedActionException(java.lang.Exception); + } + +# these class names are used to disambiguate JNI method lookups: + +-keepnames public class java.net.URL +-keepnames public class java.util.Enumeration +-keepnames public class java.security.ProtectionDomain +-keepnames public class java.security.PrivilegedAction +-keepnames public class java.security.PrivilegedExceptionAction +-keepnames public class java.security.AccessControlContext + +# the following methods and fields are refered to by name in the OpenJDK +# native code: + +-keep class java.util.Properties { + public java.lang.Object put(java.lang.Object, java.lang.Object); + } + +-keepclassmembers class * { + public boolean equals(java.lang.Object); + public void wait(); + public void notify(); + public void notifyAll(); + public java.lang.String toString(); + } + +-keepclassmembers class java.lang.String { + public String(byte[]); + public String(byte[], java.lang.String); + public byte[] getBytes(); + public byte[] getBytes(java.lang.String); + } + +-keepclassmembers class java.lang.Boolean { + public boolean getBoolean(java.lang.String); + } + +-keepclassmembers class java.util.zip.Inflater { + long strm; + boolean needDict; + boolean finished; + byte[] buf; + int off; + int len; + } + +-keepclassmembers class java.io.FileDescriptor { + private int fd; + private long handle; + } + +-keep class java.net.NetworkInterface { + ; + } +-keep class java.net.InetAddress { + ; + } +-keep class java.net.Inet4Address { + ; + } +-keep class java.net.Inet4AddressImpl +-keep class java.net.Inet6Address { + ; + } +-keep class java.net.Inet6AddressImpl +-keep class java.net.InetSocketAddress { + public InetSocketAddress(java.net.InetAddress, int); + } +-keep class java.net.ServerSocket +-keep class java.net.SocketTimeoutException + +-keepclassmembers class java.net.PlainSocketImpl { + ; + } + +-keepclassmembers class java.net.TwoStacksPlainSocketImpl { + *** fd1; + *** lastfd; +} + +-keepclassmembers class java.net.AbstractPlainSocketImpl { + *** timeout; + *** trafficClass; +} + +-keepclassmembers class java.net.SocketImpl { + *** serverSocket; + *** fd; + *** address; + *** port; + *** localport; +} + +-keepclassmembers class java.io.FileInputStream { + private java.io.FileDescriptor fd; + } + +-keepclassmembers class java.io.FileOutputStream { + private java.io.FileDescriptor fd; + private boolean append; + } + +# changed in native code via sun.misc.Unsafe (todo: handle other +# Atomic* classes) +-keepclassmembers class java.util.concurrent.atomic.AtomicInteger { + private int value; + } + +# avoid inlining due to access check using a fixed offset into call stack: +-keep,allowshrinking,allowobfuscation class java.util.concurrent.atomic.AtomicReferenceFieldUpdater { + *** newUpdater(...); + } + +# accessed reflectively via an AtomicReferenceFieldUpdater: +-keepclassmembers class java.io.BufferedInputStream { + protected byte[] buf; + } + +-keep class java.lang.System { + public static java.io.InputStream in; + public static java.io.PrintStream out; + public static java.io.PrintStream err; + # avoid inlining due to access check using fixed offset into call stack: + static java.lang.Class getCallerClass(); + # called from jni_util.c: + static java.lang.String getProperty(java.lang.String); + } + +# refered to by name from native code: +-keepnames public class java.io.InputStream +-keepnames public class java.io.PrintStream + +# avoid inlining due to access check using fixed offset into call stack: +-keep,allowshrinking,allowobfuscation class java.lang.System { + static java.lang.Class getCallerClass(); + } + +-keep class java.io.UnixFileSystem { + public UnixFileSystem(); + } + +-keep class java.io.WinNTFileSystem { + public WinNTFileSystem(); + } + +-keep class java.io.File { + private java.lang.String path; + } + +-keepclassmembers class java.lang.ClassLoader$NativeLibrary { + long handle; + private int jniVersion; + } + +-keep class java.nio.charset.Charset { + # called from jni_util.c: + boolean isSupported(java.lang.String); + } + +# Charsets are loaded via reflection. If you need others besides +# UTF-8, you'll need to add them (e.g. sun.nio.cs.ISO_8859_1). +-keep class sun.nio.cs.UTF_8 + +# loaded reflectively to handle embedded resources: +-keep class avian.avianvmresource.Handler + +# loaded reflectively by sun.misc.Launcher: +-keep class avian.file.Handler + +# refered to symbolically in MethodAccessorGenerator: +-keep class sun.reflect.MethodAccessorImpl { + ; + } +-keep class sun.reflect.ConstructorAccessorImpl { + ; + } +-keep class sun.reflect.SerializationConstructorAccessorImpl { + ; + } + +# referred to by name in LocaleData to load resources: +-keep class sun.util.resources.CalendarData +-keep class sun.util.resources.TimeZoneNames +-keep class sun.text.resources.FormatData + +# loaded via reflection from DefaultFileSystemProvider: +-keep class sun.nio.fs.LinuxFileSystemProvider +-keep class sun.nio.fs.BsdFileSystemProvider + +# loaded via JNI in UnixNativeDispatcher.c: +-keep class sun.nio.fs.UnixFileAttributes { + ; +} +-keep class sun.nio.fs.UnixFileStoreAttributes { + ; +} +-keep class sun.nio.fs.UnixMountEntry { + ; +} + +-keep class sun.nio.fs.UnixException { + UnixException(int); + } + +-keep class sun.net.www.protocol.jar.Handler + +# These concurrent classes refer to certain members reflectively in their static initializers +-keepclassmembers class java.util.concurrent.ConcurrentHashMap$HashEntry { + *** next; +} + +-keepclassmembers class java.util.concurrent.CopyOnWriteArrayList { + *** lock; +} + +-keepclassmembers class java.util.concurrent.CountDownLatch { + *** allocationSpinLock; +} + +-keepclassmembers class java.util.concurrent.PriorityBlockingQueue { + *** allocationSpinLock; +} + +-keepclassmembers class java.util.concurrent.SynchronousQueue$TransferStack { + *** head; +} + +-keepclassmembers class java.util.concurrent.ConcurrentLinkedQueue { + *** head; + *** tail; +} + +-keepclassmembers class java.util.concurrent.ConcurrentLinkedQueue$Node { + *** item; + *** next; +} + +-keepclassmembers class java.util.concurrent.SynchronousQueue$TransferStack$SNode { + *** match; + *** next; +} + +# These classes are accessed via bytecode generated on the fly. +-keepclassmembers class java.lang.reflect.Proxy { + ; +} + +-keepclassmembers class java.lang.reflect.InvocationHandler { + ; +} + +# Don't strip special enum members. +-keepclassmembers,allowoptimization enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +-keepclassmembers class java.lang.reflect.Executable { + ; +} + diff --git a/sgx-jvm/avian/settings.gradle b/sgx-jvm/avian/settings.gradle new file mode 100644 index 0000000000..1d99d5dcf6 --- /dev/null +++ b/sgx-jvm/avian/settings.gradle @@ -0,0 +1 @@ +rootProject.name="avian" \ No newline at end of file diff --git a/sgx-jvm/avian/src/CMakeLists.txt b/sgx-jvm/avian/src/CMakeLists.txt new file mode 100644 index 0000000000..c949643c0a --- /dev/null +++ b/sgx-jvm/avian/src/CMakeLists.txt @@ -0,0 +1,7 @@ +add_subdirectory(codegen) +add_subdirectory(system) +add_subdirectory(heap) +add_subdirectory(util) +add_subdirectory(tools) + +add_library(avian_jvm_finder finder.cpp) \ No newline at end of file diff --git a/sgx-jvm/avian/src/android/stubs.cpp b/sgx-jvm/avian/src/android/stubs.cpp new file mode 100644 index 0000000000..0c1295d464 --- /dev/null +++ b/sgx-jvm/avian/src/android/stubs.cpp @@ -0,0 +1,24 @@ +struct JavaVM; + +extern "C" int JNI_OnLoad(JavaVM*, void*) +{ + return 0; +} + +struct _JNIEnv; + +struct JniConstants { + static void init(_JNIEnv* env); +}; + +void JniConstants::init(_JNIEnv*) +{ + // ignore +} + +struct _JavaVM; + +int libconscrypt_JNI_OnLoad(_JavaVM*, void*) +{ + return 0; +} diff --git a/sgx-jvm/avian/src/arm.S b/sgx-jvm/avian/src/arm.S new file mode 100644 index 0000000000..941097d4e8 --- /dev/null +++ b/sgx-jvm/avian/src/arm.S @@ -0,0 +1,132 @@ +/* arm.S: JNI gluecode for ARM + Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/types.h" + +.text + +#define LOCAL(x) .L##x + +#ifdef __APPLE__ +# define GLOBAL(x) _##x +#else +# define GLOBAL(x) x +#endif + +.globl GLOBAL(vmNativeCall) +.align 2 +GLOBAL(vmNativeCall): + /* + arguments: + r0 -> r4 : function + r1 -> r5 : stackTotal + r2 : memoryTable + r3 : memoryCount + [sp, #0] -> r6 : gprTable + [sp, #4] -> r7 : vfpTable + [sp, #8] -> r8 : returnType + */ + mov ip, sp // save stack frame + stmfd sp!, {r4-r8, lr} // save clobbered non-volatile regs + + // mv args into non-volatile regs + mov r4, r0 + mov r5, r1 + ldr r6, [ip] + ldr r7, [ip, #4] + ldr r8, [ip, #8] + + // setup stack arguments if necessary + sub sp, sp, r5 // allocate stack + mov ip, sp +LOCAL(loop): + tst r3, r3 + ldrne r0, [r2], #4 + strne r0, [ip], #4 + subne r3, r3, #4 + bne LOCAL(loop) + + // setup argument registers if necessary + tst r6, r6 +#if (defined __APPLE__) && (defined __clang_major__) && (__clang_major__ >= 4) + ldmiane r6, {r0-r3} +#else + ldmneia r6, {r0-r3} +#endif +#if defined(__ARM_PCS_VFP) + // and VFP registers + vldmia r7, {d0-d7} +#endif + +#if defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__) + mov lr, pc + bx r4 +#else + blx r4 // call function +#endif + add sp, sp, r5 // deallocate stack + +#if defined(__ARM_PCS_VFP) + cmp r8,#FLOAT_TYPE + bne LOCAL(double) + fmrs r0,s0 + b LOCAL(exit) + +LOCAL(double): + cmp r8,#DOUBLE_TYPE + bne LOCAL(exit) + fmrrd r0,r1,d0 +#endif + +LOCAL(exit): + ldmfd sp!, {r4-r8, pc} // restore non-volatile regs and return + +.globl GLOBAL(vmJump) +.align 2 +GLOBAL(vmJump): + mov lr, r0 + ldr r0, [sp] + ldr r1, [sp, #4] + mov sp, r2 + mov r8, r3 + bx lr + +#define CHECKPOINT_THREAD 4 +#define CHECKPOINT_STACK 24 + +.globl GLOBAL(vmRun) +.align 2 +GLOBAL(vmRun): + // r0: function + // r1: arguments + // r2: checkpoint + stmfd sp!, {r4-r11, lr} + // align stack + sub sp, sp, #12 + + str sp, [r2, #CHECKPOINT_STACK] + + mov r12, r0 + ldr r0, [r2, #CHECKPOINT_THREAD] + +#if defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__) + mov lr, pc + bx r12 +#else + blx r12 +#endif + +.globl GLOBAL(vmRun_returnAddress) +.align 2 +GLOBAL(vmRun_returnAddress): + add sp, sp, #12 + ldmfd sp!, {r4-r11, lr} + bx lr diff --git a/sgx-jvm/avian/src/arm.masm b/sgx-jvm/avian/src/arm.masm new file mode 100644 index 0000000000..ae036db58f --- /dev/null +++ b/sgx-jvm/avian/src/arm.masm @@ -0,0 +1,89 @@ +; Copyright (c) 2008-2015, Avian Contributors +; +; Permission to use, copy, modify, and/or distribute this software +; for any purpose with or without fee is hereby granted, provided +; that the above copyright notice and this permission notice appear +; in all copies. +; +; There is NO WARRANTY for this software. See license.txt for +; details. +; +; ORIGIN: https://github.com/gkvas/avian/tree/wince + + AREA text, CODE, ARM + + EXPORT vmNativeCall +vmNativeCall + ; arguments: + ; r0 -> r4 : function + ; r1 -> r5 : stackTotal + ; r2 : memoryTable + ; r3 : memoryCount + ; [sp, #0] -> r6 : gprTable + + mov ip, sp ; save stack frame + stmfd sp!, {r4-r6, lr} ; save clobbered non-volatile regs + + ; mv args into non-volatile regs + mov r4, r0 + mov r5, r1 + ldr r6, [ip] + + ; setup stack arguments if necessary + sub sp, sp, r5 ; allocate stack + mov ip, sp +loop + tst r3, r3 + ldrne r0, [r2], #4 + strne r0, [ip], #4 + subne r3, r3, #4 + bne loop + + ; setup argument registers if necessary + tst r6, r6 + ldmneia r6, {r0-r3} + + blx r4 ; call function + add sp, sp, r5 ; deallocate stack + + ldmfd sp!, {r4-r6, pc} ; restore non-volatile regs and return + + EXPORT vmJump +vmJump + mov lr, r0 + ldr r0, [sp] + ldr r1, [sp, #4] + mov sp, r2 + mov r8, r3 + bx lr + +CHECKPOINT_THREAD EQU 4 +CHECKPOINT_STACK EQU 24 + + EXPORT vmRun +vmRun + ; r0: function + ; r1: arguments + ; r2: checkpoint + stmfd sp!, {r4-r11, lr} + ; align stack + sub sp, sp, #12 + + str sp, [r2, #CHECKPOINT_STACK] + + mov r12, r0 + ldr r0, [r2, #CHECKPOINT_THREAD] + + blx r12 + + EXPORT vmRun_returnAddress +vmRun_returnAddress + add sp, sp, #12 + ldmfd sp!, {r4-r11, lr} + bx lr + + EXPORT vmTrap +vmTrap + bkpt 3 + + END \ No newline at end of file diff --git a/sgx-jvm/avian/src/arm64.S b/sgx-jvm/avian/src/arm64.S new file mode 100644 index 0000000000..1941be3ea6 --- /dev/null +++ b/sgx-jvm/avian/src/arm64.S @@ -0,0 +1,148 @@ +/* arm.S: JNI gluecode for ARM + Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/types.h" + +.text + +#define LOCAL(x) .L##x + +#ifdef __APPLE__ +# define GLOBAL(x) _##x +#else +# define GLOBAL(x) x +#endif + +.globl GLOBAL(vmNativeCall) +.align 2 +GLOBAL(vmNativeCall): + // arguments: + // x0 -> x19 : function + // w1 -> w20 : stackTotal + // x2 : memoryTable + // w3 : memoryCount + // x4 -> x21 : gprTable + // x5 -> x22 : vfpTable + // w6 -> w23 : returnType + + // allocate frame + stp x29, x30, [sp,#-64]! + mov x29, sp + + // save callee-saved register values so we can clobber them + stp x19, x20, [sp,#16] + stp x21, x22, [sp,#32] + str x23, [sp,#48] + + // move arguments into callee-saved registers + mov x19, x0 + mov w20, w1 + mov x21, x4 + mov x22, x5 + mov w23, w6 + + // setup stack arguments if necessary + sub sp, sp, w20, uxtw // allocate stack + mov x9, sp +LOCAL(loop): + cmp w3, wzr + b.eq LOCAL(populateGPRs) + ldr x0, [x2], #8 + str x0, [x9], #8 + sub w3, w3, #8 + b LOCAL(loop) + +LOCAL(populateGPRs): + cmp x21, xzr + b.eq LOCAL(populateVFPs) + ldp x0, x1, [x21] + ldp x2, x3, [x21,#16] + ldp x4, x5, [x21,#32] + ldp x6, x7, [x21,#48] + +LOCAL(populateVFPs): + cmp x22, xzr + b.eq LOCAL(doCall) + ldp d0, d1, [x22] + ldp d2, d3, [x22,#16] + ldp d4, d5, [x22,#32] + ldp d6, d7, [x22,#48] + +LOCAL(doCall): + blr x19 // call function + add sp, sp, w20, uxtw // deallocate stack + + cmp w23,#FLOAT_TYPE + b.ne LOCAL(double) + fmov w0,s0 + b LOCAL(exit) + +LOCAL(double): + cmp w23,#DOUBLE_TYPE + b.ne LOCAL(exit) + fmov x0,d0 + +LOCAL(exit): + ldp x19, x20, [sp,#16] + ldp x21, x22, [sp,#32] + ldr x23, [sp,#48] + ldp x29, x30, [sp],#64 + ret + +.globl GLOBAL(vmJump) +.align 2 +GLOBAL(vmJump): + mov x30, x0 + mov x0, x4 + mov x1, x5 + mov sp, x2 + mov x19, x3 + br x30 + +#define CHECKPOINT_THREAD 8 +#define CHECKPOINT_STACK 48 + +.globl GLOBAL(vmRun) +.align 2 +GLOBAL(vmRun): + // x0: function + // x1: arguments + // x2: checkpoint + + // allocate frame + stp x29, x30, [sp,#-96]! + mov x29, sp + + // save callee-saved register values + stp x19, x20, [sp,#16] + stp x21, x22, [sp,#32] + stp x23, x24, [sp,#48] + stp x25, x26, [sp,#64] + stp x27, x28, [sp,#80] + + mov x19, sp + str x19, [x2, #CHECKPOINT_STACK] + + mov x19, x0 + ldr x0, [x2, #CHECKPOINT_THREAD] + + blr x19 + +.globl GLOBAL(vmRun_returnAddress) +.align 2 +GLOBAL(vmRun_returnAddress): + ldp x19, x20, [sp,#16] + ldp x21, x22, [sp,#32] + ldp x23, x24, [sp,#48] + ldp x25, x26, [sp,#64] + ldp x27, x28, [sp,#80] + ldp x29, x30, [sp],#96 + br x30 diff --git a/sgx-jvm/avian/src/avian/alloc-vector.h b/sgx-jvm/avian/src/avian/alloc-vector.h new file mode 100644 index 0000000000..b1df61e31c --- /dev/null +++ b/sgx-jvm/avian/src/avian/alloc-vector.h @@ -0,0 +1,186 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef VECTOR_H +#define VECTOR_H + +#include + +#include +#include +#include +#include + +#undef max +#undef min + +namespace vm { + +class Vector { + public: + Vector(avian::util::Aborter* a, + avian::util::Alloc* allocator, + size_t minimumCapacity) + : a(a), + allocator(allocator), + data(0, 0), + position(0), + minimumCapacity(minimumCapacity) + { + } + + ~Vector() + { + dispose(); + } + + void dispose() + { + if (data.items and minimumCapacity > 0) { + allocator->free(data.items, data.count); + data.items = 0; + data.count = 0; + } + } + + void ensure(size_t space) + { + if (position + space > data.count) { + assertT(a, minimumCapacity > 0); + + size_t newCapacity = avian::util::max( + position + space, avian::util::max(minimumCapacity, data.count * 2)); + if (data.begin()) { + data.resize(allocator, newCapacity); + } else { + data = avian::util::Slice::alloc(allocator, newCapacity); + } + } + } + + void get(size_t offset, void* dst, size_t size) + { + assertT(a, offset + size <= position); + memcpy(dst, data.begin() + offset, size); + } + + void set(size_t offset, const void* src, size_t size) + { + assertT(a, offset + size <= position); + memcpy(data.begin() + offset, src, size); + } + + void pop(void* dst, size_t size) + { + get(position - size, dst, size); + position -= size; + } + + void* allocate(size_t size) + { + ensure(size); + void* r = data.begin() + position; + position += size; + return r; + } + + void* append(const void* p, size_t size) + { + void* r = allocate(size); + memcpy(r, p, size); + return r; + } + + void append(uint8_t v) + { + append(&v, 1); + } + + void append2(uint16_t v) + { + append(&v, 2); + } + + void append4(uint32_t v) + { + append(&v, 4); + } + + void appendTargetAddress(target_uintptr_t v) + { + append(&v, TargetBytesPerWord); + } + + void appendAddress(uintptr_t v) + { + append(&v, BytesPerWord); + } + + void appendAddress(void* v) + { + append(&v, BytesPerWord); + } + + void set2(size_t offset, uint16_t v) + { + assertT(a, offset <= position - 2); + memcpy(data.begin() + offset, &v, 2); + } + + size_t get(size_t offset) + { + uint8_t v; + get(offset, &v, 1); + return v; + } + + size_t get2(size_t offset) + { + uint16_t v; + get(offset, &v, 2); + return v; + } + + size_t get4(size_t offset) + { + uint32_t v; + get(offset, &v, 4); + return v; + } + + uintptr_t getAddress(size_t offset) + { + uintptr_t v; + get(offset, &v, BytesPerWord); + return v; + } + + size_t length() + { + return position; + } + + template + T* peek(size_t offset) + { + assertT(a, offset + sizeof(T) <= position); + return reinterpret_cast(data.begin() + offset); + } + + avian::util::Aborter* a; + avian::util::Alloc* allocator; + avian::util::Slice data; + size_t position; + size_t minimumCapacity; +}; + +} // namespace vm + +#endif // VECTOR_H diff --git a/sgx-jvm/avian/src/avian/append.h b/sgx-jvm/avian/src/avian/append.h new file mode 100644 index 0000000000..5ea19bbfeb --- /dev/null +++ b/sgx-jvm/avian/src/avian/append.h @@ -0,0 +1,56 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef APPEND_H +#define APPEND_H + +#include +#include + +namespace vm { + +inline const char* append(avian::util::AllocOnly* allocator, + const char* a, + const char* b, + const char* c) +{ + unsigned al = strlen(a); + unsigned bl = strlen(b); + unsigned cl = strlen(c); + char* p = static_cast(allocator->allocate((al + bl + cl) + 1)); + memcpy(p, a, al); + memcpy(p + al, b, bl); + memcpy(p + al + bl, c, cl + 1); + return p; +} + +inline const char* append(avian::util::AllocOnly* allocator, + const char* a, + const char* b) +{ + unsigned al = strlen(a); + unsigned bl = strlen(b); + char* p = static_cast(allocator->allocate((al + bl) + 1)); + memcpy(p, a, al); + memcpy(p + al, b, bl + 1); + return p; +} + +inline const char* copy(avian::util::AllocOnly* allocator, const char* a) +{ + unsigned al = strlen(a); + char* p = static_cast(allocator->allocate(al + 1)); + memcpy(p, a, al + 1); + return p; +} + +} // namespace vm + +#endif // APPEND_H diff --git a/sgx-jvm/avian/src/avian/arch.h b/sgx-jvm/avian/src/avian/arch.h new file mode 100644 index 0000000000..0328d4124f --- /dev/null +++ b/sgx-jvm/avian/src/avian/arch.h @@ -0,0 +1,52 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef ARCH_H +#define ARCH_H + +#ifdef _MSC_VER +#include "windows.h" +#pragma push_macro("assert") +#include "intrin.h" +#pragma pop_macro("assert") +#undef interface +#endif + +#include "avian/common.h" + +extern "C" void NO_RETURN vmJump(void* address, + void* frame, + void* stack, + void* thread, + uintptr_t returnLow, + uintptr_t returnHigh); + +namespace vm { + +inline void compileTimeMemoryBarrier() +{ +#ifdef _MSC_VER + _ReadWriteBarrier(); +#else + __asm__ __volatile__("" : : : "memory"); +#endif +} + +} // namespace vm + +#if (defined ARCH_x86_32) || (defined ARCH_x86_64) +#include "x86.h" +#elif (defined ARCH_arm) || (defined ARCH_arm64) +#include "arm.h" +#else +#error unsupported architecture +#endif + +#endif // ARCH_H diff --git a/sgx-jvm/avian/src/avian/arm.h b/sgx-jvm/avian/src/avian/arm.h new file mode 100644 index 0000000000..4a0797207b --- /dev/null +++ b/sgx-jvm/avian/src/avian/arm.h @@ -0,0 +1,403 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef ARM_H +#define ARM_H + +#include "avian/types.h" +#include "avian/common.h" +#include + +#ifdef __APPLE__ +#include "libkern/OSAtomic.h" +#include "libkern/OSCacheControl.h" +#include "mach/mach_types.h" +#include "mach/thread_act.h" +#include "mach/thread_status.h" + +#define THREAD_STATE ARM_THREAD_STATE +#define THREAD_STATE_TYPE arm_thread_state_t +#define THREAD_STATE_COUNT ARM_THREAD_STATE_COUNT + +#if __DARWIN_UNIX03 && defined(_STRUCT_ARM_EXCEPTION_STATE) +#define FIELD(x) __##x +#else +#define FIELD(x) x +#endif + +#define THREAD_STATE_IP(state) ((state).FIELD(pc)) +#define THREAD_STATE_STACK(state) ((state).FIELD(sp)) +#if (defined __APPLE__) && (defined ARCH_arm64) +#define THREAD_STATE_THREAD(state) ((state).FIELD(x[19])) +#else +#define THREAD_STATE_THREAD(state) ((state).FIELD(r[8])) +#endif +#define THREAD_STATE_LINK(state) ((state).FIELD(lr)) + +#define IP_REGISTER(context) THREAD_STATE_IP(context->uc_mcontext->FIELD(ss)) +#define STACK_REGISTER(context) \ + THREAD_STATE_STACK(context->uc_mcontext->FIELD(ss)) +#define THREAD_REGISTER(context) \ + THREAD_STATE_THREAD(context->uc_mcontext->FIELD(ss)) +#define LINK_REGISTER(context) \ + THREAD_STATE_LINK(context->uc_mcontext->FIELD(ss)) +#elif(defined __QNX__) +#include "arm/smpxchg.h" +#include "sys/mman.h" + +#define IP_REGISTER(context) (context->uc_mcontext.cpu.gpr[ARM_REG_PC]) +#define STACK_REGISTER(context) (context->uc_mcontext.cpu.gpr[ARM_REG_SP]) +#define THREAD_REGISTER(context) (context->uc_mcontext.cpu.gpr[ARM_REG_IP]) +#define LINK_REGISTER(context) (context->uc_mcontext.cpu.gpr[ARM_REG_LR]) +#else +#ifdef ARCH_arm +#define IP_REGISTER(context) (context->uc_mcontext.arm_pc) +#define STACK_REGISTER(context) (context->uc_mcontext.arm_sp) +#define THREAD_REGISTER(context) (context->uc_mcontext.arm_ip) +#define LINK_REGISTER(context) (context->uc_mcontext.arm_lr) +#else +#define IP_REGISTER(context) (context->uc_mcontext.pc) +#define STACK_REGISTER(context) (context->uc_mcontext.sp) +#define THREAD_REGISTER(context) (context->uc_mcontext.regs[19]) +#define LINK_REGISTER(context) (context->uc_mcontext.regs[30]) +#endif +#endif + +#define VA_LIST(x) (&(x)) + +extern "C" uint64_t vmNativeCall(void* function, + unsigned stackTotal, + void* memoryTable, + unsigned memoryCount, + void* gprTable, + void* vfpTable, + unsigned returnType); + +namespace vm { + +inline void trap() +{ +#ifdef _MSC_VER + __debugbreak(); +#else + asm("brk 0"); +#endif +} + +// todo: determine the minimal operation types and domains needed to +// implement the following barriers (see +// http://community.arm.com/groups/processors/blog/2011/10/19/memory-access-ordering-part-3--memory-access-ordering-in-the-arm-architecture). +// For now, we just use DMB SY as a conservative but not necessarily +// performant choice. + +#ifndef _MSC_VER +inline void memoryBarrier() +{ +#ifdef __APPLE__ + OSMemoryBarrier(); +#elif(__GNUC__ >= 4) && (__GNUC_MINOR__ >= 1) + return __sync_synchronize(); +#elif(!defined AVIAN_ASSUME_ARMV6) + __asm__ __volatile__("dmb" : : : "memory"); +#else + __asm__ __volatile__("" : : : "memory"); +#endif +} +#endif + +inline void storeStoreMemoryBarrier() +{ +#ifdef _MSC_VER + _ReadWriteBarrier(); +#else + memoryBarrier(); +#endif +} + +inline void storeLoadMemoryBarrier() +{ +#ifdef _MSC_VER + MemoryBarrier(); +#else + memoryBarrier(); +#endif +} + +inline void loadMemoryBarrier() +{ +#ifdef _MSC_VER + _ReadWriteBarrier(); +#else + memoryBarrier(); +#endif +} + +#if !defined(AVIAN_AOT_ONLY) + +#if defined(__ANDROID__) || defined(__linux__) +// http://code.google.com/p/android/issues/detail?id=1803 +extern "C" void __clear_cache(void* beg __attribute__((__unused__)), + void* end __attribute__((__unused__))); +#endif +inline void syncInstructionCache(const void* start, unsigned size) +{ +#ifdef __APPLE__ + sys_icache_invalidate(const_cast(start), size); +#elif(defined __QNX__) + msync(const_cast(start), size, MS_INVALIDATE_ICACHE); +#else + __clear_cache( + const_cast(start), + const_cast(static_cast(start) + size)); +#endif +} + +#endif // AVIAN_AOT_ONLY + +#ifndef __APPLE__ +typedef int(__kernel_cmpxchg_t)(int oldval, int newval, int* ptr); +#define __kernel_cmpxchg (*(__kernel_cmpxchg_t*)0xffff0fc0) +#endif + +inline bool atomicCompareAndSwap32(uint32_t* p, uint32_t old, uint32_t new_) +{ +#ifdef __APPLE__ + return OSAtomicCompareAndSwap32Barrier( + old, new_, reinterpret_cast(p)); +#elif(defined __QNX__) + return old == _smp_cmpxchg(p, old, new_); +#elif (defined ARCH_arm64) + return __sync_bool_compare_and_swap(p, old, new_); +#else + int r = __kernel_cmpxchg( + static_cast(old), static_cast(new_), reinterpret_cast(p)); + return (!r ? true : false); +#endif +} + +#ifdef ARCH_arm64 +inline bool atomicCompareAndSwap64(uint64_t* p, uint64_t old, uint64_t new_) +{ + return __sync_bool_compare_and_swap(p, old, new_); +} + +inline bool atomicCompareAndSwap(uintptr_t* p, uintptr_t old, uintptr_t new_) +{ + return atomicCompareAndSwap64(reinterpret_cast(p), old, new_); +} +#else +inline bool atomicCompareAndSwap(uintptr_t* p, uintptr_t old, uintptr_t new_) +{ + return atomicCompareAndSwap32(reinterpret_cast(p), old, new_); +} +#endif + +#if (defined __APPLE__) && (defined ARCH_arm64) +const bool AppleARM64 = true; +#else +const bool AppleARM64 = false; +#endif + +inline void advance(unsigned* stackIndex, + unsigned* stackSubIndex, + unsigned newStackSubIndex) +{ + if (AppleARM64) { + if (newStackSubIndex == BytesPerWord) { + *stackSubIndex = 0; + ++(*stackIndex); + } else { + *stackSubIndex = newStackSubIndex; + } + } +} + +inline void push(uint8_t type, + uintptr_t* stack, + unsigned* stackIndex, + unsigned* stackSubIndex, + uintptr_t argument) +{ + if (AppleARM64) { + // See + // https://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html + // for how Apple diverges from the generic ARM64 ABI on iOS. + // Specifically, arguments passed on the stack are aligned to + // their natural alignment rather than 8. + switch (type) { + case INT8_TYPE: + reinterpret_cast(stack + *stackIndex)[*stackSubIndex] = argument; + advance(stackIndex, stackSubIndex, *stackSubIndex + 1); + break; + + case INT16_TYPE: + advance(stackIndex, stackSubIndex, pad(*stackSubIndex, 2)); + reinterpret_cast(stack + *stackIndex)[*stackSubIndex / 2] + = argument; + advance(stackIndex, stackSubIndex, *stackSubIndex + 2); + break; + + case INT32_TYPE: + case FLOAT_TYPE: + advance(stackIndex, stackSubIndex, pad(*stackSubIndex, 4)); + reinterpret_cast(stack + *stackIndex)[*stackSubIndex / 4] + = argument; + advance(stackIndex, stackSubIndex, *stackSubIndex + 4); + break; + + case POINTER_TYPE: + advance(stackIndex, stackSubIndex, pad(*stackSubIndex)); + stack[(*stackIndex)++] = argument; + break; + + default: + abort(); + } + } else { + stack[(*stackIndex)++] = argument; + } +} + +inline uint64_t dynamicCall(void* function, + uintptr_t* arguments, + uint8_t* argumentTypes, + unsigned argumentCount, + unsigned argumentsSize UNUSED, + unsigned returnType) +{ +#if (defined __APPLE__) || (defined ARCH_arm64) + const unsigned Alignment = 1; +#else + const unsigned Alignment = 2; +#endif + + const unsigned GprCount = BytesPerWord; + uintptr_t gprTable[GprCount]; + unsigned gprIndex = 0; + + const unsigned VfpCount = BytesPerWord == 8 ? 8 : 16; + uintptr_t vfpTable[VfpCount]; + unsigned vfpIndex = 0; + unsigned vfpBackfillIndex UNUSED = 0; + + RUNTIME_ARRAY(uintptr_t, + stack, + (argumentCount * 8) + / BytesPerWord); // is > argumentSize to account for padding + unsigned stackIndex = 0; + unsigned stackSubIndex = 0; + + unsigned ai = 0; + for (unsigned ati = 0; ati < argumentCount; ++ati) { + switch (argumentTypes[ati]) { + case DOUBLE_TYPE: +#if (defined __ARM_PCS_VFP) || (defined ARCH_arm64) + { + if (vfpIndex + Alignment <= VfpCount) { + if (vfpIndex % Alignment) { + vfpBackfillIndex = vfpIndex; + ++vfpIndex; + } + + memcpy(vfpTable + vfpIndex, arguments + ai, 8); + vfpIndex += 8 / BytesPerWord; + } else { + advance(&stackIndex, &stackSubIndex, pad(stackSubIndex)); + vfpIndex = VfpCount; + if (stackIndex % Alignment) { + ++stackIndex; + } + + memcpy(RUNTIME_ARRAY_BODY(stack) + stackIndex, arguments + ai, 8); + stackIndex += 8 / BytesPerWord; + } + ai += 8 / BytesPerWord; + } break; + + case FLOAT_TYPE: + if (vfpBackfillIndex) { + vfpTable[vfpBackfillIndex] = arguments[ai]; + vfpBackfillIndex = 0; + } else if (vfpIndex < VfpCount) { + vfpTable[vfpIndex++] = arguments[ai]; + } else { + push(argumentTypes[ati], + RUNTIME_ARRAY_BODY(stack), + &stackIndex, + &stackSubIndex, + arguments[ai]); + } + ++ai; + break; +#endif + case INT64_TYPE: { + if (gprIndex + Alignment <= GprCount) { // pass argument in register(s) + if (Alignment == 1 and BytesPerWord < 8 + and gprIndex + Alignment == GprCount) { + gprTable[gprIndex++] = arguments[ai]; + RUNTIME_ARRAY_BODY(stack)[stackIndex++] = arguments[ai + 1]; + } else { + if (gprIndex % Alignment) { + ++gprIndex; + } + + memcpy(gprTable + gprIndex, arguments + ai, 8); + gprIndex += 8 / BytesPerWord; + } + } else { // pass argument on stack + advance(&stackIndex, &stackSubIndex, pad(stackSubIndex)); + gprIndex = GprCount; + if (stackIndex % Alignment) { + ++stackIndex; + } + + memcpy(RUNTIME_ARRAY_BODY(stack) + stackIndex, arguments + ai, 8); + stackIndex += 8 / BytesPerWord; + } + ai += 8 / BytesPerWord; + } break; + + default: { + if (gprIndex < GprCount) { + gprTable[gprIndex++] = arguments[ai]; + } else { + push(argumentTypes[ati], + RUNTIME_ARRAY_BODY(stack), + &stackIndex, + &stackSubIndex, + arguments[ai]); + } + ++ai; + } break; + } + } + + if (gprIndex < GprCount) { // pad since assembly loads all GPRs + memset(gprTable + gprIndex, 0, (GprCount - gprIndex) * 4); + gprIndex = GprCount; + } + if (vfpIndex < VfpCount) { + memset(vfpTable + vfpIndex, 0, (VfpCount - vfpIndex) * 4); + vfpIndex = VfpCount; + } + + unsigned stackSize = pad(stackIndex * BytesPerWord + stackSubIndex, 16); + return vmNativeCall(function, + stackSize, + RUNTIME_ARRAY_BODY(stack), + pad(stackIndex * BytesPerWord + stackSubIndex, BytesPerWord), + (gprIndex ? gprTable : 0), + (vfpIndex ? vfpTable : 0), + returnType); +} + +} // namespace vm + +#endif // ARM_H diff --git a/sgx-jvm/avian/src/avian/bootimage.h b/sgx-jvm/avian/src/avian/bootimage.h new file mode 100644 index 0000000000..d14755f8b3 --- /dev/null +++ b/sgx-jvm/avian/src/avian/bootimage.h @@ -0,0 +1,81 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef BOOTIMAGE_H +#define BOOTIMAGE_H + +#include "avian/common.h" +#include "java-common.h" +#include "avian/target.h" +#include "avian/machine.h" + +#include + +namespace vm { + +class BootImage { + public: + class Thunk { + public: + Thunk() : start(0), frameSavedOffset(0), length(0) + { + } + + Thunk(uint32_t start, uint32_t frameSavedOffset, uint32_t length) + : start(start), frameSavedOffset(frameSavedOffset), length(length) + { + } + + uint32_t start; + uint32_t frameSavedOffset; + uint32_t length; + } PACKED; + + class ThunkCollection { + public: +#define THUNK_FIELD(name) Thunk name; +#include "bootimage-fields.cpp" +#undef THUNK_FIELD + } PACKED; + + static const uint32_t Magic = 0x22377322; + +#define FIELD(name) uint32_t name; +#include "bootimage-fields.cpp" +#undef FIELD + + ThunkCollection thunks; +} 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 +#define LABEL(x) target_##x +#include "bootimage-template.cpp" +#undef LABEL +#undef NAME + +#define NAME(x) x +#define LABEL(x) x +#include "bootimage-template.cpp" +#undef LABEL +#undef NAME + +} // namespace vm + +#endif // BOOTIMAGE_H diff --git a/sgx-jvm/avian/src/avian/classpath-common.h b/sgx-jvm/avian/src/avian/classpath-common.h new file mode 100644 index 0000000000..85815cbb76 --- /dev/null +++ b/sgx-jvm/avian/src/avian/classpath-common.h @@ -0,0 +1,864 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef CLASSPATH_COMMON_H +#define CLASSPATH_COMMON_H + +#include +#include +#include + +using namespace avian::util; + +namespace vm { + +object getTrace(Thread* t, unsigned skipCount) +{ + class Visitor : public Processor::StackVisitor { + public: + Visitor(Thread* t, int skipCount) : t(t), trace(0), skipCount(skipCount) + { + } + + virtual bool visit(Processor::StackWalker* walker) + { + if (skipCount == 0) { + GcMethod* method = walker->method(); + if (isAssignableFrom(t, type(t, GcThrowable::Type), method->class_()) + and vm::strcmp(reinterpret_cast(""), + method->name()->body().begin()) == 0) { + return true; + } else { + trace = makeTrace(t, walker); + return false; + } + } else { + --skipCount; + return true; + } + } + + Thread* t; + object trace; + unsigned skipCount; + } v(t, skipCount); + + t->m->processor->walkStack(t, &v); + + if (v.trace == 0) + v.trace = makeObjectArray(t, 0); + + return v.trace; +} + +bool compatibleArrayTypes(Thread* t UNUSED, GcClass* a, GcClass* b) +{ + return a->arrayElementSize() and b->arrayElementSize() + and (a == b or (not((a->vmFlags() & PrimitiveFlag) + or (b->vmFlags() & PrimitiveFlag)))); +} + +void arrayCopy(Thread* t, + object src, + int32_t srcOffset, + object dst, + int32_t dstOffset, + int32_t length) +{ + if (LIKELY(src and dst)) { + if (LIKELY(compatibleArrayTypes( + t, objectClass(t, src), objectClass(t, dst)))) { + unsigned elementSize = objectClass(t, src)->arrayElementSize(); + + if (LIKELY(elementSize)) { + intptr_t sl = fieldAtOffset(src, BytesPerWord); + intptr_t dl = fieldAtOffset(dst, BytesPerWord); + if (LIKELY(length > 0)) { + if (LIKELY(srcOffset >= 0 and srcOffset + length <= sl + and dstOffset >= 0 and dstOffset + length <= dl)) { + uint8_t* sbody = &fieldAtOffset(src, ArrayBody); + uint8_t* dbody = &fieldAtOffset(dst, ArrayBody); + if (src == dst) { + memmove(dbody + (dstOffset * elementSize), + sbody + (srcOffset * elementSize), + length * elementSize); + } else { + memcpy(dbody + (dstOffset * elementSize), + sbody + (srcOffset * elementSize), + length * elementSize); + } + + if (objectClass(t, dst)->objectMask()) { + mark(t, dst, ArrayBody + (dstOffset * BytesPerWord), length); + } + + return; + } else { + throwNew(t, GcIndexOutOfBoundsException::Type); + } + } else { + return; + } + } + } + } else { + throwNew(t, GcNullPointerException::Type); + return; + } + + throwNew(t, GcArrayStoreException::Type); +} + +void runOnLoadIfFound(Thread* t, System::Library* library) +{ + void* p = library->resolve("JNI_OnLoad"); + +#ifdef PLATFORM_WINDOWS + if (p == 0) { + p = library->resolve("_JNI_OnLoad@8"); + if (p == 0) { + p = library->resolve("JNI_OnLoad@8"); + } + } +#endif + + if (p) { + jint(JNICALL * JNI_OnLoad)(Machine*, void*); + memcpy(&JNI_OnLoad, &p, sizeof(void*)); + JNI_OnLoad(t->m, 0); + } +} + +System::Library* loadLibrary(Thread* t, const char* name) +{ + ACQUIRE(t, t->m->classLock); + + System::Library* last = t->m->libraries; + for (System::Library* lib = t->m->libraries; lib; lib = lib->next()) { + if (lib->name() and ::strcmp(lib->name(), name) == 0) { + // already loaded + return lib; + } + last = lib; + } + + System::Library* lib; + if (t->m->system->success(t->m->system->load(&lib, name))) { + last->setNext(lib); + return lib; + } else { + return 0; + } +} + +System::Library* loadLibrary(Thread* t, + const char* path, + const char* name, + bool mapName, + bool runOnLoad, + bool throw_ = true) +{ + ACQUIRE(t, t->m->classLock); + + char* mappedName; + unsigned nameLength = strlen(name); + if (mapName) { + const char* builtins = findProperty(t, "avian.builtins"); + if (builtins) { + const char* s = builtins; + while (*s) { + if (::strncmp(s, name, nameLength) == 0 + and (s[nameLength] == ',' or s[nameLength] == 0)) { + // library is built in to this executable + if (runOnLoad and not t->m->triedBuiltinOnLoad) { + t->m->triedBuiltinOnLoad = true; + // todo: release the classLock before calling this to + // avoid the possibility of deadlock: + runOnLoadIfFound(t, t->m->libraries); + } + return t->m->libraries; + } else { + while (*s and *s != ',') + ++s; + if (*s) + ++s; + } + } + } + + const char* prefix = t->m->system->libraryPrefix(); + const char* suffix = t->m->system->librarySuffix(); + unsigned mappedNameLength = nameLength + strlen(prefix) + strlen(suffix); + + mappedName = static_cast(t->m->heap->allocate(mappedNameLength + 1)); + + snprintf(mappedName, mappedNameLength + 1, "%s%s%s", prefix, name, suffix); + + name = mappedName; + nameLength = mappedNameLength; + } else { + mappedName = 0; + } + + THREAD_RESOURCE2(t, char*, mappedName, unsigned, nameLength, if (mappedName) { + t->m->heap->free(mappedName, nameLength + 1); + }); + + System::Library* lib = 0; + for (Tokenizer tokenizer(path, t->m->system->pathSeparator()); + tokenizer.hasMore();) { + String token(tokenizer.next()); + + unsigned fullNameLength = token.length + 1 + nameLength; + THREAD_RUNTIME_ARRAY(t, char, fullName, fullNameLength + 1); + + snprintf(RUNTIME_ARRAY_BODY(fullName), + fullNameLength + 1, + "%.*s/%s", + token.length, + token.text, + name); + + lib = loadLibrary(t, RUNTIME_ARRAY_BODY(fullName)); + if (lib) + break; + } + + if (lib == 0) { + lib = loadLibrary(t, name); + } + + if (lib) { + if (runOnLoad) { + runOnLoadIfFound(t, lib); + } + } else if (throw_) { + throwNew(t, + GcUnsatisfiedLinkError::Type, + "library not found in %s: %s", + path, + name); + } + + return lib; +} + +GcStackTraceElement* makeStackTraceElement(Thread* t, GcTraceElement* e) +{ + PROTECT(t, e); + + GcMethod* method = cast(t, e->method()); + PROTECT(t, method); + + GcByteArray* class_name = method->class_()->name(); + PROTECT(t, class_name); + + THREAD_RUNTIME_ARRAY(t, char, s, class_name->length()); + replace('/', + '.', + RUNTIME_ARRAY_BODY(s), + reinterpret_cast(class_name->body().begin())); + GcString* class_name_string = makeString(t, "%s", RUNTIME_ARRAY_BODY(s)); + PROTECT(t, class_name_string); + + GcByteArray* method_name = method->name(); + PROTECT(t, method_name); + + GcString* method_name_string = t->m->classpath->makeString( + t, method_name, 0, method_name->length() - 1); + PROTECT(t, method_name_string); + + unsigned line = t->m->processor->lineNumber(t, method, e->ip()); + + GcByteArray* file = method->class_()->sourceFile(); + GcString* file_string + = file ? t->m->classpath->makeString(t, file, 0, file->length() - 1) : 0; + + return makeStackTraceElement( + t, class_name_string, method_name_string, file_string, line); +} + +GcObject* translateInvokeResult(Thread* t, unsigned returnCode, object o) +{ + switch (returnCode) { + case ByteField: + return makeByte(t, cast(t, o)->value()); + + case BooleanField: + return makeBoolean(t, cast(t, o)->value() != 0); + + case CharField: + return makeChar(t, cast(t, o)->value()); + + case ShortField: + return makeShort(t, cast(t, o)->value()); + + case FloatField: + return makeFloat(t, cast(t, o)->value()); + + case IntField: + case LongField: + case ObjectField: + case VoidField: + return reinterpret_cast(o); + + case DoubleField: + return makeDouble(t, cast(t, o)->value()); + + default: + abort(t); + } +} + +GcClass* resolveClassBySpec(Thread* t, + GcClassLoader* loader, + const char* spec, + unsigned specLength) +{ + switch (*spec) { + case 'L': { + THREAD_RUNTIME_ARRAY(t, char, s, specLength - 1); + memcpy(RUNTIME_ARRAY_BODY(s), spec + 1, specLength - 2); + RUNTIME_ARRAY_BODY(s)[specLength - 2] = 0; + return resolveClass(t, loader, RUNTIME_ARRAY_BODY(s)); + } + + case '[': { + THREAD_RUNTIME_ARRAY(t, char, s, specLength + 1); + memcpy(RUNTIME_ARRAY_BODY(s), spec, specLength); + RUNTIME_ARRAY_BODY(s)[specLength] = 0; + return resolveClass(t, loader, RUNTIME_ARRAY_BODY(s)); + } + + default: + return primitiveClass(t, *spec); + } +} + +GcJclass* resolveJType(Thread* t, + GcClassLoader* loader, + const char* spec, + unsigned specLength) +{ + return getJClass(t, resolveClassBySpec(t, loader, spec, specLength)); +} + +GcPair* resolveParameterTypes(Thread* t, + GcClassLoader* loader, + GcByteArray* spec, + unsigned* parameterCount, + unsigned* returnTypeSpec) +{ + PROTECT(t, loader); + PROTECT(t, spec); + + GcPair* list = 0; + PROTECT(t, list); + + unsigned offset = 1; + unsigned count = 0; + while (spec->body()[offset] != ')') { + switch (spec->body()[offset]) { + case 'L': { + unsigned start = offset; + ++offset; + while (spec->body()[offset] != ';') + ++offset; + ++offset; + + GcClass* type + = resolveClassBySpec(t, + loader, + reinterpret_cast(&spec->body()[start]), + offset - start); + + list = makePair(t, type, list); + + ++count; + } break; + + case '[': { + unsigned start = offset; + while (spec->body()[offset] == '[') + ++offset; + switch (spec->body()[offset]) { + case 'L': + ++offset; + while (spec->body()[offset] != ';') + ++offset; + ++offset; + break; + + default: + ++offset; + break; + } + + GcClass* type + = resolveClassBySpec(t, + loader, + reinterpret_cast(&spec->body()[start]), + offset - start); + + list = makePair(t, type, list); + ++count; + } break; + + default: + list = makePair(t, primitiveClass(t, spec->body()[offset]), list); + ++offset; + ++count; + break; + } + } + + *parameterCount = count; + *returnTypeSpec = offset + 1; + return list; +} + +object resolveParameterJTypes(Thread* t, + GcClassLoader* loader, + GcByteArray* spec, + unsigned* parameterCount, + unsigned* returnTypeSpec) +{ + GcPair* list + = resolveParameterTypes(t, loader, spec, parameterCount, returnTypeSpec); + + PROTECT(t, list); + + object array = makeObjectArray(t, type(t, GcJclass::Type), *parameterCount); + PROTECT(t, array); + + for (int i = *parameterCount - 1; i >= 0; --i) { + object c = getJClass(t, cast(t, list->first())); + reinterpret_cast(array)->setBodyElement(t, i, c); + list = cast(t, list->second()); + } + + return array; +} + +object resolveExceptionJTypes(Thread* t, + GcClassLoader* loader, + GcMethodAddendum* addendum) +{ + if (addendum == 0 or addendum->exceptionTable() == 0) { + return makeObjectArray(t, type(t, GcJclass::Type), 0); + } + + PROTECT(t, loader); + PROTECT(t, addendum); + + GcShortArray* exceptionTable + = cast(t, addendum->exceptionTable()); + PROTECT(t, exceptionTable); + + object array + = makeObjectArray(t, type(t, GcJclass::Type), exceptionTable->length()); + PROTECT(t, array); + + for (unsigned i = 0; i < exceptionTable->length(); ++i) { + uint16_t index = exceptionTable->body()[i] - 1; + + object o = singletonObject(t, addendum->pool()->as(t), index); + + if (objectClass(t, o) == type(t, GcReference::Type)) { + o = resolveClass(t, loader, cast(t, o)->name()); + + addendum->pool()->setBodyElement( + t, index, reinterpret_cast(o)); + } + + o = getJClass(t, cast(t, o)); + + reinterpret_cast(array)->setBodyElement(t, i, o); + } + + return array; +} + +object invoke(Thread* t, GcMethod* method, object instance, object args) +{ + PROTECT(t, method); + PROTECT(t, instance); + PROTECT(t, args); + + if (method->flags() & ACC_STATIC) { + instance = 0; + } + + if ((args == 0 ? 0 : objectArrayLength(t, args)) + != method->parameterCount()) { + throwNew(t, GcIllegalArgumentException::Type); + } + + if (method->parameterCount()) { + unsigned specLength = method->spec()->length(); + THREAD_RUNTIME_ARRAY(t, char, spec, specLength); + memcpy( + RUNTIME_ARRAY_BODY(spec), method->spec()->body().begin(), specLength); + unsigned i = 0; + for (MethodSpecIterator it(t, RUNTIME_ARRAY_BODY(spec)); it.hasNext();) { + GcClass* type; + bool objectType = false; + const char* p = it.next(); + switch (*p) { + case 'Z': + type = vm::type(t, GcBoolean::Type); + break; + case 'B': + type = vm::type(t, GcByte::Type); + break; + case 'S': + type = vm::type(t, GcShort::Type); + break; + case 'C': + type = vm::type(t, GcChar::Type); + break; + case 'I': + type = vm::type(t, GcInt::Type); + break; + case 'F': + type = vm::type(t, GcFloat::Type); + break; + case 'J': + type = vm::type(t, GcLong::Type); + break; + case 'D': + type = vm::type(t, GcDouble::Type); + break; + + case 'L': + case '[': { + objectType = true; + unsigned nameLength; + if (*p == 'L') { + ++p; + nameLength = it.s - p; + } else { + nameLength = (it.s - p) + 1; + } + THREAD_RUNTIME_ARRAY(t, char, name, nameLength); + memcpy(RUNTIME_ARRAY_BODY(name), p, nameLength - 1); + RUNTIME_ARRAY_BODY(name)[nameLength - 1] = 0; + type = resolveClass( + t, method->class_()->loader(), RUNTIME_ARRAY_BODY(name)); + } break; + + default: + abort(); + } + + object arg = objectArrayBody(t, args, i++); + if ((arg == 0 and (not objectType)) + or (arg and (not instanceOf(t, type, arg)))) { + if (false) { + fprintf(stderr, + "%s is not a %s\n", + arg ? objectClass(t, arg)->name()->body().begin() + : reinterpret_cast(""), + type->name()->body().begin()); + } + + throwNew(t, GcIllegalArgumentException::Type); + } + } + } + + initClass(t, method->class_()); + + unsigned returnCode = method->returnCode(); + + THREAD_RESOURCE0(t, { + if (t->exception) { + t->exception = makeThrowable( + t, GcInvocationTargetException::Type, 0, 0, t->exception); + + t->exception->as(t) + ->setTarget(t, t->exception->cause()); + } + }); + + object result; + if (args) { + result = t->m->processor->invokeArray(t, method, instance, args); + } else { + result = t->m->processor->invoke(t, method, instance); + } + + return translateInvokeResult(t, returnCode, result); +} + +// only safe to call during bootstrap when there's only one thread +// running: +void intercept(Thread* t, + GcClass* c, + const char* name, + const char* spec, + void* function, + bool updateRuntimeData) +{ + GcMethod* m = findMethodOrNull(t, c, name, spec); + if (m) { + PROTECT(t, m); + + if (updateRuntimeData) { + GcMethod* clone = methodClone(t, m); + + m->flags() |= ACC_NATIVE; + + // make clone private to prevent vtable updates at compilation + // time. Otherwise, our interception might be bypassed by calls + // through the vtable. + clone->flags() |= ACC_PRIVATE; + + GcNativeIntercept* native = makeNativeIntercept(t, function, true, clone); + + PROTECT(t, native); + + GcMethodRuntimeData* runtimeData = getMethodRuntimeData(t, m); + + runtimeData->setNative(t, native->as(t)); + } else { + m->flags() |= ACC_NATIVE; + } + } else { + // If we can't find the method, just ignore it, since ProGuard may + // have stripped it out as unused. Otherwise, the code below can + // be enabled for debugging purposes. + + if (false) { + fprintf(stderr, + "unable to find %s%s in %s\n", + name, + spec, + c->name()->body().begin()); + + abort(t); + } + } +} + +Finder* getFinder(Thread* t, const char* name, size_t nameLength) +{ + ACQUIRE(t, t->m->referenceLock); + + for (GcFinder* p = roots(t)->virtualFileFinders(); p; p = p->next()) { + if (p->name()->length() == nameLength + and strncmp(reinterpret_cast(p->name()->body().begin()), + name, + nameLength)) { + return static_cast(p->finder()); + } + } + + GcByteArray* n = makeByteArray(t, nameLength + 1); + memcpy(n->body().begin(), name, nameLength); + + void* p = t->m->libraries->resolve( + reinterpret_cast(n->body().begin())); + + if (p) { + uint8_t* (*function)(size_t*); + memcpy(&function, &p, BytesPerWord); + + size_t size = 0; + uint8_t* data = function(&size); + if (data) { + Finder* f = makeFinder(t->m->system, t->m->heap, data, size); + GcFinder* finder = makeFinder(t, f, n, roots(t)->virtualFileFinders()); + + roots(t)->setVirtualFileFinders(t, finder); + + return f; + } + } + + return 0; +} + +object getDeclaredClasses(Thread* t, GcClass* c, bool publicOnly) +{ + GcClassAddendum* addendum = c->addendum(); + if (addendum) { + GcArray* table = cast(t, addendum->innerClassTable()); + if (table) { + PROTECT(t, table); + + unsigned count = 0; + for (unsigned i = 0; i < table->length(); ++i) { + GcInnerClassReference* reference + = cast(t, table->body()[i]); + GcByteArray* outer = reference->outer(); + if (outer and byteArrayEqual(t, outer, c->name()) + and ((not publicOnly) or (reference->flags() & ACC_PUBLIC))) { + ++count; + } + } + + object result = makeObjectArray(t, type(t, GcJclass::Type), count); + PROTECT(t, result); + + for (unsigned i = 0; i < table->length(); ++i) { + GcInnerClassReference* reference + = cast(t, table->body()[i]); + GcByteArray* outer = reference->outer(); + if (outer and byteArrayEqual(t, outer, c->name()) + and ((not publicOnly) or (reference->flags() & ACC_PUBLIC))) { + object inner + = getJClass(t, resolveClass(t, c->loader(), reference->inner())); + + --count; + reinterpret_cast(result)->setBodyElement(t, count, inner); + } + } + + return result; + } + } + + return makeObjectArray(t, type(t, GcJclass::Type), 0); +} + +unsigned classModifiers(Thread* t, GcClass* c) +{ + GcClassAddendum* addendum = c->addendum(); + if (addendum) { + GcArray* table = cast(t, addendum->innerClassTable()); + if (table) { + for (unsigned i = 0; i < table->length(); ++i) { + GcInnerClassReference* reference + = cast(t, table->body()[i]); + if (0 == strcmp(c->name()->body().begin(), + reference->inner()->body().begin())) { + return reference->flags(); + } + } + } + } + + return c->flags(); +} + +object makeMethod(Thread* t, GcJclass* class_, int index) +{ + GcMethod* method = cast( + t, cast(t, class_->vmClass()->methodTable())->body()[index]); + PROTECT(t, method); + + GcClass* c + = resolveClass(t, roots(t)->bootLoader(), "java/lang/reflect/Method"); + PROTECT(t, c); + + object instance = makeNew(t, c); + PROTECT(t, instance); + + GcMethod* constructor = resolveMethod(t, c, "", "(Lavian/VMMethod;)V"); + + t->m->processor->invoke(t, constructor, instance, method); + + if (method->name()->body()[0] == '<') { + object oldInstance = instance; + + c = resolveClass( + t, roots(t)->bootLoader(), "java/lang/reflect/Constructor"); + + object instance = makeNew(t, c); + + GcMethod* constructor + = resolveMethod(t, c, "", "(Ljava/lang/Method;)V"); + + t->m->processor->invoke(t, constructor, instance, oldInstance); + } + + return instance; +} + +int64_t getPrimitive(Thread* t, object instance, int code, int offset) +{ + switch (code) { + case ByteField: + return fieldAtOffset(instance, offset); + case BooleanField: + return fieldAtOffset(instance, offset); + case CharField: + return fieldAtOffset(instance, offset); + case ShortField: + return fieldAtOffset(instance, offset); + case IntField: + return fieldAtOffset(instance, offset); + case LongField: + return fieldAtOffset(instance, offset); + case FloatField: + return fieldAtOffset(instance, offset); + case DoubleField: + return fieldAtOffset(instance, offset); + default: + abort(t); + } +} + +void setPrimitive(Thread* t, + object instance, + int code, + int offset, + int64_t value) +{ + switch (code) { + case ByteField: + fieldAtOffset(instance, offset) = static_cast(value); + break; + case BooleanField: + fieldAtOffset(instance, offset) = static_cast(value); + break; + case CharField: + fieldAtOffset(instance, offset) = static_cast(value); + break; + case ShortField: + fieldAtOffset(instance, offset) = static_cast(value); + break; + case IntField: + fieldAtOffset(instance, offset) = static_cast(value); + break; + case LongField: + fieldAtOffset(instance, offset) = static_cast(value); + break; + case FloatField: + fieldAtOffset(instance, offset) = static_cast(value); + break; + case DoubleField: + fieldAtOffset(instance, offset) = static_cast(value); + break; + default: + abort(t); + } +} + +int64_t invokeMethod(Thread* t, GcMethod* method, object instance, object args) +{ + THREAD_RESOURCE0(t, { + if (t->exception) { + GcThrowable* exception = t->exception; + t->exception = makeThrowable( + t, GcInvocationTargetException::Type, 0, 0, exception); + } + }); + + unsigned returnCode = method->returnCode(); + + return reinterpret_cast(translateInvokeResult( + t, returnCode, t->m->processor->invokeArray(t, method, instance, args))); +} + +} // namespace vm + +#endif // CLASSPATH_COMMON_H diff --git a/sgx-jvm/avian/src/avian/common.h b/sgx-jvm/avian/src/avian/common.h new file mode 100644 index 0000000000..fc837d6e88 --- /dev/null +++ b/sgx-jvm/avian/src/avian/common.h @@ -0,0 +1,497 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_COMMON_H +#define AVIAN_COMMON_H + +#ifndef __STDC_CONSTANT_MACROS +#define __STDC_CONSTANT_MACROS +#endif + +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include "avian/types.h" +#include + +#ifdef UNUSED +#undef UNUSED +#endif + +#ifdef _MSC_VER + +#include +#include + +#ifdef linux +#undef linux +#endif + +// don't complain about using 'this' in member initializers: +#pragma warning(disable : 4355) + +#define strncasecmp _strnicmp + +#define FP_UNDEF 2 + +#define not ! +#define or || +#define and && +#define xor ^ + +#define LIKELY(v) v +#define UNLIKELY(v) v + +#define UNUSED + +#define NO_RETURN __declspec(noreturn) + +#define PACKED + +#define PLATFORM_WINDOWS + +#ifdef _M_IX86 +typedef int32_t intptr_t; +typedef uint32_t uintptr_t; +#define ARCH_x86_32 +#define BYTES_PER_WORD 4 +#elif defined _M_X64 +typedef int64_t intptr_t; +typedef uint64_t uintptr_t; +#define ARCH_x86_64 +#define BYTES_PER_WORD 8 +#elif defined _M_ARM_FP +typedef int32_t intptr_t; +typedef uint32_t uintptr_t; +#define ARCH_arm +#define BYTES_PER_WORD 4 +#else +#error "unsupported architecture" +#endif + +namespace vm { + +typedef intptr_t intptr_alias_t; + +} // namespace vm + +#else // not _MSC_VER + +#include + +#define BYTES_PER_WORD __SIZEOF_POINTER__ + +#define LIKELY(v) __builtin_expect((v) != 0, true) +#define UNLIKELY(v) __builtin_expect((v) != 0, false) + +#define UNUSED __attribute__((unused)) + +#define NO_RETURN __attribute__((noreturn)) + +#define PACKED __attribute__((packed)) + +#ifdef __MINGW32__ +#define PLATFORM_WINDOWS +#endif + +#ifdef __i386__ +#define ARCH_x86_32 +#elif defined __x86_64__ +#define ARCH_x86_64 +#elif defined __arm__ +#define ARCH_arm +#elif defined __aarch64__ +#define ARCH_arm64 +#else +#error "unsupported architecture" +#endif + +namespace vm { + +typedef intptr_t __attribute__((__may_alias__)) intptr_alias_t; + +} // namespace vm + +#endif // not _MSC_VER + +#ifdef PLATFORM_WINDOWS +#define AVIAN_EXPORT __declspec(dllexport) +#define PATH_SEPARATOR ';' +#else // not PLATFORM_WINDOWS +#define AVIAN_EXPORT \ + __attribute__((visibility("default"))) __attribute__((used)) +#define PATH_SEPARATOR ':' +#endif // not PLATFORM_WINDOWS + +#ifdef PRId64 +#define LLD PRId64 +#define ULD PRIu64 +#define LD PRIdPTR +#define LX PRIxPTR +#else +#if (defined ARCH_x86_32) || (defined ARCH_arm) +#define LD "ld" +#if (defined _MSC_VER) || ((defined __MINGW32__) && __GNUC__ >= 4) +#if (__GNUC__ == 4 && __GNUC_MINOR__ <= 8) +#define LLD "I64d" +#else +#define LLD "lld" +#endif +#else +#define LLD "lld" +#endif +#ifdef __APPLE__ +#define ULD "lu" +#define LX "lx" +#else +#define LX "x" +#define ULD "u" +#endif +#elif defined ARCH_x86_64 +#define LD "ld" +#define LX "lx" +#if (defined _MSC_VER) || (defined __MINGW32__) +#if (__GNUC__ == 4 && __GNUC_MINOR__ <= 8) +#define LLD "I64d" +#define ULD "I64x" +#else +#define LLD "lld" +#define ULD "llu" +#endif +#else +#ifdef __APPLE__ +#define LLD "lld" +#else +#define LLD "ld" +#endif +#define ULD "lu" +#endif +#else +#error "Unsupported architecture" +#endif +#endif + +#ifdef PLATFORM_WINDOWS +#define SO_PREFIX "" +#else +#define SO_PREFIX "lib" +#endif + +#ifdef __APPLE__ +#define SO_SUFFIX ".dylib" +#elif defined PLATFORM_WINDOWS +#define SO_SUFFIX ".dll" +#else +#define SO_SUFFIX ".so" +#endif + +#define MACRO_XY(X, Y) X##Y +#define MACRO_MakeNameXY(FX, LINE) MACRO_XY(FX, LINE) +#define MAKE_NAME(FX) MACRO_MakeNameXY(FX, __LINE__) + +#define RESOURCE(type, name, release) \ + class MAKE_NAME(Resource_) { \ + public: \ + MAKE_NAME(Resource_)(type name) : name(name) \ + { \ + } \ + ~MAKE_NAME(Resource_)() \ + { \ + release; \ + } \ + \ + private: \ + type name; \ + } MAKE_NAME(resource_)(name); + +#ifdef _MSC_VER +#pragma warning(disable : 4291) +#endif + +namespace vm { + +inline intptr_alias_t& alias(void* p, unsigned offset) +{ + return *reinterpret_cast(static_cast(p) + offset); +} + +#ifdef _MSC_VER + +inline int vsnprintf(char* dst, size_t size, const char* format, va_list a) +{ + return vsnprintf_s(dst, size, _TRUNCATE, format, a); +} + +inline int snprintf(char* dst, size_t size, const char* format, ...) +{ + va_list a; + va_start(a, format); + int r = vsnprintf(dst, size, format, a); + va_end(a); + return r; +} + +inline FILE* fopen(const char* name, const char* mode) +{ + FILE* file; + if (fopen_s(&file, name, mode) == 0) { + return file; + } else { + return 0; + } +} + +#else // not _MSC_VER + +inline int vsnprintf(char* dst, size_t size, const char* format, va_list a) +{ + return ::vsnprintf(dst, size, format, a); +} + +inline int snprintf(char* dst, size_t size, const char* format, ...) +{ + va_list a; + va_start(a, format); + int r = vsnprintf(dst, size, format, a); + va_end(a); + return r; +} + +inline FILE* fopen(const char* name, const char* mode) +{ + return ::fopen(name, mode); +} + +#endif // not _MSC_VER + +const unsigned BytesPerWord = sizeof(uintptr_t); +const unsigned BitsPerWord = BytesPerWord * 8; + +const uintptr_t PointerMask = ((~static_cast(0)) / BytesPerWord) + * BytesPerWord; + +const unsigned LikelyPageSizeInBytes = 4 * 1024; + +inline unsigned pad(unsigned n, unsigned alignment) +{ + return (n + (alignment - 1)) & ~(alignment - 1); +} + +inline unsigned pad(unsigned n) +{ + return pad(n, BytesPerWord); +} + +inline uintptr_t padWord(uintptr_t n, uintptr_t alignment) +{ + return (n + (alignment - 1)) & ~(alignment - 1); +} + +inline uintptr_t padWord(uintptr_t n) +{ + return padWord(n, BytesPerWord); +} + +inline bool fitsInInt8(int64_t v) +{ + return v == static_cast(v); +} + +inline bool fitsInInt16(int64_t v) +{ + return v == static_cast(v); +} + +inline bool fitsInInt32(int64_t v) +{ + return v == static_cast(v); +} +template +inline unsigned wordOf(unsigned i) +{ + return i / (sizeof(T) * 8); +} + +inline unsigned wordOf(unsigned i) +{ + return wordOf(i); +} + +template +inline unsigned bitOf(unsigned i) +{ + return i % (sizeof(T) * 8); +} + +inline unsigned bitOf(unsigned i) +{ + return bitOf(i); +} + +template +inline unsigned indexOf(unsigned word, unsigned bit) +{ + return (word * (sizeof(T) * 8)) + bit; +} + +inline unsigned indexOf(unsigned word, unsigned bit) +{ + return indexOf(word, bit); +} + +template +inline void markBit(T* map, unsigned i) +{ + map[wordOf(i)] |= static_cast(1) << bitOf(i); +} + +template +inline void clearBit(T* map, unsigned i) +{ + map[wordOf(i)] &= ~(static_cast(1) << bitOf(i)); +} + +template +inline unsigned getBit(T* map, unsigned i) +{ + return (map[wordOf(i)] & (static_cast(1) << bitOf(i))) + >> bitOf(i); +} + +// todo: the following (clearBits, setBits, and getBits) could be made +// more efficient by operating on a word at a time instead of a bit at +// a time: + +template +inline void clearBits(T* map, unsigned bitsPerRecord, unsigned index) +{ + for (unsigned i = index, limit = index + bitsPerRecord; i < limit; ++i) { + clearBit(map, i); + } +} + +template +inline void setBits(T* map, unsigned bitsPerRecord, int index, unsigned v) +{ + for (int i = index + bitsPerRecord - 1; i >= index; --i) { + if (v & 1) + markBit(map, i); + else + clearBit(map, i); + v >>= 1; + } +} + +template +inline unsigned getBits(T* map, unsigned bitsPerRecord, unsigned index) +{ + unsigned v = 0; + for (unsigned i = index, limit = index + bitsPerRecord; i < limit; ++i) { + v <<= 1; + v |= getBit(map, i); + } + return v; +} + +template +inline T& fieldAtOffset(void* p, unsigned offset) +{ + return *reinterpret_cast(static_cast(p) + offset); +} + +template +inline T* maskAlignedPointer(T* p) +{ + return reinterpret_cast(reinterpret_cast(p) & PointerMask); +} + +inline void write4(uint8_t* dst, uint32_t v) +{ + memcpy(dst, &v, 4); +} + +inline uint32_t floatToBits(float f) +{ + uint32_t bits; + memcpy(&bits, &f, 4); + return bits; +} + +inline uint64_t doubleToBits(double d) +{ + uint64_t bits; + memcpy(&bits, &d, 8); + return bits; +} + +inline double bitsToDouble(uint64_t bits) +{ + double d; + memcpy(&d, &bits, 8); + return d; +} + +inline float bitsToFloat(uint32_t bits) +{ + float f; + memcpy(&f, &bits, 4); + return f; +} + +inline int difference(void* a, void* b) +{ + return reinterpret_cast(a) - reinterpret_cast(b); +} + +template +inline void* voidPointer(T function) +{ + void* p; + memcpy(&p, &function, sizeof(void*)); + return p; +} + +inline void replace(char a, char b, char* c) +{ + for (; *c; ++c) + if (*c == a) + *c = b; +} + +inline void replace(char a, char b, char* dst, const char* src) +{ + unsigned i = 0; + for (; src[i]; ++i) { + dst[i] = src[i] == a ? b : src[i]; + } + dst[i] = 0; +} + +inline bool equal(const void* a, unsigned al, const void* b, unsigned bl) +{ + if (al == bl) { + return memcmp(a, b, al) == 0; + } else { + return false; + } +} + +} // namespace vm + +#endif // AVIAN_COMMON_H diff --git a/sgx-jvm/avian/src/avian/constants.h b/sgx-jvm/avian/src/avian/constants.h new file mode 100644 index 0000000000..ebeb975af3 --- /dev/null +++ b/sgx-jvm/avian/src/avian/constants.h @@ -0,0 +1,294 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef CONSTANTS_H +#define CONSTANTS_H + +namespace vm { + +enum OpCode { + aaload = 0x32, + aastore = 0x53, + aconst_null = 0x01, + aload = 0x19, + aload_0 = 0x2a, + aload_1 = 0x2b, + aload_2 = 0x2c, + aload_3 = 0x2d, + anewarray = 0xbd, + areturn = 0xb0, + arraylength = 0xbe, + astore = 0x3a, + astore_0 = 0x4b, + astore_1 = 0x4c, + astore_2 = 0x4d, + astore_3 = 0x4e, + athrow = 0xbf, + baload = 0x33, + bastore = 0x54, + bipush = 0x10, + breakpoint = 0xca, + caload = 0x34, + castore = 0x55, + checkcast = 0xc0, + d2f = 0x90, + d2i = 0x8e, + d2l = 0x8f, + dadd = 0x63, + daload = 0x31, + dastore = 0x52, + dcmpg = 0x98, + dcmpl = 0x97, + dconst_0 = 0x0e, + dconst_1 = 0x0f, + ddiv = 0x6f, + dload = 0x18, + dload_0 = 0x26, + dload_1 = 0x27, + dload_2 = 0x28, + dload_3 = 0x29, + dmul = 0x6b, + dneg = 0x77, + drem = 0x73, + dreturn = 0xaf, + dstore = 0x39, + dstore_0 = 0x47, + dstore_1 = 0x48, + dstore_2 = 0x49, + dstore_3 = 0x4a, + dsub = 0x67, + dup = 0x59, + dup_x1 = 0x5a, + dup_x2 = 0x5b, + dup2 = 0x5c, + dup2_x1 = 0x5d, + dup2_x2 = 0x5e, + f2d = 0x8d, + f2i = 0x8b, + f2l = 0x8c, + fadd = 0x62, + faload = 0x30, + fastore = 0x51, + fcmpg = 0x96, + fcmpl = 0x95, + fconst_0 = 0x0b, + fconst_1 = 0x0c, + fconst_2 = 0x0d, + fdiv = 0x6e, + fload = 0x17, + fload_0 = 0x22, + fload_1 = 0x23, + fload_2 = 0x24, + fload_3 = 0x25, + fmul = 0x6a, + fneg = 0x76, + frem = 0x72, + freturn = 0xae, + fstore = 0x38, + fstore_0 = 0x43, + fstore_1 = 0x44, + fstore_2 = 0x45, + fstore_3 = 0x46, + fsub = 0x66, + getfield = 0xb4, + getstatic = 0xb2, + goto_ = 0xa7, + goto_w = 0xc8, + i2b = 0x91, + i2c = 0x92, + i2d = 0x87, + i2f = 0x86, + i2l = 0x85, + i2s = 0x93, + iadd = 0x60, + iaload = 0x2e, + iand = 0x7e, + iastore = 0x4f, + iconst_m1 = 0x02, + iconst_0 = 0x03, + iconst_1 = 0x04, + iconst_2 = 0x05, + iconst_3 = 0x06, + iconst_4 = 0x07, + iconst_5 = 0x08, + idiv = 0x6c, + if_acmpeq = 0xa5, + if_acmpne = 0xa6, + if_icmpeq = 0x9f, + if_icmpne = 0xa0, + if_icmplt = 0xa1, + if_icmpge = 0xa2, + if_icmpgt = 0xa3, + if_icmple = 0xa4, + ifeq = 0x99, + ifge = 0x9c, + ifgt = 0x9d, + ifle = 0x9e, + iflt = 0x9b, + ifne = 0x9a, + ifnonnull = 0xc7, + ifnull = 0xc6, + iinc = 0x84, + iload = 0x15, + iload_0 = 0x1a, + iload_1 = 0x1b, + iload_2 = 0x1c, + iload_3 = 0x1d, + impdep1 = 0xfe, + impdep2 = 0xff, + imul = 0x68, + ineg = 0x74, + instanceof = 0xc1, + invokedynamic = 0xba, + invokeinterface = 0xb9, + invokespecial = 0xb7, + invokestatic = 0xb8, + invokevirtual = 0xb6, + ior = 0x80, + irem = 0x70, + ireturn = 0xac, + ishl = 0x78, + ishr = 0x7a, + istore = 0x36, + istore_0 = 0x3b, + istore_1 = 0x3c, + istore_2 = 0x3d, + istore_3 = 0x3e, + isub = 0x64, + iushr = 0x7c, + ixor = 0x82, + jsr = 0xa8, + jsr_w = 0xc9, + l2d = 0x8a, + l2f = 0x89, + l2i = 0x88, + ladd = 0x61, + laload = 0x2f, + land = 0x7f, + lastore = 0x50, + lcmp = 0x94, + lconst_0 = 0x09, + lconst_1 = 0x0a, + ldc = 0x12, + ldc_w = 0x13, + ldc2_w = 0x14, + ldiv_ = 0x6d, + lload = 0x16, + lload_0 = 0x1e, + lload_1 = 0x1f, + lload_2 = 0x20, + lload_3 = 0x21, + lmul = 0x69, + lneg = 0x75, + lookupswitch = 0xab, + lor = 0x81, + lrem = 0x71, + lreturn = 0xad, + lshl = 0x79, + lshr = 0x7b, + lstore = 0x37, + lstore_0 = 0x3f, + lstore_1 = 0x40, + lstore_2 = 0x41, + lstore_3 = 0x42, + lsub = 0x65, + lushr = 0x7d, + lxor = 0x83, + monitorenter = 0xc2, + monitorexit = 0xc3, + multianewarray = 0xc5, + new_ = 0xbb, + newarray = 0xbc, + nop = 0x00, + pop_ = 0x57, + pop2 = 0x58, + putfield = 0xb5, + putstatic = 0xb3, + ret = 0xa9, + return_ = 0xb1, + saload = 0x35, + sastore = 0x56, + sipush = 0x11, + swap = 0x5f, + tableswitch = 0xaa, + wide = 0xc4 +}; + +enum TypeCode { + T_BOOLEAN = 4, + T_CHAR = 5, + T_FLOAT = 6, + T_DOUBLE = 7, + T_BYTE = 8, + T_SHORT = 9, + T_INT = 10, + T_LONG = 11 +}; + +enum Constant { + CONSTANT_Class = 7, + CONSTANT_Double = 6, + CONSTANT_Fieldref = 9, + CONSTANT_Float = 4, + CONSTANT_Integer = 3, + CONSTANT_InterfaceMethodref = 11, + CONSTANT_InvokeDynamic = 18, + CONSTANT_Long = 5, + CONSTANT_MethodHandle = 15, + CONSTANT_MethodType = 16, + CONSTANT_Methodref = 10, + CONSTANT_NameAndType = 12, + CONSTANT_String = 8, + CONSTANT_Utf8 = 1 +}; + +const unsigned ACC_PUBLIC = 1 << 0; +const unsigned ACC_PRIVATE = 1 << 1; +const unsigned ACC_PROTECTED = 1 << 2; +const unsigned ACC_STATIC = 1 << 3; +const unsigned ACC_FINAL = 1 << 4; +const unsigned ACC_SUPER = 1 << 5; +const unsigned ACC_SYNCHRONIZED = ACC_SUPER; +const unsigned ACC_VOLATILE = 1 << 6; +const unsigned ACC_TRANSIENT = 1 << 7; +const unsigned ACC_VARARGS = 1 << 7; +const unsigned ACC_NATIVE = 1 << 8; +const unsigned ACC_INTERFACE = 1 << 9; +const unsigned ACC_ABSTRACT = 1 << 10; +const unsigned ACC_STRICT = 1 << 11; + +const unsigned REF_getField = 1; +const unsigned REF_getStatic = 2; +const unsigned REF_putField = 3; +const unsigned REF_putStatic = 4; +const unsigned REF_invokeVirtual = 5; +const unsigned REF_invokeStatic = 6; +const unsigned REF_invokeSpecial = 7; +const unsigned REF_newInvokeSpecial = 8; +const unsigned REF_invokeInterface = 9; + +const int AVIAN_JNI_COMMIT = 1; +const int AVIAN_JNI_ABORT = 2; + +const int AVIAN_JNI_OK = 0; +const int AVIAN_JNI_ERR = -1; +const int AVIAN_JNI_EDETACHED = -2; +const int AVIAN_JNI_EVERSION = -3; +const int AVIAN_JNI_ENOMEM = -4; +const int AVIAN_JNI_EEXIST = -5; +const int AVIAN_JNI_EINVAL = -6; + +const int AVIAN_JNI_VERSION_1_1 = 0x00010001; +const int AVIAN_JNI_VERSION_1_2 = 0x00010002; +const int AVIAN_JNI_VERSION_1_4 = 0x00010004; + +} // namespace vm + +#endif // CONSTANTS_H diff --git a/sgx-jvm/avian/src/avian/embed.h b/sgx-jvm/avian/src/avian/embed.h new file mode 100644 index 0000000000..7f1f457f99 --- /dev/null +++ b/sgx-jvm/avian/src/avian/embed.h @@ -0,0 +1,17 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef EMBED_H +#define EMBED_H + +#define RESID_MAIN_CLASS 100 +#define RESID_BOOT_JAR L"BOOT.JAR" + +#endif diff --git a/sgx-jvm/avian/src/avian/environment.h b/sgx-jvm/avian/src/avian/environment.h new file mode 100644 index 0000000000..a4eba6dbb5 --- /dev/null +++ b/sgx-jvm/avian/src/avian/environment.h @@ -0,0 +1,33 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_ENVIRONMENT_H +#define AVIAN_ENVIRONMENT_H + +#ifndef AVIAN_TARGET_FORMAT +#error build system should have defined AVIAN_TARGET_FORMAT +#endif + +#ifndef AVIAN_TARGET_ARCH +#error build system should have defined AVIAN_TARGET_ARCH +#endif + +#define AVIAN_FORMAT_UNKNOWN 0 +#define AVIAN_FORMAT_ELF 1 +#define AVIAN_FORMAT_PE 2 +#define AVIAN_FORMAT_MACHO 3 + +#define AVIAN_ARCH_UNKNOWN 0 +#define AVIAN_ARCH_X86 (1 << 8) +#define AVIAN_ARCH_X86_64 (2 << 8) +#define AVIAN_ARCH_ARM (3 << 8) +#define AVIAN_ARCH_ARM64 (4 << 8) + +#endif diff --git a/sgx-jvm/avian/src/avian/finder.h b/sgx-jvm/avian/src/avian/finder.h new file mode 100644 index 0000000000..133f1127ee --- /dev/null +++ b/sgx-jvm/avian/src/avian/finder.h @@ -0,0 +1,216 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef FINDER_H +#define FINDER_H + +#include "avian/common.h" +#include + +namespace avian { +namespace util { +class Alloc; +} +} + +namespace vm { + +const unsigned LocalHeaderSize = 30; +const unsigned HeaderSize = 46; + +const unsigned CentralDirectorySignature = 0x06054b50; +const unsigned EntrySignature = 0x02014b50; + +const unsigned CentralDirectorySearchStart = 22; + +inline uint16_t get2(const uint8_t* p) +{ + return (static_cast(p[1]) << 8) | (static_cast(p[0])); +} + +inline uint32_t get4(const uint8_t* p) +{ + return (static_cast(p[3]) << 24) + | (static_cast(p[2]) << 16) + | (static_cast(p[1]) << 8) | (static_cast(p[0])); +} + +inline uint32_t signature(const uint8_t* p) +{ + return get4(p); +} + +inline uint16_t compressionMethod(const uint8_t* centralHeader) +{ + return get2(centralHeader + 10); +} + +inline uint32_t fileTime(const uint8_t* centralHeader) +{ + return get4(centralHeader + 12); +} + +inline uint32_t fileCRC(const uint8_t* centralHeader) +{ + return get4(centralHeader + 16); +} + +inline uint32_t compressedSize(const uint8_t* centralHeader) +{ + return get4(centralHeader + 20); +} + +inline uint32_t uncompressedSize(const uint8_t* centralHeader) +{ + return get4(centralHeader + 24); +} + +inline uint16_t fileNameLength(const uint8_t* centralHeader) +{ + return get2(centralHeader + 28); +} + +inline uint16_t extraFieldLength(const uint8_t* centralHeader) +{ + return get2(centralHeader + 30); +} + +inline uint16_t commentFieldLength(const uint8_t* centralHeader) +{ + return get2(centralHeader + 32); +} + +inline uint32_t localHeaderOffset(const uint8_t* centralHeader) +{ + return get4(centralHeader + 42); +} + +inline uint16_t localFileNameLength(const uint8_t* localHeader) +{ + return get2(localHeader + 26); +} + +inline uint16_t localExtraFieldLength(const uint8_t* localHeader) +{ + return get2(localHeader + 28); +} + +inline uint32_t centralDirectoryOffset(const uint8_t* centralHeader) +{ + return get4(centralHeader + 16); +} + +inline const uint8_t* fileName(const uint8_t* centralHeader) +{ + return centralHeader + 46; +} + +inline const uint8_t* fileData(const uint8_t* localHeader) +{ + return localHeader + LocalHeaderSize + localFileNameLength(localHeader) + + localExtraFieldLength(localHeader); +} + +inline const uint8_t* endOfEntry(const uint8_t* p) +{ + return p + HeaderSize + fileNameLength(p) + extraFieldLength(p) + + commentFieldLength(p); +} + +inline bool readLine(const uint8_t* base, + unsigned total, + size_t* start, + size_t* length) +{ + const uint8_t* p = base + *start; + const uint8_t* end = base + total; + while (p != end and (*p == '\n' or *p == '\r')) + ++p; + + *start = p - base; + while (p != end and not(*p == '\n' or *p == '\r')) + ++p; + + *length = (p - base) - *start; + + return *length != 0; +} + +class Finder { + public: + class IteratorImp { + public: + virtual const char* next(size_t* size) = 0; + virtual void dispose() = 0; + }; + + class Iterator { + public: + Iterator(Finder* finder) + : it(finder->iterator()), current(it->next(¤tSize)) + { + } + + ~Iterator() + { + it->dispose(); + } + + bool hasMore() + { + if (current) + return true; + current = it->next(¤tSize); + return current != 0; + } + + const char* next(size_t* size) + { + if (hasMore()) { + *size = currentSize; + const char* v = current; + current = 0; + return v; + } else { + return 0; + } + } + + IteratorImp* it; + const char* current; + size_t currentSize; + }; + + virtual IteratorImp* iterator() = 0; + virtual System::Region* find(const char* name) = 0; + virtual System::FileType stat(const char* name, + size_t* length, + bool tryDirectory = false) = 0; + virtual const char* urlPrefix(const char* name) = 0; + virtual const char* nextUrlPrefix(const char* name, void*& finderElementPtr) + = 0; + virtual const char* sourceUrl(const char* name) = 0; + virtual const char* path() = 0; + virtual void dispose() = 0; +}; + +AVIAN_EXPORT Finder* makeFinder(System* s, + avian::util::Alloc* a, + const char* path, + const char* bootLibrary); + +Finder* makeFinder(System* s, + avian::util::Alloc* a, + const uint8_t* jarData, + size_t jarLength); + +} // namespace vm + +#endif // FINDER_H diff --git a/sgx-jvm/avian/src/avian/heapwalk.h b/sgx-jvm/avian/src/avian/heapwalk.h new file mode 100644 index 0000000000..32bf0104aa --- /dev/null +++ b/sgx-jvm/avian/src/avian/heapwalk.h @@ -0,0 +1,49 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef HEAPWALK_H +#define HEAPWALK_H + +#include "avian/common.h" +#include "java-common.h" + +namespace vm { + +class Thread; + +class HeapMap { + public: + virtual int find(object value) = 0; + virtual void dispose() = 0; +}; + +class HeapVisitor { + public: + virtual void root() = 0; + virtual unsigned visitNew(object value) = 0; + virtual void visitOld(object value, unsigned number) = 0; + virtual void push(object parent, unsigned parentNumber, unsigned childOffset) + = 0; + virtual void pop() = 0; +}; + +class HeapWalker { + public: + virtual unsigned visitRoot(object root) = 0; + virtual void visitAllRoots() = 0; + virtual HeapMap* map() = 0; + virtual void dispose() = 0; +}; + +HeapWalker* makeHeapWalker(Thread* t, HeapVisitor* v); + +} // namespace vm + +#endif // HEAPWALK_H diff --git a/sgx-jvm/avian/src/avian/java-common.h b/sgx-jvm/avian/src/avian/java-common.h new file mode 100644 index 0000000000..52b8c9d499 --- /dev/null +++ b/sgx-jvm/avian/src/avian/java-common.h @@ -0,0 +1,83 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef JAVA_COMMON_H +#define JAVA_COMMON_H + +namespace vm { + +class Machine; +class Thread; + +class GcObject; +; + +typedef GcObject* object; + +typedef uint8_t jboolean; +typedef int8_t jbyte; +typedef uint16_t jchar; +typedef int16_t jshort; +typedef int32_t jint; +typedef int64_t jlong; +typedef float jfloat; +typedef double jdouble; + +typedef jint jsize; + +typedef object* jobject; + +class GcString; +class GcJclass; +class GcThrowable; +class GcBooleanArray; +class GcByteArray; +class GcCharArray; +class GcShortArray; +class GcIntArray; +class GcLongArray; +class GcFloatArray; +class GcDoubleArray; +class GcObjectArray; + +typedef GcJclass** jclass; +typedef GcThrowable** jthrowable; +typedef GcString** jstring; +typedef jobject jweak; + +typedef jobject jarray; +typedef GcBooleanArray** jbooleanArray; +typedef GcByteArray** jbyteArray; +typedef GcCharArray** jcharArray; +typedef GcShortArray** jshortArray; +typedef GcIntArray** jintArray; +typedef GcLongArray** jlongArray; +typedef GcFloatArray** jfloatArray; +typedef GcDoubleArray** jdoubleArray; +typedef GcObjectArray** jobjectArray; + +typedef uintptr_t jfieldID; +typedef uintptr_t jmethodID; + +union jvalue { + jboolean z; + jbyte b; + jchar c; + jshort s; + jint i; + jlong j; + jfloat f; + jdouble d; + jobject l; +}; + +} // namespace vm + +#endif // JAVA_COMMON_H diff --git a/sgx-jvm/avian/src/avian/jnienv.h b/sgx-jvm/avian/src/avian/jnienv.h new file mode 100644 index 0000000000..9947ade6ec --- /dev/null +++ b/sgx-jvm/avian/src/avian/jnienv.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef JNIENV_H +#define JNIENV_H + +#include "avian/machine.h" + +#define BOOTSTRAP_PROPERTY "avian.bootstrap" +#define JAVA_COMMAND_PROPERTY "sun.java.command" +#define JAVA_LAUNCHER_PROPERTY "sun.java.launcher" +#define CRASHDIR_PROPERTY "avian.crash.dir" +#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" + +namespace vm { + +void populateJNITables(JavaVMVTable* vmTable, JNIEnvVTable* envTable); + +} // namespace vm + +#endif // JNIENV_H diff --git a/sgx-jvm/avian/src/avian/lzma-util.h b/sgx-jvm/avian/src/avian/lzma-util.h new file mode 100644 index 0000000000..6837f79dd9 --- /dev/null +++ b/sgx-jvm/avian/src/avian/lzma-util.h @@ -0,0 +1,56 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef LZMA_UTIL_H +#define LZMA_UTIL_H + +#include +#include +#include +#include + +namespace vm { + +const unsigned Padding = 16; + +class LzmaAllocator { + public: + LzmaAllocator(avian::util::Alloc* a) : a(a) + { + allocator.Alloc = allocate; + allocator.Free = free; + } + + ISzAlloc allocator; + avian::util::Alloc* a; + + static void* allocate(void* allocator, size_t size) + { + uint8_t* p = static_cast( + static_cast(allocator)->a->allocate(size + Padding)); + int32_t size32 = size; + memcpy(p, &size32, 4); + return p + Padding; + } + + static void free(void* allocator, void* address) + { + if (address) { + void* p = static_cast(address) - Padding; + int32_t size32; + memcpy(&size32, p, 4); + static_cast(allocator)->a->free(p, size32 + Padding); + } + } +}; + +} // namespace vm + +#endif // LZMA_UTIL_H diff --git a/sgx-jvm/avian/src/avian/lzma.h b/sgx-jvm/avian/src/avian/lzma.h new file mode 100644 index 0000000000..a9c6baa545 --- /dev/null +++ b/sgx-jvm/avian/src/avian/lzma.h @@ -0,0 +1,38 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef LZMA_H +#define LZMA_H + +#include + +namespace avian { +namespace util { +class AllocOnly; +} +} + +namespace vm { + +uint8_t* decodeLZMA(System* s, + avian::util::Alloc* a, + uint8_t* in, + size_t inSize, + size_t* outSize); + +uint8_t* encodeLZMA(System* s, + avian::util::Alloc* a, + uint8_t* in, + size_t inSize, + size_t* outSize); + +} // namespace vm + +#endif // LZMA_H diff --git a/sgx-jvm/avian/src/avian/machine.h b/sgx-jvm/avian/src/avian/machine.h new file mode 100644 index 0000000000..8b59bb85a2 --- /dev/null +++ b/sgx-jvm/avian/src/avian/machine.h @@ -0,0 +1,3868 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef MACHINE_H +#define MACHINE_H + +#include "avian/common.h" +#include "java-common.h" +#include +#include +#include +#include +#include "avian/finder.h" +#include "avian/processor.h" +#include "avian/constants.h" +#include "avian/arch.h" + +using namespace avian::util; + +#ifdef PLATFORM_WINDOWS +#define JNICALL __stdcall +#else +#define JNICALL +#endif + +#define PROTECT(thread, name) \ + Thread::SingleProtector MAKE_NAME(protector_)(thread, &name); + +#define ACQUIRE(t, x) MonitorResource MAKE_NAME(monitorResource_)(t, x) + +#define ACQUIRE_OBJECT(t, x) \ + ObjectMonitorResource MAKE_NAME(monitorResource_)(t, x) + +#define ACQUIRE_FIELD_FOR_READ(t, field) \ + FieldReadResource MAKE_NAME(monitorResource_)(t, field) + +#define ACQUIRE_FIELD_FOR_WRITE(t, field) \ + FieldWriteResource MAKE_NAME(monitorResource_)(t, field) + +#define ACQUIRE_RAW(t, x) RawMonitorResource MAKE_NAME(monitorResource_)(t, x) + +#define ENTER(t, state) StateResource MAKE_NAME(stateResource_)(t, state) + +#define THREAD_RESOURCE0(t, releaseBody) \ + class MAKE_NAME(Resource_) : public Thread::AutoResource { \ + public: \ + MAKE_NAME(Resource_)(Thread * t) : AutoResource(t) \ + { \ + } \ + ~MAKE_NAME(Resource_)() \ + { \ + releaseBody; \ + } \ + virtual void release() \ + { \ + this->MAKE_NAME(Resource_)::~MAKE_NAME(Resource_)(); \ + } \ + } MAKE_NAME(resource_)(t); + +#define OBJECT_RESOURCE(t, name, releaseBody) \ + class MAKE_NAME(Resource_) : public Thread::AutoResource { \ + public: \ + MAKE_NAME(Resource_)(Thread * t, object name) \ + : AutoResource(t), name(name), protector(t, &(this->name)) \ + { \ + } \ + ~MAKE_NAME(Resource_)() \ + { \ + releaseBody; \ + } \ + virtual void release() \ + { \ + this->MAKE_NAME(Resource_)::~MAKE_NAME(Resource_)(); \ + } \ + \ + private: \ + object name; \ + Thread::SingleProtector protector; \ + } MAKE_NAME(resource_)(t, name); + +#define THREAD_RESOURCE(t, type, name, releaseBody) \ + class MAKE_NAME(Resource_) : public Thread::AutoResource { \ + public: \ + MAKE_NAME(Resource_)(Thread * t, type name) : AutoResource(t), name(name) \ + { \ + } \ + ~MAKE_NAME(Resource_)() \ + { \ + releaseBody; \ + } \ + virtual void release() \ + { \ + this->MAKE_NAME(Resource_)::~MAKE_NAME(Resource_)(); \ + } \ + \ + private: \ + type name; \ + } MAKE_NAME(resource_)(t, name); + +#define THREAD_RESOURCE2(t, type1, name1, type2, name2, releaseBody) \ + class MAKE_NAME(Resource_) : public Thread::AutoResource { \ + public: \ + MAKE_NAME(Resource_)(Thread * t, type1 name1, type2 name2) \ + : AutoResource(t), name1(name1), name2(name2) \ + { \ + } \ + ~MAKE_NAME(Resource_)() \ + { \ + releaseBody; \ + } \ + virtual void release() \ + { \ + this->MAKE_NAME(Resource_)::~MAKE_NAME(Resource_)(); \ + } \ + \ + private: \ + type1 name1; \ + type2 name2; \ + } MAKE_NAME(resource_)(t, name1, name2); + +AVIAN_EXPORT void vmPrintTrace(vm::Thread* t); + +AVIAN_EXPORT void vmfPrintTrace(vm::Thread* t, FILE* out); + +namespace vm { + +const bool Verbose = false; +const bool DebugRun = false; +const bool DebugCalls = false; +const bool DebugStack = false; +const bool DebugMonitors = false; +const bool DebugReferences = false; + +const bool AbortOnOutOfMemoryError = false; + +const uintptr_t HashTakenMark = 1; +const uintptr_t ExtendedMark = 2; +const uintptr_t FixedMark = 3; + +const unsigned ThreadHeapSizeInBytes = 64 * 1024; +const unsigned ThreadHeapSizeInWords = ThreadHeapSizeInBytes / BytesPerWord; + +const unsigned ThreadBackupHeapSizeInBytes = 2 * 1024; +const unsigned ThreadBackupHeapSizeInWords = ThreadBackupHeapSizeInBytes + / BytesPerWord; + +const unsigned ThreadHeapPoolSize = 64; + +const unsigned FixedFootprintThresholdInBytes = ThreadHeapPoolSize + * ThreadHeapSizeInBytes; + +// number of zombie threads which may accumulate before we force a GC +// to clean them up: +const unsigned ZombieCollectionThreshold = 16; + +enum FieldCode { + VoidField, + ByteField, + CharField, + DoubleField, + FloatField, + IntField, + LongField, + ShortField, + BooleanField, + ObjectField +}; + +enum StackTag { + IntTag, // must be zero + ObjectTag +}; + +const int NativeLine = -2; +const int UnknownLine = -1; + +// class vmFlags: +const unsigned ReferenceFlag = 1 << 0; +const unsigned WeakReferenceFlag = 1 << 1; +const unsigned NeedInitFlag = 1 << 2; +const unsigned InitFlag = 1 << 3; +const unsigned InitErrorFlag = 1 << 4; +const unsigned PrimitiveFlag = 1 << 5; +const unsigned BootstrapFlag = 1 << 6; +const unsigned HasFinalizerFlag = 1 << 7; +const unsigned LinkFlag = 1 << 8; +const unsigned HasFinalMemberFlag = 1 << 9; +const unsigned SingletonFlag = 1 << 10; +const unsigned ContinuationFlag = 1 << 11; + +// method vmFlags: +const unsigned ClassInitFlag = 1 << 0; +const unsigned ConstructorFlag = 1 << 1; + +#ifndef JNI_VERSION_1_6 +#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; + +struct JNINativeMethod { + char* name; + char* signature; + void* function; +}; + +struct JavaVMOption { + char* optionString; + void* extraInfo; +}; + +struct JavaVMInitArgs { + jint version; + + jint nOptions; + JavaVMOption* options; + jboolean ignoreUnrecognized; +}; + +struct JavaVMVTable { + void* reserved0; + void* reserved1; + void* reserved2; + + jint(JNICALL* DestroyJavaVM)(JavaVM*); + + jint(JNICALL* AttachCurrentThread)(JavaVM*, JNIEnv**, void*); + + jint(JNICALL* DetachCurrentThread)(JavaVM*); + + jint(JNICALL* GetEnv)(JavaVM*, JNIEnv**, jint); + + jint(JNICALL* AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv**, void*); +}; + +struct JNIEnvVTable { + void* reserved0; + void* reserved1; + void* reserved2; + void* reserved3; + + jint(JNICALL* GetVersion)(JNIEnv*); + + jclass( + JNICALL* DefineClass)(JNIEnv*, const char*, jobject, const jbyte*, jsize); + + jclass(JNICALL* FindClass)(JNIEnv*, const char*); + + jmethodID(JNICALL* FromReflectedMethod)(JNIEnv*, jobject); + + jfieldID(JNICALL* FromReflectedField)(JNIEnv*, jobject); + + jobject(JNICALL* ToReflectedMethod)(JNIEnv*, jclass, jmethodID, jboolean); + + jclass(JNICALL* GetSuperclass)(JNIEnv*, jclass); + + jboolean(JNICALL* IsAssignableFrom)(JNIEnv*, jclass, jclass); + + jobject(JNICALL* ToReflectedField)(JNIEnv*, jclass, jfieldID, jboolean); + + jint(JNICALL* Throw)(JNIEnv*, jthrowable); + + jint(JNICALL* ThrowNew)(JNIEnv*, jclass, const char*); + + jthrowable(JNICALL* ExceptionOccurred)(JNIEnv*); + + void(JNICALL* ExceptionDescribe)(JNIEnv*); + + void(JNICALL* ExceptionClear)(JNIEnv*); + + void(JNICALL* FatalError)(JNIEnv*, const char*); + + jint(JNICALL* PushLocalFrame)(JNIEnv*, jint); + + jobject(JNICALL* PopLocalFrame)(JNIEnv*, jobject); + + jobject(JNICALL* NewGlobalRef)(JNIEnv*, jobject); + + void(JNICALL* DeleteGlobalRef)(JNIEnv*, jobject); + + void(JNICALL* DeleteLocalRef)(JNIEnv*, jobject); + + jboolean(JNICALL* IsSameObject)(JNIEnv*, jobject, jobject); + + jobject(JNICALL* NewLocalRef)(JNIEnv*, jobject); + + jint(JNICALL* EnsureLocalCapacity)(JNIEnv*, jint); + + jobject(JNICALL* AllocObject)(JNIEnv*, jclass); + + jobject(JNICALL* NewObject)(JNIEnv*, jclass, jmethodID, ...); + + jobject(JNICALL* NewObjectV)(JNIEnv*, jclass, jmethodID, va_list); + + jobject(JNICALL* NewObjectA)(JNIEnv*, jclass, jmethodID, const jvalue*); + + jclass(JNICALL* GetObjectClass)(JNIEnv*, jobject); + + jboolean(JNICALL* IsInstanceOf)(JNIEnv*, jobject, jclass); + + jmethodID(JNICALL* GetMethodID)(JNIEnv*, jclass, const char*, const char*); + + jobject(JNICALL* CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...); + + jobject(JNICALL* CallObjectMethodV)(JNIEnv*, jobject, jmethodID, va_list); + + jobject(JNICALL* CallObjectMethodA)(JNIEnv*, + jobject, + jmethodID, + const jvalue*); + + jboolean(JNICALL* CallBooleanMethod)(JNIEnv*, jobject, jmethodID, ...); + + jboolean(JNICALL* CallBooleanMethodV)(JNIEnv*, jobject, jmethodID, va_list); + + jboolean(JNICALL* CallBooleanMethodA)(JNIEnv*, + jobject, + jmethodID, + const jvalue*); + + jbyte(JNICALL* CallByteMethod)(JNIEnv*, jobject, jmethodID, ...); + + jbyte(JNICALL* CallByteMethodV)(JNIEnv*, jobject, jmethodID, va_list); + + jbyte(JNICALL* CallByteMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*); + + jchar(JNICALL* CallCharMethod)(JNIEnv*, jobject, jmethodID, ...); + + jchar(JNICALL* CallCharMethodV)(JNIEnv*, jobject, jmethodID, va_list); + + jchar(JNICALL* CallCharMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*); + + jshort(JNICALL* CallShortMethod)(JNIEnv*, jobject, jmethodID, ...); + + jshort(JNICALL* CallShortMethodV)(JNIEnv*, jobject, jmethodID, va_list); + + jshort(JNICALL* CallShortMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*); + + jint(JNICALL* CallIntMethod)(JNIEnv*, jobject, jmethodID, ...); + + jint(JNICALL* CallIntMethodV)(JNIEnv*, jobject, jmethodID, va_list); + + jint(JNICALL* CallIntMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*); + + jlong(JNICALL* CallLongMethod)(JNIEnv*, jobject, jmethodID, ...); + + jlong(JNICALL* CallLongMethodV)(JNIEnv*, jobject, jmethodID, va_list); + + jlong(JNICALL* CallLongMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*); + + jfloat(JNICALL* CallFloatMethod)(JNIEnv*, jobject, jmethodID, ...); + + jfloat(JNICALL* CallFloatMethodV)(JNIEnv*, jobject, jmethodID, va_list); + + jfloat(JNICALL* CallFloatMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*); + + jdouble(JNICALL* CallDoubleMethod)(JNIEnv*, jobject, jmethodID, ...); + + jdouble(JNICALL* CallDoubleMethodV)(JNIEnv*, jobject, jmethodID, va_list); + + jdouble(JNICALL* CallDoubleMethodA)(JNIEnv*, + jobject, + jmethodID, + const jvalue*); + + void(JNICALL* CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); + + void(JNICALL* CallVoidMethodV)(JNIEnv*, jobject, jmethodID, va_list); + + void(JNICALL* CallVoidMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*); + + jobject(JNICALL* CallNonvirtualObjectMethod)(JNIEnv*, + jobject, + jclass, + jmethodID, + ...); + + jobject(JNICALL* CallNonvirtualObjectMethodV)(JNIEnv*, + jobject, + jclass, + jmethodID, + va_list); + + jobject(JNICALL* CallNonvirtualObjectMethodA)(JNIEnv*, + jobject, + jclass, + jmethodID, + const jvalue*); + + jboolean(JNICALL* CallNonvirtualBooleanMethod)(JNIEnv*, + jobject, + jclass, + jmethodID, + ...); + + jboolean(JNICALL* CallNonvirtualBooleanMethodV)(JNIEnv*, + jobject, + jclass, + jmethodID, + va_list); + + jboolean(JNICALL* CallNonvirtualBooleanMethodA)(JNIEnv*, + jobject, + jclass, + jmethodID, + const jvalue*); + + jbyte(JNICALL* CallNonvirtualByteMethod)(JNIEnv*, + jobject, + jclass, + jmethodID, + ...); + + jbyte(JNICALL* CallNonvirtualByteMethodV)(JNIEnv*, + jobject, + jclass, + jmethodID, + va_list); + + jbyte(JNICALL* CallNonvirtualByteMethodA)(JNIEnv*, + jobject, + jclass, + jmethodID, + const jvalue*); + + jchar(JNICALL* CallNonvirtualCharMethod)(JNIEnv*, + jobject, + jclass, + jmethodID, + ...); + + jchar(JNICALL* CallNonvirtualCharMethodV)(JNIEnv*, + jobject, + jclass, + jmethodID, + va_list); + + jchar(JNICALL* CallNonvirtualCharMethodA)(JNIEnv*, + jobject, + jclass, + jmethodID, + const jvalue*); + + jshort(JNICALL* CallNonvirtualShortMethod)(JNIEnv*, + jobject, + jclass, + jmethodID, + ...); + + jshort(JNICALL* CallNonvirtualShortMethodV)(JNIEnv*, + jobject, + jclass, + jmethodID, + va_list); + + jshort(JNICALL* CallNonvirtualShortMethodA)(JNIEnv*, + jobject, + jclass, + jmethodID, + const jvalue*); + + jint(JNICALL* CallNonvirtualIntMethod)(JNIEnv*, + jobject, + jclass, + jmethodID, + ...); + + jint(JNICALL* CallNonvirtualIntMethodV)(JNIEnv*, + jobject, + jclass, + jmethodID, + va_list); + + jint(JNICALL* CallNonvirtualIntMethodA)(JNIEnv*, + jobject, + jclass, + jmethodID, + const jvalue*); + + jlong(JNICALL* CallNonvirtualLongMethod)(JNIEnv*, + jobject, + jclass, + jmethodID, + ...); + + jlong(JNICALL* CallNonvirtualLongMethodV)(JNIEnv*, + jobject, + jclass, + jmethodID, + va_list); + jlong(JNICALL* CallNonvirtualLongMethodA)(JNIEnv*, + jobject, + jclass, + jmethodID, + const jvalue*); + + jfloat(JNICALL* CallNonvirtualFloatMethod)(JNIEnv*, + jobject, + jclass, + jmethodID, + ...); + + jfloat(JNICALL* CallNonvirtualFloatMethodV)(JNIEnv*, + jobject, + jclass, + jmethodID, + va_list); + + jfloat(JNICALL* CallNonvirtualFloatMethodA)(JNIEnv*, + jobject, + jclass, + jmethodID, + const jvalue*); + + jdouble(JNICALL* CallNonvirtualDoubleMethod)(JNIEnv*, + jobject, + jclass, + jmethodID, + ...); + + jdouble(JNICALL* CallNonvirtualDoubleMethodV)(JNIEnv*, + jobject, + jclass, + jmethodID, + va_list); + + jdouble(JNICALL* CallNonvirtualDoubleMethodA)(JNIEnv*, + jobject, + jclass, + jmethodID, + const jvalue*); + + void(JNICALL* CallNonvirtualVoidMethod)(JNIEnv*, + jobject, + jclass, + jmethodID, + ...); + + void(JNICALL* CallNonvirtualVoidMethodV)(JNIEnv*, + jobject, + jclass, + jmethodID, + va_list); + + void(JNICALL* CallNonvirtualVoidMethodA)(JNIEnv*, + jobject, + jclass, + jmethodID, + const jvalue*); + + jfieldID(JNICALL* GetFieldID)(JNIEnv*, jclass, const char*, const char*); + + jobject(JNICALL* GetObjectField)(JNIEnv*, jobject, jfieldID); + + jboolean(JNICALL* GetBooleanField)(JNIEnv*, jobject, jfieldID); + + jbyte(JNICALL* GetByteField)(JNIEnv*, jobject, jfieldID); + + jchar(JNICALL* GetCharField)(JNIEnv*, jobject, jfieldID); + + jshort(JNICALL* GetShortField)(JNIEnv*, jobject, jfieldID); + + jint(JNICALL* GetIntField)(JNIEnv*, jobject, jfieldID); + + jlong(JNICALL* GetLongField)(JNIEnv*, jobject, jfieldID); + + jfloat(JNICALL* GetFloatField)(JNIEnv*, jobject, jfieldID); + + jdouble(JNICALL* GetDoubleField)(JNIEnv*, jobject, jfieldID); + + void(JNICALL* SetObjectField)(JNIEnv*, jobject, jfieldID, jobject); + + void(JNICALL* SetBooleanField)(JNIEnv*, jobject, jfieldID, jboolean); + + void(JNICALL* SetByteField)(JNIEnv*, jobject, jfieldID, jbyte); + + void(JNICALL* SetCharField)(JNIEnv*, jobject, jfieldID, jchar); + + void(JNICALL* SetShortField)(JNIEnv*, jobject, jfieldID, jshort); + + void(JNICALL* SetIntField)(JNIEnv*, jobject, jfieldID, jint); + + void(JNICALL* SetLongField)(JNIEnv*, jobject, jfieldID, jlong); + + void(JNICALL* SetFloatField)(JNIEnv*, jobject, jfieldID, jfloat); + + void(JNICALL* SetDoubleField)(JNIEnv*, jobject, jfieldID, jdouble); + + jmethodID(JNICALL* GetStaticMethodID)(JNIEnv*, + jclass, + const char*, + const char*); + + jobject(JNICALL* CallStaticObjectMethod)(JNIEnv*, jclass, jmethodID, ...); + + jobject(JNICALL* CallStaticObjectMethodV)(JNIEnv*, + jclass, + jmethodID, + va_list); + + jobject(JNICALL* CallStaticObjectMethodA)(JNIEnv*, + jclass, + jmethodID, + const jvalue*); + + jboolean(JNICALL* CallStaticBooleanMethod)(JNIEnv*, jclass, jmethodID, ...); + + jboolean(JNICALL* CallStaticBooleanMethodV)(JNIEnv*, + jclass, + jmethodID, + va_list); + + jboolean(JNICALL* CallStaticBooleanMethodA)(JNIEnv*, + jclass, + jmethodID, + const jvalue*); + + jbyte(JNICALL* CallStaticByteMethod)(JNIEnv*, jclass, jmethodID, ...); + + jbyte(JNICALL* CallStaticByteMethodV)(JNIEnv*, jclass, jmethodID, va_list); + + jbyte(JNICALL* CallStaticByteMethodA)(JNIEnv*, + jclass, + jmethodID, + const jvalue*); + + jchar(JNICALL* CallStaticCharMethod)(JNIEnv*, jclass, jmethodID, ...); + + jchar(JNICALL* CallStaticCharMethodV)(JNIEnv*, jclass, jmethodID, va_list); + + jchar(JNICALL* CallStaticCharMethodA)(JNIEnv*, + jclass, + jmethodID, + const jvalue*); + + jshort(JNICALL* CallStaticShortMethod)(JNIEnv*, jclass, jmethodID, ...); + + jshort(JNICALL* CallStaticShortMethodV)(JNIEnv*, jclass, jmethodID, va_list); + + jshort(JNICALL* CallStaticShortMethodA)(JNIEnv*, + jclass, + jmethodID, + const jvalue*); + + jint(JNICALL* CallStaticIntMethod)(JNIEnv*, jclass, jmethodID, ...); + + jint(JNICALL* CallStaticIntMethodV)(JNIEnv*, jclass, jmethodID, va_list); + + jint(JNICALL* CallStaticIntMethodA)(JNIEnv*, + jclass, + jmethodID, + const jvalue*); + + jlong(JNICALL* CallStaticLongMethod)(JNIEnv*, jclass, jmethodID, ...); + + jlong(JNICALL* CallStaticLongMethodV)(JNIEnv*, jclass, jmethodID, va_list); + + jlong(JNICALL* CallStaticLongMethodA)(JNIEnv*, + jclass, + jmethodID, + const jvalue*); + + jfloat(JNICALL* CallStaticFloatMethod)(JNIEnv*, jclass, jmethodID, ...); + + jfloat(JNICALL* CallStaticFloatMethodV)(JNIEnv*, jclass, jmethodID, va_list); + + jfloat(JNICALL* CallStaticFloatMethodA)(JNIEnv*, + jclass, + jmethodID, + const jvalue*); + + jdouble(JNICALL* CallStaticDoubleMethod)(JNIEnv*, jclass, jmethodID, ...); + + jdouble(JNICALL* CallStaticDoubleMethodV)(JNIEnv*, + jclass, + jmethodID, + va_list); + + jdouble(JNICALL* CallStaticDoubleMethodA)(JNIEnv*, + jclass, + jmethodID, + const jvalue*); + + void(JNICALL* CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...); + + void(JNICALL* CallStaticVoidMethodV)(JNIEnv*, jclass, jmethodID, va_list); + + void(JNICALL* CallStaticVoidMethodA)(JNIEnv*, + jclass, + jmethodID, + const jvalue*); + + jfieldID(JNICALL* GetStaticFieldID)(JNIEnv*, + jclass, + const char*, + const char*); + + jobject(JNICALL* GetStaticObjectField)(JNIEnv*, jclass, jfieldID); + + jboolean(JNICALL* GetStaticBooleanField)(JNIEnv*, jclass, jfieldID); + + jbyte(JNICALL* GetStaticByteField)(JNIEnv*, jclass, jfieldID); + + jchar(JNICALL* GetStaticCharField)(JNIEnv*, jclass, jfieldID); + + jshort(JNICALL* GetStaticShortField)(JNIEnv*, jclass, jfieldID); + + jint(JNICALL* GetStaticIntField)(JNIEnv*, jclass, jfieldID); + + jlong(JNICALL* GetStaticLongField)(JNIEnv*, jclass, jfieldID); + + jfloat(JNICALL* GetStaticFloatField)(JNIEnv*, jclass, jfieldID); + + jdouble(JNICALL* GetStaticDoubleField)(JNIEnv*, jclass, jfieldID); + + void(JNICALL* SetStaticObjectField)(JNIEnv*, jclass, jfieldID, jobject); + + void(JNICALL* SetStaticBooleanField)(JNIEnv*, jclass, jfieldID, jboolean); + + void(JNICALL* SetStaticByteField)(JNIEnv*, jclass, jfieldID, jbyte); + + void(JNICALL* SetStaticCharField)(JNIEnv*, jclass, jfieldID, jchar); + + void(JNICALL* SetStaticShortField)(JNIEnv*, jclass, jfieldID, jshort); + + void(JNICALL* SetStaticIntField)(JNIEnv*, jclass, jfieldID, jint); + + void(JNICALL* SetStaticLongField)(JNIEnv*, jclass, jfieldID, jlong); + + void(JNICALL* SetStaticFloatField)(JNIEnv*, jclass, jfieldID, jfloat); + + void(JNICALL* SetStaticDoubleField)(JNIEnv*, jclass, jfieldID, jdouble); + + jstring(JNICALL* NewString)(JNIEnv*, const jchar*, jsize); + + jsize(JNICALL* GetStringLength)(JNIEnv*, jstring); + + const jchar*(JNICALL* GetStringChars)(JNIEnv*, jstring, jboolean*); + + void(JNICALL* ReleaseStringChars)(JNIEnv*, jstring, const jchar*); + + jstring(JNICALL* NewStringUTF)(JNIEnv*, const char*); + + jsize(JNICALL* GetStringUTFLength)(JNIEnv*, jstring); + + const char*(JNICALL* GetStringUTFChars)(JNIEnv*, jstring, jboolean*); + + void(JNICALL* ReleaseStringUTFChars)(JNIEnv*, jstring, const char*); + + jsize(JNICALL* GetArrayLength)(JNIEnv*, jarray); + + jobjectArray(JNICALL* NewObjectArray)(JNIEnv*, jsize, jclass, jobject); + + jobject(JNICALL* GetObjectArrayElement)(JNIEnv*, jobjectArray, jsize); + + void(JNICALL* SetObjectArrayElement)(JNIEnv*, jobjectArray, jsize, jobject); + + jbooleanArray(JNICALL* NewBooleanArray)(JNIEnv*, jsize); + + jbyteArray(JNICALL* NewByteArray)(JNIEnv*, jsize); + + jcharArray(JNICALL* NewCharArray)(JNIEnv*, jsize); + + jshortArray(JNICALL* NewShortArray)(JNIEnv*, jsize); + + jintArray(JNICALL* NewIntArray)(JNIEnv*, jsize); + + jlongArray(JNICALL* NewLongArray)(JNIEnv*, jsize); + + jfloatArray(JNICALL* NewFloatArray)(JNIEnv*, jsize); + + jdoubleArray(JNICALL* NewDoubleArray)(JNIEnv*, jsize); + + jboolean*(JNICALL* GetBooleanArrayElements)(JNIEnv*, + jbooleanArray, + jboolean*); + + jbyte*(JNICALL* GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*); + + jchar*(JNICALL* GetCharArrayElements)(JNIEnv*, jcharArray, jboolean*); + + jshort*(JNICALL* GetShortArrayElements)(JNIEnv*, jshortArray, jboolean*); + + jint*(JNICALL* GetIntArrayElements)(JNIEnv*, jintArray, jboolean*); + + jlong*(JNICALL* GetLongArrayElements)(JNIEnv*, jlongArray, jboolean*); + + jfloat*(JNICALL* GetFloatArrayElements)(JNIEnv*, jfloatArray, jboolean*); + + jdouble*(JNICALL* GetDoubleArrayElements)(JNIEnv*, jdoubleArray, jboolean*); + + void(JNICALL* ReleaseBooleanArrayElements)(JNIEnv*, + jbooleanArray, + jboolean*, + jint); + + void(JNICALL* ReleaseByteArrayElements)(JNIEnv*, jbyteArray, jbyte*, jint); + + void(JNICALL* ReleaseCharArrayElements)(JNIEnv*, jcharArray, jchar*, jint); + + void(JNICALL* ReleaseShortArrayElements)(JNIEnv*, jshortArray, jshort*, jint); + + void(JNICALL* ReleaseIntArrayElements)(JNIEnv*, jintArray, jint*, jint); + + void(JNICALL* ReleaseLongArrayElements)(JNIEnv*, jlongArray, jlong*, jint); + + void(JNICALL* ReleaseFloatArrayElements)(JNIEnv*, jfloatArray, jfloat*, jint); + + void(JNICALL* ReleaseDoubleArrayElements)(JNIEnv*, + jdoubleArray, + jdouble*, + jint); + + void(JNICALL* GetBooleanArrayRegion)(JNIEnv*, + jbooleanArray, + jsize, + jsize, + jboolean*); + + void(JNICALL* GetByteArrayRegion)(JNIEnv*, jbyteArray, jsize, jsize, jbyte*); + + void(JNICALL* GetCharArrayRegion)(JNIEnv*, jcharArray, jsize, jsize, jchar*); + + void(JNICALL* GetShortArrayRegion)(JNIEnv*, + jshortArray, + jsize, + jsize, + jshort*); + + void(JNICALL* GetIntArrayRegion)(JNIEnv*, jintArray, jsize, jsize, jint*); + + void(JNICALL* GetLongArrayRegion)(JNIEnv*, jlongArray, jsize, jsize, jlong*); + + void(JNICALL* GetFloatArrayRegion)(JNIEnv*, + jfloatArray, + jsize, + jsize, + jfloat*); + + void(JNICALL* GetDoubleArrayRegion)(JNIEnv*, + jdoubleArray, + jsize, + jsize, + jdouble*); + + void(JNICALL* SetBooleanArrayRegion)(JNIEnv*, + jbooleanArray, + jsize, + jsize, + const jboolean*); + + void(JNICALL* SetByteArrayRegion)(JNIEnv*, + jbyteArray, + jsize, + jsize, + const jbyte*); + + void(JNICALL* SetCharArrayRegion)(JNIEnv*, + jcharArray, + jsize, + jsize, + const jchar*); + + void(JNICALL* SetShortArrayRegion)(JNIEnv*, + jshortArray, + jsize, + jsize, + const jshort*); + + void(JNICALL* SetIntArrayRegion)(JNIEnv*, + jintArray, + jsize, + jsize, + const jint*); + + void(JNICALL* SetLongArrayRegion)(JNIEnv*, + jlongArray, + jsize, + jsize, + const jlong*); + + void(JNICALL* SetFloatArrayRegion)(JNIEnv*, + jfloatArray, + jsize, + jsize, + const jfloat*); + + void(JNICALL* SetDoubleArrayRegion)(JNIEnv*, + jdoubleArray, + jsize, + jsize, + const jdouble*); + + jint(JNICALL* RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*, jint); + + jint(JNICALL* UnregisterNatives)(JNIEnv*, jclass); + + jint(JNICALL* MonitorEnter)(JNIEnv*, jobject); + + jint(JNICALL* MonitorExit)(JNIEnv*, jobject); + + jint(JNICALL* GetJavaVM)(JNIEnv*, JavaVM**); + + void(JNICALL* GetStringRegion)(JNIEnv*, jstring, jsize, jsize, jchar*); + + void(JNICALL* GetStringUTFRegion)(JNIEnv*, jstring, jsize, jsize, char*); + + void*(JNICALL* GetPrimitiveArrayCritical)(JNIEnv*, jarray, jboolean*); + + void(JNICALL* ReleasePrimitiveArrayCritical)(JNIEnv*, jarray, void*, jint); + + const jchar*(JNICALL* GetStringCritical)(JNIEnv*, jstring, jboolean*); + + void(JNICALL* ReleaseStringCritical)(JNIEnv*, jstring, const jchar*); + + jweak(JNICALL* NewWeakGlobalRef)(JNIEnv*, jobject); + + void(JNICALL* DeleteWeakGlobalRef)(JNIEnv*, jweak); + + jboolean(JNICALL* ExceptionCheck)(JNIEnv*); + + jobject(JNICALL* NewDirectByteBuffer)(JNIEnv*, void*, jlong); + + void*(JNICALL* GetDirectBufferAddress)(JNIEnv* env, jobject); + + jlong(JNICALL* GetDirectBufferCapacity)(JNIEnv*, jobject); +}; + +inline void atomicOr(uint32_t* p, int v) +{ + for (uint32_t old = *p; not atomicCompareAndSwap32(p, old, old | v); + old = *p) { + } +} + +inline void atomicAnd(uint32_t* p, int v) +{ + for (uint32_t old = *p; not atomicCompareAndSwap32(p, old, old & v); + old = *p) { + } +} + +inline int strcmp(const int8_t* a, const int8_t* b) +{ + return ::strcmp(reinterpret_cast(a), + reinterpret_cast(b)); +} + +void noop(); + +class Reference { + public: + Reference(object target, Reference** handle, bool weak) + : target(target), next(*handle), handle(handle), count(0), weak(weak) + { + if (next) { + next->handle = &next; + } + *handle = this; + } + + object target; + Reference* next; + Reference** handle; + unsigned count; + bool weak; +}; + +class Classpath; + +class Gc { + public: + enum Type { +#include "type-enums.cpp" + }; +}; + +class GcObject { + public: + template + T* as(Thread* t); + + template + bool isa(Thread* t); + + protected: + template + T& field_at(size_t offset) + { + return *reinterpret_cast(reinterpret_cast(this) + offset); + } +}; + +class GcFinalizer; +class GcClassLoader; +class GcJreference; +class GcArray; +class GcThrowable; +class GcRoots; + +class Machine { + public: + enum AllocationType { + MovableAllocation, + FixedAllocation, + ImmortalAllocation + }; + + Machine(System* system, + Heap* heap, + Finder* bootFinder, + Finder* appFinder, + Processor* processor, + Classpath* classpath, + const char** properties, + unsigned propertyCount, + const char** arguments, + unsigned argumentCount, + unsigned stackSizeInBytes); + + ~Machine() + { + dispose(); + } + + void dispose(); + + JavaVMVTable* vtable; + System* system; + Heap::Client* heapClient; + Heap* heap; + Finder* bootFinder; + Finder* appFinder; + Processor* processor; + Classpath* classpath; + Thread* rootThread; + Thread* exclusive; + Thread* finalizeThread; + Reference* jniReferences; + char** properties; + unsigned propertyCount; + const char** arguments; + unsigned argumentCount; + unsigned threadCount; + unsigned activeCount; + unsigned liveCount; + unsigned daemonCount; + unsigned fixedFootprint; + unsigned stackSizeInBytes; + System::Local* localThread; + System::Monitor* stateLock; + System::Monitor* heapLock; + System::Monitor* classLock; + System::Monitor* referenceLock; + System::Monitor* shutdownLock; + System::Library* libraries; + FILE* errorLog; + BootImage* bootimage; + GcArray* types; + GcRoots* roots; + GcFinalizer* finalizers; + GcFinalizer* tenuredFinalizers; + GcFinalizer* finalizeQueue; + GcJreference* weakReferences; + GcJreference* tenuredWeakReferences; + bool unsafe; + bool collecting; + bool triedBuiltinOnLoad; + bool dumpedHeapOnOOM; + bool alive; + JavaVMVTable javaVMVTable; + JNIEnvVTable jniEnvVTable; + uintptr_t* heapPool[ThreadHeapPoolSize]; + unsigned heapPoolIndex; + size_t bootimageSize; +}; + +void printTrace(Thread* t, GcThrowable* exception); + +void enterActiveState(Thread* t); + +#ifdef VM_STRESS + +inline void stress(Thread* t); + +#else // not VM_STRESS + +#define stress(t) + +#endif // not VM_STRESS + +uint64_t runThread(Thread*, uintptr_t*); + +uint64_t run(Thread* t, + uint64_t (*function)(Thread*, uintptr_t*), + uintptr_t* arguments); + +void checkDaemon(Thread* t); + +GcRoots* roots(Thread* t); + +extern "C" uint64_t vmRun(uint64_t (*function)(Thread*, uintptr_t*), + uintptr_t* arguments, + void* checkpoint); + +extern "C" void vmRun_returnAddress(); + +class GcThread; +class GcThrowable; +class GcString; + +class Thread { + public: + enum State { + NoState, + ActiveState, + IdleState, + ZombieState, + JoinedState, + ExclusiveState, + ExitState + }; + + enum Flag { + UseBackupHeapFlag = 1 << 0, + WaitingFlag = 1 << 1, + TracingFlag = 1 << 2, + DaemonFlag = 1 << 3, + StressFlag = 1 << 4, + ActiveFlag = 1 << 5, + SystemFlag = 1 << 6, + JoinFlag = 1 << 7, + TryNativeFlag = 1 << 8 + }; + + class Protector { + public: + Protector(Thread* t) : t(t), next(t->protector) + { + t->protector = this; + } + + ~Protector() + { + t->protector = next; + } + + virtual void visit(Heap::Visitor* v) = 0; + + Thread* t; + Protector* next; + }; + + class SingleProtector : public Protector { + public: + SingleProtector(Thread* t, void* p) : Protector(t), p(p) + { + } + + virtual void visit(Heap::Visitor* v) + { + v->visit(p); + } + + void* p; + }; + + class Resource { + public: + Resource(Thread* t, Resource* next) : t(t), next(next) + { + t->resource = this; + } + + virtual void release() = 0; + + Thread* t; + Resource* next; + }; + + class AutoResource : public Resource { + public: + AutoResource(Thread* t) : Resource(t, t->resource) + { + } + + ~AutoResource() + { + t->resource = next; + } + + virtual void release() = 0; + }; + + class ClassInitStack : public AutoResource { + public: + ClassInitStack(Thread* t, GcClass* class_) + : AutoResource(t), + next(t->classInitStack), + class_(class_), + protector(t, &(this->class_)) + { + t->classInitStack = this; + } + + ~ClassInitStack() + { + t->classInitStack = next; + } + + virtual void release() + { + this->ClassInitStack::~ClassInitStack(); + } + + ClassInitStack* next; + GcClass* class_; + SingleProtector protector; + }; + + class LibraryLoadStack : public AutoResource { + public: + LibraryLoadStack(Thread* t, GcClassLoader* classLoader) + : AutoResource(t), + next(t->libraryLoadStack), + classLoader(classLoader), + protector(t, &(this->classLoader)) + { + t->libraryLoadStack = this; + } + + ~LibraryLoadStack() + { + t->libraryLoadStack = next; + } + + virtual void release() + { + this->LibraryLoadStack::~LibraryLoadStack(); + } + + LibraryLoadStack* next; + GcClassLoader* classLoader; + SingleProtector protector; + }; + + class Checkpoint { + public: + Checkpoint(Thread* t) + : t(t), + next(t->checkpoint), + resource(t->resource), + protector(t->protector), + noThrow(false) + { + t->checkpoint = this; + } + + ~Checkpoint() + { + t->checkpoint = next; + } + + virtual void NO_RETURN unwind() = 0; + + Thread* t; + Checkpoint* next; + Resource* resource; + Protector* protector; + bool noThrow; + }; + + class RunCheckpoint : public Checkpoint { + public: + RunCheckpoint(Thread* t) : Checkpoint(t), stack(0) + { + } + + virtual void unwind() + { + void* stack = this->stack; + this->stack = 0; + expect(t->m->system, stack); + vmJump(voidPointer(vmRun_returnAddress), 0, stack, t, 0, 0); + } + + void* stack; + }; + + class Runnable : public System::Runnable { + public: + Runnable(Thread* t) : t(t) + { + } + + virtual void attach(System::Thread* st) + { + t->systemThread = st; + } + + virtual void run(); + + virtual bool interrupted(); + + virtual void setInterrupted(bool v); + + Thread* t; + }; + + Thread(Machine* m, GcThread* javaThread, Thread* parent); + + void init(); + void exit(); + void dispose(); + + void setFlag(Flag flag) { + atomicOr(&flags, flag); + } + + void clearFlag(Flag flag) { + atomicAnd(&flags, ~flag); + } + + unsigned getFlags() { + return flags; + } + + JNIEnvVTable* vtable; + Machine* m; + Thread* parent; + Thread* peer; + Thread* child; + Thread* waitNext; + State state; + unsigned criticalLevel; + System::Thread* systemThread; + System::Monitor* lock; + GcThread* javaThread; + GcThrowable* exception; + unsigned heapIndex; + unsigned heapOffset; + Protector* protector; + ClassInitStack* classInitStack; + LibraryLoadStack* libraryLoadStack; + Resource* resource; + Checkpoint* checkpoint; + Runnable runnable; + uintptr_t* defaultHeap; + uintptr_t* heap; + uintptr_t backupHeap[ThreadBackupHeapSizeInWords]; + unsigned backupHeapIndex; + + private: + unsigned flags; +}; + +class GcJfield; + +class Classpath { + public: + virtual GcJclass* makeJclass(Thread* t, GcClass* class_) = 0; + + virtual GcString* makeString(Thread* t, + object array, + int32_t offset, + int32_t length) = 0; + + virtual GcThread* makeThread(Thread* t, Thread* parent) = 0; + + virtual object makeJMethod(Thread* t, GcMethod* vmMethod) = 0; + + virtual GcMethod* getVMMethod(Thread* t, object jmethod) = 0; + + virtual object makeJField(Thread* t, GcField* vmField) = 0; + + virtual GcField* getVMField(Thread* t, GcJfield* jfield) = 0; + + virtual void clearInterrupted(Thread* t) = 0; + + virtual void runThread(Thread* t) = 0; + + virtual void resolveNative(Thread* t, GcMethod* method) = 0; + + virtual void interceptMethods(Thread* t) = 0; + + virtual void preBoot(Thread* t) = 0; + + virtual bool mayInitClasses() = 0; + + virtual void boot(Thread* t) = 0; + + virtual const char* bootClasspath() = 0; + + virtual object makeDirectByteBuffer(Thread* t, void* p, jlong capacity) = 0; + + virtual void* getDirectBufferAddress(Thread* t, object buffer) = 0; + + virtual int64_t getDirectBufferCapacity(Thread* t, object buffer) = 0; + + virtual bool canTailCall(Thread* t, + GcMethod* caller, + GcByteArray* calleeClassName, + GcByteArray* calleeMethodName, + GcByteArray* calleeMethodSpec) = 0; + + virtual GcClassLoader* libraryClassLoader(Thread* t, GcMethod* caller) = 0; + + virtual void shutDown(Thread* t) = 0; + + virtual void dispose() = 0; +}; + +#ifdef _MSC_VER + +template +class ThreadRuntimeArray : public Thread::AutoResource { + public: + ThreadRuntimeArray(Thread* t, unsigned size) + : AutoResource(t), + body(static_cast(t->m->heap->allocate(size * sizeof(T)))), + size(size) + { + } + + ~ThreadRuntimeArray() + { + t->m->heap->free(body, size * sizeof(T)); + } + + virtual void release() + { + ThreadRuntimeArray::~ThreadRuntimeArray(); + } + + T* body; + unsigned size; +}; + +#define THREAD_RUNTIME_ARRAY(thread, type, name, size) \ + ThreadRuntimeArray name(thread, size); + +#else // not _MSC_VER + +#define THREAD_RUNTIME_ARRAY(thread, type, name, size) type name##_body[size]; + +#endif // not _MSC_VER + +Classpath* makeClasspath(System* system, + Allocator* allocator, + const char* javaHome, + const char* embedPrefix); + +typedef uint64_t(JNICALL* FastNativeFunction)(Thread*, GcMethod*, uintptr_t*); +typedef void(JNICALL* FastVoidNativeFunction)(Thread*, GcMethod*, uintptr_t*); + +inline GcClass* objectClass(Thread*, object o) +{ + return reinterpret_cast( + maskAlignedPointer(fieldAtOffset(o, 0))); +} + +inline unsigned stackSizeInWords(Thread* t) +{ + return t->m->stackSizeInBytes / BytesPerWord; +} + +void enter(Thread* t, Thread::State state); + +inline void enterActiveState(Thread* t) +{ + enter(t, Thread::ActiveState); +} + +class StateResource : public Thread::AutoResource { + public: + StateResource(Thread* t, Thread::State state) + : AutoResource(t), oldState(t->state) + { + enter(t, state); + } + + ~StateResource() + { + enter(t, oldState); + } + + virtual void release() + { + this->StateResource::~StateResource(); + } + + private: + Thread::State oldState; +}; + +inline void dispose(Thread* t, Reference* r) +{ + *(r->handle) = r->next; + if (r->next) { + r->next->handle = r->handle; + } + t->m->heap->free(r, sizeof(*r)); +} + +inline void acquire(Thread*, Reference* r) +{ + ++r->count; +} + +inline void release(Thread* t, Reference* r) +{ + if ((--r->count) == 0) { + dispose(t, r); + } +} + +void collect(Thread* t, Heap::CollectionType type, int pendingAllocation = 0); + +void shutDown(Thread* t); + +#ifdef VM_STRESS + +inline void stress(Thread* t) +{ + if ((not t->m->unsafe) + and (t->getFlags() & (Thread::StressFlag | Thread::TracingFlag)) == 0 + and t->state != Thread::NoState and t->state != Thread::IdleState) { + t->setFlag(Thread::StressFlag); + +#ifdef VM_STRESS_MAJOR + collect(t, Heap::MajorCollection); +#else // not VM_STRESS_MAJOR + collect(t, Heap::MinorCollection); +#endif // not VM_STRESS_MAJOR + + t->clearFlag(Thread::StressFlag); + } +} + +#endif // not VM_STRESS + +inline void acquire(Thread* t, System::Monitor* m) +{ + if (not m->tryAcquire(t->systemThread)) { + ENTER(t, Thread::IdleState); + m->acquire(t->systemThread); + } + + stress(t); +} + +inline void release(Thread* t, System::Monitor* m) +{ + m->release(t->systemThread); +} + +class MonitorResource : public Thread::AutoResource { + public: + MonitorResource(Thread* t, System::Monitor* m) : AutoResource(t), m(m) + { + acquire(t, m); + } + + ~MonitorResource() + { + vm::release(t, m); + } + + virtual void release() + { + this->MonitorResource::~MonitorResource(); + } + + private: + System::Monitor* m; +}; + +class RawMonitorResource : public Thread::Resource { + public: + RawMonitorResource(Thread* t, System::Monitor* m) + : Resource(t, t->resource), m(m) + { + m->acquire(t->systemThread); + } + + ~RawMonitorResource() + { + t->resource = next; + vm::release(t, m); + } + + virtual void release() + { + this->RawMonitorResource::~RawMonitorResource(); + } + + private: + System::Monitor* m; +}; + +inline Aborter* getAborter(Thread* t) +{ + return t->m->system; +} + +inline bool ensure(Thread* t, unsigned sizeInBytes) +{ + if (t->heapIndex + ceilingDivide(sizeInBytes, BytesPerWord) + > ThreadHeapSizeInWords) { + if (sizeInBytes <= ThreadBackupHeapSizeInBytes) { + expect(t, (t->getFlags() & Thread::UseBackupHeapFlag) == 0); + + t->setFlag(Thread::UseBackupHeapFlag); + + return true; + } else { + return false; + } + } else { + return true; + } +} + +object allocate2(Thread* t, unsigned sizeInBytes, bool objectMask); + +object allocate3(Thread* t, + Alloc* allocator, + Machine::AllocationType type, + unsigned sizeInBytes, + bool objectMask); + +inline object allocateSmall(Thread* t, unsigned sizeInBytes) +{ + assertT(t, + t->heapIndex + ceilingDivide(sizeInBytes, BytesPerWord) + <= ThreadHeapSizeInWords); + + object o = reinterpret_cast(t->heap + t->heapIndex); + t->heapIndex += ceilingDivide(sizeInBytes, BytesPerWord); + return o; +} + +inline object allocate(Thread* t, unsigned sizeInBytes, bool objectMask) +{ + stress(t); + + if (UNLIKELY(t->heapIndex + ceilingDivide(sizeInBytes, BytesPerWord) + > ThreadHeapSizeInWords or t->m->exclusive)) { + return allocate2(t, sizeInBytes, objectMask); + } else { + assertT(t, t->criticalLevel == 0); + return allocateSmall(t, sizeInBytes); + } +} + +inline void mark(Thread* t, object o, unsigned offset, unsigned count) +{ + t->m->heap->mark(o, offset / BytesPerWord, count); +} + +inline void mark(Thread* t, object o, unsigned offset) +{ + t->m->heap->mark(o, offset / BytesPerWord, 1); +} + +inline void setField(Thread* t, object target, unsigned offset, object value) +{ + fieldAtOffset(target, offset) = value; + mark(t, target, offset); +} + +inline void setObject(Thread* t, + GcObject* target, + unsigned offset, + GcObject* value) +{ + setField(t, target, offset, value); +} + +inline void setObjectClass(Thread*, object o, GcClass* c) +{ + fieldAtOffset(o, 0) = reinterpret_cast( + reinterpret_cast(c) + | (reinterpret_cast(fieldAtOffset(o, 0)) + & (~PointerMask))); +} + +inline const char* findProperty(Machine* m, const char* name) +{ + for (unsigned i = 0; i < m->propertyCount; ++i) { + const char* p = m->properties[i]; + const char* n = name; + while (*p and *p != '=' and *n and *p == *n) { + ++p; + ++n; + } + if (*p == '=' and *n == 0) { + return p + 1; + } + } + return 0; +} + +inline const char* findProperty(Thread* t, const char* name) +{ + return findProperty(t->m, name); +} + +object arrayBodyUnsafe(Thread*, GcArray*, unsigned); + +bool instanceOf(Thread* t, GcClass* class_, object o); + +template +T* GcObject::as(Thread* t UNUSED) +{ + assertT(t, + t->m->unsafe || instanceOf(t, + reinterpret_cast(arrayBodyUnsafe( + t, t->m->types, T::Type)), + this)); + return static_cast(this); +} + +template +bool GcObject::isa(Thread* t) +{ + return instanceOf( + t, + reinterpret_cast(arrayBodyUnsafe(t, t->m->types, T::Type)), + this); +} + +template +T* cast(Thread* t UNUSED, object o) +{ + if (o == 0) { + return 0; + } + assertT(t, + t->m->unsafe || instanceOf(t, + reinterpret_cast(arrayBodyUnsafe( + t, t->m->types, T::Type)), + o)); + return reinterpret_cast(o); +} + +#include "type-declarations.cpp" + +inline object arrayBodyUnsafe(Thread*, GcArray* a, unsigned index) +{ + return a->body()[index]; +} + +inline void Thread::Runnable::run() +{ + enterActiveState(t); + + vm::run(t, runThread, 0); + + if (t->exception and t->exception != roots(t)->shutdownInProgress()) { + printTrace(t, t->exception); + } + + t->exit(); +} + +inline bool Thread::Runnable::interrupted() +{ + return t->javaThread and t->javaThread->interrupted(); +} + +inline void Thread::Runnable::setInterrupted(bool v) +{ + t->javaThread->interrupted() = v; +} + +inline uint64_t runRaw(Thread* t, + uint64_t (*function)(Thread*, uintptr_t*), + uintptr_t* arguments) +{ + Thread::RunCheckpoint checkpoint(t); + return vmRun(function, arguments, &checkpoint); +} + +inline uint64_t run(Thread* t, + uint64_t (*function)(Thread*, uintptr_t*), + uintptr_t* arguments) +{ + ENTER(t, Thread::ActiveState); + return runRaw(t, function, arguments); +} + +inline void runJavaThread(Thread* t) +{ + t->m->classpath->runThread(t); +} + +void runFinalizeThread(Thread* t); + +inline uint64_t runThread(Thread* t, uintptr_t*) +{ + t->m->localThread->set(t); + + checkDaemon(t); + + if (t == t->m->finalizeThread) { + runFinalizeThread(t); + } else if (t->javaThread) { + runJavaThread(t); + } + + return 1; +} + +inline bool startThread(Thread* t, Thread* p) +{ + p->setFlag(Thread::JoinFlag); +#ifdef SGX + static const char16_t *nameToSkip = u"Reference Handler"; + if (p->javaThread->name()->length(t) == 17) { + if (!memcmp(nameToSkip, cast(t, p->javaThread->name()->data())->body().begin(), 17 * 2)) { + printf("Skipping start of reference handler thread\n"); + return true; + } + } +#endif + return t->m->system->success(t->m->system->start(&(p->runnable))); +} + +inline void addThread(Thread* t, Thread* p) +{ + ACQUIRE_RAW(t, t->m->stateLock); + + assertT(t, p->state == Thread::NoState); + expect(t, + t->state == Thread::ActiveState || t->state == Thread::ExclusiveState + || t->state == Thread::NoState); + + p->state = Thread::IdleState; + ++t->m->threadCount; + ++t->m->liveCount; + + p->peer = p->parent->child; + p->parent->child = p; + + if (p->javaThread) { + p->javaThread->peer() = reinterpret_cast(p); + } +} + +inline void removeThread(Thread* t, Thread* p) +{ + ACQUIRE_RAW(t, t->m->stateLock); + + assertT(t, p->state == Thread::IdleState); + + --t->m->liveCount; + --t->m->threadCount; + + t->m->stateLock->notifyAll(t->systemThread); + + p->parent->child = p->peer; + + if (p->javaThread) { + p->javaThread->peer() = 0; + } + + p->dispose(); +} + +inline Thread* startThread(Thread* t, GcThread* javaThread) +{ + { + PROTECT(t, javaThread); + + stress(t); + + ACQUIRE_RAW(t, t->m->stateLock); + + if (t->m->threadCount > t->m->liveCount + ZombieCollectionThreshold) { + collect(t, Heap::MinorCollection); + } + } + + Thread* p = t->m->processor->makeThread(t->m, javaThread, t); + + addThread(t, p); + + if (startThread(t, p)) { + return p; + } else { + removeThread(t, p); + return 0; + } +} + +inline void registerDaemon(Thread* t) +{ + ACQUIRE_RAW(t, t->m->stateLock); + + t->setFlag(Thread::DaemonFlag); + + ++t->m->daemonCount; + + t->m->stateLock->notifyAll(t->systemThread); +} + +inline void checkDaemon(Thread* t) +{ + if (t->javaThread->daemon()) { + registerDaemon(t); + } +} + +inline uint64_t initAttachedThread(Thread* t, uintptr_t* arguments) +{ + bool daemon = arguments[0]; + + t->javaThread = t->m->classpath->makeThread(t, t->m->rootThread); + + t->javaThread->peer() = reinterpret_cast(t); + + if (daemon) { + t->javaThread->daemon() = true; + + registerDaemon(t); + } + + t->m->localThread->set(t); + + return 1; +} + +inline Thread* attachThread(Machine* m, bool daemon) +{ + Thread* t = m->processor->makeThread(m, 0, m->rootThread); + m->system->attach(&(t->runnable)); + + addThread(t, t); + + enter(t, Thread::ActiveState); + + uintptr_t arguments[] = {daemon}; + + if (run(t, initAttachedThread, arguments)) { + enter(t, Thread::IdleState); + return t; + } else { + t->exit(); + return 0; + } +} + +inline GcRoots* roots(Thread* t) +{ + return t->m->roots; +} + +inline GcClass* type(Thread* t, Gc::Type type) +{ + return cast(t, t->m->types->body()[type]); +} + +inline void setType(Thread* t, Gc::Type type, GcClass* value) +{ + t->m->types->setBodyElement(t, type, value); +} + +inline bool objectFixed(Thread*, object o) +{ + return (alias(o, 0) & (~PointerMask)) == FixedMark; +} + +inline bool objectExtended(Thread*, object o) +{ + return (alias(o, 0) & (~PointerMask)) == ExtendedMark; +} + +inline bool hashTaken(Thread*, object o) +{ + return (alias(o, 0) & (~PointerMask)) == HashTakenMark; +} + +inline unsigned baseSize(Thread* t UNUSED, object o, GcClass* class_) +{ + assertT(t, class_->fixedSize() >= BytesPerWord); + + unsigned size = ceilingDivide(class_->fixedSize(), BytesPerWord); + if (class_->arrayElementSize() > 0) { + size += ceilingDivide(class_->arrayElementSize() + * fieldAtOffset( + o, class_->fixedSize() - BytesPerWord), + BytesPerWord); + } + return size; +} + +object makeTrace(Thread* t, Processor::StackWalker* walker); + +object makeTrace(Thread* t, Thread* target); + +inline object makeTrace(Thread* t) +{ + return makeTrace(t, t); +} + +inline object makeNew(Thread* t, GcClass* class_) +{ + assertT(t, t->state == Thread::NoState or t->state == Thread::ActiveState); + + PROTECT(t, class_); + unsigned sizeInBytes = pad(class_->fixedSize()); + assertT(t, sizeInBytes); + object instance = allocate(t, sizeInBytes, class_->objectMask()); + setObjectClass(t, instance, class_); + + return instance; +} + +object makeNewGeneral(Thread* t, GcClass* class_); + +inline object make(Thread* t, GcClass* class_) +{ + if (UNLIKELY(class_->vmFlags() & (WeakReferenceFlag | HasFinalizerFlag))) { + return makeNewGeneral(t, class_); + } else { + return makeNew(t, class_); + } +} + +GcByteArray* makeByteArrayV(Thread* t, const char* format, va_list a, int size); + +GcByteArray* makeByteArray(Thread* t, const char* format, ...); + +GcString* makeString(Thread* t, const char* format, ...); + +#ifndef HAVE_StringOffset + +inline uint32_t GcString::length(Thread* t) +{ + return cast(t, this->data())->length(); +} + +inline uint32_t GcString::offset(Thread*) +{ + return 0; +} + +#ifndef HAVE_StringHash32 + +inline GcString* makeString(Thread* t, object data, int32_t hash, int32_t) +{ + return makeString(t, data, hash); +} + +#endif // not HAVE_StringHash32 + +inline GcString* makeString(Thread* t, + object odata, + unsigned offset, + unsigned length, + unsigned) +{ + GcCharArray* data = cast(t, odata); + if (offset == 0 and length == data->length()) { + return makeString(t, reinterpret_cast(data), 0, 0); + } else { + PROTECT(t, data); + + GcCharArray* array = makeCharArray(t, length); + + memcpy(array->body().begin(), &data->body()[offset], length * 2); + + return makeString(t, reinterpret_cast(array), 0, 0); + } +} + +#endif // not HAVE_StringOffset + +int stringUTFLength(Thread* t, + GcString* string, + unsigned start, + unsigned length); + +inline int stringUTFLength(Thread* t, GcString* string) +{ + return stringUTFLength(t, string, 0, string->length(t)); +} + +void stringChars(Thread* t, + GcString* string, + unsigned start, + unsigned length, + char* chars); + +inline void stringChars(Thread* t, GcString* string, char* chars) +{ + stringChars(t, string, 0, string->length(t), chars); +} + +void stringChars(Thread* t, + GcString* string, + unsigned start, + unsigned length, + uint16_t* chars); + +inline void stringChars(Thread* t, GcString* string, uint16_t* chars) +{ + stringChars(t, string, 0, string->length(t), chars); +} + +void stringUTFChars(Thread* t, + GcString* string, + unsigned start, + unsigned length, + char* chars, + unsigned charsLength); + +inline void stringUTFChars(Thread* t, + GcString* string, + char* chars, + unsigned charsLength) +{ + stringUTFChars(t, string, 0, string->length(t), chars, charsLength); +} + +bool isAssignableFrom(Thread* t, GcClass* a, GcClass* b); + +GcMethod* classInitializer(Thread* t, GcClass* class_); + +object frameMethod(Thread* t, int frame); + +inline uintptr_t& extendedWord(Thread* t UNUSED, object o, unsigned baseSize) +{ + assertT(t, objectExtended(t, o)); + return fieldAtOffset(o, baseSize * BytesPerWord); +} + +inline unsigned extendedSize(Thread* t, object o, unsigned baseSize) +{ + return baseSize + objectExtended(t, o); +} + +inline void markHashTaken(Thread* t, object o) +{ + assertT(t, not objectExtended(t, o)); + assertT(t, not objectFixed(t, o)); + + ACQUIRE_RAW(t, t->m->heapLock); + + alias(o, 0) |= HashTakenMark; + t->m->heap->pad(o); +} + +inline uint32_t takeHash(Thread*, object o) +{ + // some broken code implicitly relies on System.identityHashCode + // always returning a non-negative number (e.g. old versions of + // com/sun/xml/bind/v2/util/CollisionCheckStack.hash), hence the "& + // 0x7FFFFFFF": + return (reinterpret_cast(o) / BytesPerWord) & 0x7FFFFFFF; +} + +inline uint32_t objectHash(Thread* t, object o) +{ + if (objectExtended(t, o)) { + return extendedWord(t, o, baseSize(t, o, objectClass(t, o))); + } else { + if (not objectFixed(t, o)) { + markHashTaken(t, o); + } + return takeHash(t, o); + } +} + +inline bool objectEqual(Thread*, object a, object b) +{ + return a == b; +} + +inline uint32_t byteArrayHash(Thread* t UNUSED, object ao) +{ + GcByteArray* a = cast(t, ao); + return hash(a->body()); +} + +inline uint32_t charArrayHash(Thread* t UNUSED, object ao) +{ + GcByteArray* a = cast(t, ao); + return hash(a->body()); +} + +inline bool byteArrayEqual(Thread* t UNUSED, object ao, object bo) +{ + GcByteArray* a = cast(t, ao); + GcByteArray* b = cast(t, bo); + return a == b + or ((a->length() == b->length()) + and memcmp(a->body().begin(), b->body().begin(), a->length()) + == 0); +} + +inline uint32_t stringHash(Thread* t, object so) +{ + GcString* s = cast(t, so); + if (s->hashCode() == 0 and s->length(t)) { + if (objectClass(t, s->data()) == type(t, GcByteArray::Type)) { + s->hashCode() = hash(cast(t, s->data())->body().subslice( + s->offset(t), s->length(t))); + } else { + s->hashCode() = hash(cast(t, s->data())->body().subslice( + s->offset(t), s->length(t))); + } + } + return s->hashCode(); +} + +inline uint16_t stringCharAt(Thread* t, GcString* s, int i) +{ + if (objectClass(t, s->data()) == type(t, GcByteArray::Type)) { + return cast(t, s->data())->body()[s->offset(t) + i]; + } else { + return cast(t, s->data())->body()[s->offset(t) + i]; + } +} + +inline bool stringEqual(Thread* t, object ao, object bo) +{ + GcString* a = cast(t, ao); + GcString* b = cast(t, bo); + if (a == b) { + return true; + } else if (a->length(t) == b->length(t)) { + for (unsigned i = 0; i < a->length(t); ++i) { + if (stringCharAt(t, a, i) != stringCharAt(t, b, i)) { + return false; + } + } + return true; + } else { + return false; + } +} + +inline uint32_t methodHash(Thread* t, object mo) +{ + GcMethod* method = cast(t, mo); + return byteArrayHash(t, method->name()) ^ byteArrayHash(t, method->spec()); +} + +inline bool methodEqual(Thread* t, object ao, object bo) +{ + GcMethod* a = cast(t, ao); + GcMethod* b = cast(t, bo); + return a == b or (byteArrayEqual(t, a->name(), b->name()) + and byteArrayEqual(t, a->spec(), b->spec())); +} + +class MethodSpecIterator { + public: + MethodSpecIterator(Thread* t, const char* s) : t(t), s(s + 1) + { + } + + const char* next() + { + assertT(t, *s != ')'); + + const char* p = s; + + switch (*s) { + case 'L': + while (*s and *s != ';') + ++s; + ++s; + break; + + case '[': + while (*s == '[') + ++s; + switch (*s) { + case 'L': + while (*s and *s != ';') + ++s; + ++s; + break; + + default: + ++s; + break; + } + break; + + default: + ++s; + break; + } + + return p; + } + + bool hasNext() + { + return *s != ')'; + } + + const char* returnSpec() + { + assertT(t, *s == ')'); + return s + 1; + } + + Thread* t; + const char* s; +}; + +unsigned fieldCode(Thread* t, unsigned javaCode); + +unsigned fieldType(Thread* t, unsigned code); + +unsigned primitiveSize(Thread* t, unsigned code); + +inline unsigned fieldSize(Thread* t, unsigned code) +{ + if (code == ObjectField) { + return BytesPerWord; + } else { + return primitiveSize(t, code); + } +} + +inline unsigned fieldSize(Thread* t, GcField* field) +{ + return fieldSize(t, field->code()); +} + +inline void scanMethodSpec(Thread* t, + const char* s, + bool static_, + unsigned* parameterCount, + unsigned* parameterFootprint, + unsigned* returnCode) +{ + unsigned count = 0; + unsigned footprint = 0; + MethodSpecIterator it(t, s); + while (it.hasNext()) { + ++count; + switch (*it.next()) { + case 'J': + case 'D': + footprint += 2; + break; + + default: + ++footprint; + break; + } + } + + if (not static_) { + ++footprint; + } + + *parameterCount = count; + *parameterFootprint = footprint; + *returnCode = fieldCode(t, *it.returnSpec()); +} + +GcClass* findLoadedClass(Thread* t, GcClassLoader* loader, GcByteArray* spec); + +GcJclass* getDeclaringClass(Thread* t, GcClass* c); + +GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation); + +inline bool emptyMethod(Thread* t UNUSED, GcMethod* method) +{ + return ((method->flags() & ACC_NATIVE) == 0) + and (method->code()->length() == 1) + and (method->code()->body()[0] == return_); +} + +object parseUtf8(Thread* t, const char* data, unsigned length); + +object parseUtf8(Thread* t, GcByteArray* array); + +GcClass* parseClass(Thread* t, + GcClassLoader* loader, + const uint8_t* data, + unsigned length, + Gc::Type throwType = GcNoClassDefFoundError::Type); + +GcClass* resolveClass(Thread* t, + GcClassLoader* loader, + GcByteArray* name, + bool throw_ = true, + Gc::Type throwType = GcNoClassDefFoundError::Type); + +inline GcClass* resolveClass(Thread* t, + GcClassLoader* loader, + const char* name, + bool throw_ = true, + Gc::Type throwType = GcNoClassDefFoundError::Type) +{ + PROTECT(t, loader); + GcByteArray* n = makeByteArray(t, "%s", name); + return resolveClass(t, loader, n, throw_, throwType); +} + +GcClass* resolveSystemClass(Thread* t, + GcClassLoader* loader, + GcByteArray* name, + bool throw_ = true, + Gc::Type throwType = GcNoClassDefFoundError::Type); + +inline GcClass* resolveSystemClass(Thread* t, + GcClassLoader* loader, + const char* name) +{ + return resolveSystemClass(t, loader, makeByteArray(t, "%s", name)); +} + +void linkClass(Thread* t, GcClassLoader* loader, GcClass* class_); + +GcMethod* resolveMethod(Thread* t, + GcClass* class_, + const char* methodName, + const char* methodSpec); + +inline GcMethod* resolveMethod(Thread* t, + GcClassLoader* loader, + const char* className, + const char* methodName, + const char* methodSpec) +{ + return resolveMethod( + t, resolveClass(t, loader, className), methodName, methodSpec); +} + +GcField* resolveField(Thread* t, + GcClass* class_, + const char* fieldName, + const char* fieldSpec); + +inline GcField* resolveField(Thread* t, + GcClassLoader* loader, + const char* className, + const char* fieldName, + const char* fieldSpec) +{ + return resolveField( + t, resolveClass(t, loader, className), fieldName, fieldSpec); +} + +bool classNeedsInit(Thread* t, GcClass* c); + +bool preInitClass(Thread* t, GcClass* c); + +void postInitClass(Thread* t, GcClass* c); + +void initClass(Thread* t, GcClass* c); + +GcClass* resolveObjectArrayClass(Thread* t, + GcClassLoader* loader, + GcClass* elementClass); + +object makeObjectArray(Thread* t, GcClass* elementClass, unsigned count); + +inline object makeObjectArray(Thread* t, unsigned count) +{ + return makeObjectArray(t, type(t, GcJobject::Type), count); +} + +object findFieldInClass(Thread* t, + GcClass* class_, + GcByteArray* name, + GcByteArray* spec); + +inline GcField* findFieldInClass2(Thread* t, + GcClass* class_, + const char* name, + const char* spec) +{ + PROTECT(t, class_); + GcByteArray* n = makeByteArray(t, "%s", name); + PROTECT(t, n); + GcByteArray* s = makeByteArray(t, "%s", spec); + return cast(t, findFieldInClass(t, class_, n, s)); +} + +object findMethodInClass(Thread* t, + GcClass* class_, + GcByteArray* name, + GcByteArray* spec); + +inline GcThrowable* makeThrowable(Thread* t, + Gc::Type type, + GcString* message = 0, + object trace = 0, + GcThrowable* cause = 0) +{ + PROTECT(t, message); + PROTECT(t, trace); + PROTECT(t, cause); + + if (trace == 0) { + trace = makeTrace(t); + } + + GcThrowable* result = cast(t, make(t, vm::type(t, type))); + + result->setMessage(t, message); + result->setTrace(t, trace); + result->setCause(t, cause); + + return result; +} + +inline GcThrowable* makeThrowableV(Thread* t, + Gc::Type type, + const char* format, + va_list a, + int size) +{ + GcByteArray* s = makeByteArrayV(t, format, a, size); + + if (s) { + GcString* message = t->m->classpath->makeString(t, s, 0, s->length() - 1); + + return makeThrowable(t, type, message); + } else { + return 0; + } +} + +inline GcThrowable* makeThrowable(Thread* t, + Gc::Type type, + const char* format, + ...) +{ + int size = 256; + while (true) { + va_list a; + va_start(a, format); + GcThrowable* r = makeThrowableV(t, type, format, a, size); + va_end(a); + + if (r) { + return r; + } else { + size *= 2; + } + } +} + +void popResources(Thread* t); + +} // namespace vm + +namespace vm { + +void dumpHeap(Thread* t, FILE* out); + +inline void NO_RETURN throw_(Thread* t, GcThrowable* e) +{ + assertT(t, t->exception == 0); + assertT(t, e); + + expect(t, not t->checkpoint->noThrow); + + t->exception = e; + + if (objectClass(t, e) == type(t, GcOutOfMemoryError::Type)) { + if (not t->m->dumpedHeapOnOOM) { + t->m->dumpedHeapOnOOM = true; + const char* path = findProperty(t, "avian.heap.dump"); + if (path) { + FILE* out = vm::fopen(path, "wb"); + if (out) { + dumpHeap(t, out); + fclose(out); + } + } + } + + if (AbortOnOutOfMemoryError) { + fprintf(stderr, "OutOfMemoryError\n"); + vmPrintTrace(t); + abort(); + } + } + + // printTrace(t, e); + + popResources(t); + + t->checkpoint->unwind(); + + abort(t); +} + +inline void NO_RETURN throwNew(Thread* t, + Gc::Type type, + GcString* message = 0, + object trace = 0, + GcThrowable* cause = 0) +{ + throw_(t, makeThrowable(t, type, message, trace, cause)); +} + +inline void NO_RETURN + throwNew(Thread* t, Gc::Type type, const char* format, ...) +{ + int size = 256; + while (true) { + va_list a; + va_start(a, format); + GcThrowable* r = makeThrowableV(t, type, format, a, size); + va_end(a); + + if (r) { + throw_(t, r); + } else { + size *= 2; + } + } +} + +object findInHierarchyOrNull( + Thread* t, + GcClass* class_, + GcByteArray* name, + GcByteArray* spec, + object (*find)(Thread*, GcClass*, GcByteArray*, GcByteArray*)); + +inline object findInHierarchy( + Thread* t, + GcClass* class_, + GcByteArray* name, + GcByteArray* spec, + object (*find)(Thread*, GcClass*, GcByteArray*, GcByteArray*), + Gc::Type errorType, + bool throw_ = true) +{ + object o = findInHierarchyOrNull(t, class_, name, spec, find); + + if (throw_ and o == 0) { + throwNew(t, + errorType, + "%s %s not found in %s", + name->body().begin(), + spec->body().begin(), + class_->name()->body().begin()); + } + + return o; +} + +inline GcMethod* findMethod(Thread* t, + GcClass* class_, + GcByteArray* name, + GcByteArray* spec) +{ + return cast( + t, + findInHierarchy( + t, class_, name, spec, findMethodInClass, GcNoSuchMethodError::Type)); +} + +inline GcMethod* findMethodOrNull(Thread* t, + GcClass* class_, + const char* name, + const char* spec) +{ + PROTECT(t, class_); + GcByteArray* n = makeByteArray(t, "%s", name); + PROTECT(t, n); + GcByteArray* s = makeByteArray(t, "%s", spec); + return cast( + t, findInHierarchyOrNull(t, class_, n, s, findMethodInClass)); +} + +inline GcMethod* findVirtualMethod(Thread* t, GcMethod* method, GcClass* class_) +{ + return cast( + t, cast(t, class_->virtualTable())->body()[method->offset()]); +} + +inline GcMethod* findInterfaceMethod(Thread* t, + GcMethod* method, + GcClass* class_) +{ + if (UNLIKELY(class_->vmFlags() & BootstrapFlag)) { + PROTECT(t, method); + PROTECT(t, class_); + + resolveSystemClass(t, roots(t)->bootLoader(), class_->name()); + } + + GcClass* interface = method->class_(); + GcArray* itable = cast(t, class_->interfaceTable()); + for (unsigned i = 0; i < itable->length(); i += 2) { + if (itable->body()[i] == interface) { + return cast( + t, cast(t, itable->body()[i + 1])->body()[method->offset()]); + } + } + abort(t); +} + +inline unsigned objectArrayLength(Thread* t UNUSED, object array) +{ + assertT(t, objectClass(t, array)->fixedSize() == BytesPerWord * 2); + assertT(t, objectClass(t, array)->arrayElementSize() == BytesPerWord); + return fieldAtOffset(array, BytesPerWord); +} + +inline object& objectArrayBody(Thread* t UNUSED, object array, unsigned index) +{ + assertT(t, objectClass(t, array)->fixedSize() == BytesPerWord * 2); + assertT(t, objectClass(t, array)->arrayElementSize() == BytesPerWord); + assertT( + t, + objectClass(t, array)->objectMask() + == cast(t, t->m->types->body()[GcArray::Type])->objectMask()); + return fieldAtOffset(array, ArrayBody + (index * BytesPerWord)); +} + +unsigned parameterFootprint(Thread* t, const char* s, bool static_); + +void addFinalizer(Thread* t, object target, void (*finalize)(Thread*, object)); + +inline bool acquireSystem(Thread* t, Thread* target) +{ + ACQUIRE_RAW(t, t->m->stateLock); + + if (t->state != Thread::JoinedState) { + target->setFlag(Thread::SystemFlag); + return true; + } else { + return false; + } +} + +inline void releaseSystem(Thread* t, Thread* target) +{ + ACQUIRE_RAW(t, t->m->stateLock); + + assertT(t, t->state != Thread::JoinedState); + + target->clearFlag(Thread::SystemFlag); +} + +inline bool atomicCompareAndSwapObject(Thread* t, + object target, + unsigned offset, + object old, + object new_) +{ + if (atomicCompareAndSwap(&fieldAtOffset(target, offset), + reinterpret_cast(old), + reinterpret_cast(new_))) { + mark(t, target, offset); + return true; + } else { + return false; + } +} + +// The following two methods (monitorAtomicAppendAcquire and +// monitorAtomicPollAcquire) use the Michael and Scott Non-Blocking +// Queue Algorithm: http://www.cs.rochester.edu/u/michael/PODC96.html + +inline void monitorAtomicAppendAcquire(Thread* t, + GcMonitor* monitor, + GcMonitorNode* node) +{ + if (node == 0) { + PROTECT(t, monitor); + + node = makeMonitorNode(t, t, 0); + } + + while (true) { + GcMonitorNode* tail = cast(t, monitor->acquireTail()); + + loadMemoryBarrier(); + + object next = tail->next(); + + loadMemoryBarrier(); + + if (tail == cast(t, monitor->acquireTail())) { + if (next) { + atomicCompareAndSwapObject(t, monitor, MonitorAcquireTail, tail, next); + } else if (atomicCompareAndSwapObject( + t, tail, MonitorNodeNext, 0, node)) { + atomicCompareAndSwapObject(t, monitor, MonitorAcquireTail, tail, node); + return; + } + } + } +} + +inline Thread* monitorAtomicPollAcquire(Thread* t, + GcMonitor* monitor, + bool remove) +{ + while (true) { + GcMonitorNode* head = cast(t, monitor->acquireHead()); + + loadMemoryBarrier(); + + GcMonitorNode* tail = cast(t, monitor->acquireTail()); + + loadMemoryBarrier(); + + GcMonitorNode* next = cast(t, head->next()); + + loadMemoryBarrier(); + + if (head == cast(t, monitor->acquireHead())) { + if (head == tail) { + if (next) { + atomicCompareAndSwapObject( + t, monitor, MonitorAcquireTail, tail, next); + } else { + return 0; + } + } else { + Thread* value = static_cast(next->value()); + if ((not remove) or atomicCompareAndSwapObject( + t, monitor, MonitorAcquireHead, head, next)) { + return value; + } + } + } + } +} + +inline bool monitorTryAcquire(Thread* t, GcMonitor* monitor) +{ + if (monitor->owner() == t + or (monitorAtomicPollAcquire(t, monitor, false) == 0 + and atomicCompareAndSwap( + reinterpret_cast(&monitor->owner()), + 0, + reinterpret_cast(t)))) { + ++monitor->depth(); + return true; + } else { + return false; + } +} + +inline void monitorAcquire(Thread* t, + GcMonitor* monitor, + GcMonitorNode* node = 0) +{ + if (not monitorTryAcquire(t, monitor)) { + PROTECT(t, monitor); + PROTECT(t, node); + + ACQUIRE(t, t->lock); + + monitorAtomicAppendAcquire(t, monitor, node); + + // note that we don't try to acquire the lock until we're first in + // line, both because it's fair and because we don't support + // removing elements from arbitrary positions in the queue + + while (not(t == monitorAtomicPollAcquire(t, monitor, false) + and atomicCompareAndSwap( + reinterpret_cast(&monitor->owner()), + 0, + reinterpret_cast(t)))) { + ENTER(t, Thread::IdleState); + + t->lock->wait(t->systemThread, 0); + } + + expect(t, t == monitorAtomicPollAcquire(t, monitor, true)); + + ++monitor->depth(); + } + + assertT(t, monitor->owner() == t); +} + +inline void monitorRelease(Thread* t, GcMonitor* monitor) +{ + expect(t, monitor->owner() == t); + + if (--monitor->depth() == 0) { + monitor->owner() = 0; + + storeLoadMemoryBarrier(); + + Thread* next = monitorAtomicPollAcquire(t, monitor, false); + + if (next and acquireSystem(t, next)) { + ACQUIRE(t, next->lock); + + next->lock->notify(t->systemThread); + + releaseSystem(t, next); + } + } +} + +inline void monitorAppendWait(Thread* t, GcMonitor* monitor) +{ + assertT(t, monitor->owner() == t); + + expect(t, (t->getFlags() & Thread::WaitingFlag) == 0); + expect(t, t->waitNext == 0); + + t->setFlag(Thread::WaitingFlag); + + if (monitor->waitTail()) { + static_cast(monitor->waitTail())->waitNext = t; + } else { + monitor->waitHead() = t; + } + + monitor->waitTail() = t; +} + +inline void monitorRemoveWait(Thread* t, GcMonitor* monitor) +{ + assertT(t, monitor->owner() == t); + + Thread* previous = 0; + for (Thread* current = static_cast(monitor->waitHead()); current; + current = current->waitNext) { + if (t == current) { + if (t == monitor->waitHead()) { + monitor->waitHead() = t->waitNext; + } else { + previous->waitNext = t->waitNext; + } + + if (t == monitor->waitTail()) { + assertT(t, t->waitNext == 0); + monitor->waitTail() = previous; + } + + t->waitNext = 0; + t->clearFlag(Thread::WaitingFlag); + + return; + } else { + previous = current; + } + } + + abort(t); +} + +inline bool monitorFindWait(Thread* t, GcMonitor* monitor) +{ + assertT(t, monitor->owner() == t); + + for (Thread* current = static_cast(monitor->waitHead()); current; + current = current->waitNext) { + if (t == current) { + return true; + } + } + + return false; +} + +inline bool monitorWait(Thread* t, GcMonitor* monitor, int64_t time) +{ + expect(t, monitor->owner() == t); + + bool interrupted; + unsigned depth; + + PROTECT(t, monitor); + + // pre-allocate monitor node so we don't get an OutOfMemoryError + // when we try to re-acquire the monitor below + GcMonitorNode* monitorNode = makeMonitorNode(t, t, 0); + PROTECT(t, monitorNode); + + { + ACQUIRE(t, t->lock); + + monitorAppendWait(t, monitor); + + depth = monitor->depth(); + monitor->depth() = 1; + + monitorRelease(t, monitor); + + ENTER(t, Thread::IdleState); + + interrupted = t->lock->waitAndClearInterrupted(t->systemThread, time); + } + + monitorAcquire(t, monitor, monitorNode); + + monitor->depth() = depth; + + if (t->getFlags() & Thread::WaitingFlag) { + monitorRemoveWait(t, monitor); + } else { + expect(t, not monitorFindWait(t, monitor)); + } + + assertT(t, monitor->owner() == t); + + return interrupted; +} + +inline Thread* monitorPollWait(Thread* t UNUSED, GcMonitor* monitor) +{ + assertT(t, monitor->owner() == t); + + Thread* next = static_cast(monitor->waitHead()); + + if (next) { + monitor->waitHead() = next->waitNext; + next->clearFlag(Thread::WaitingFlag); + next->waitNext = 0; + if (next == monitor->waitTail()) { + monitor->waitTail() = 0; + } + } else { + assertT(t, monitor->waitTail() == 0); + } + + return next; +} + +inline bool monitorNotify(Thread* t, GcMonitor* monitor) +{ + expect(t, monitor->owner() == t); + + Thread* next = monitorPollWait(t, monitor); + + if (next) { + ACQUIRE(t, next->lock); + + next->lock->notify(t->systemThread); + + return true; + } else { + return false; + } +} + +inline void monitorNotifyAll(Thread* t, GcMonitor* monitor) +{ + PROTECT(t, monitor); + + while (monitorNotify(t, monitor)) { + } +} + +class ObjectMonitorResource { + public: + ObjectMonitorResource(Thread* t, GcMonitor* o) + : o(o), protector(t, &(this->o)) + { + monitorAcquire(protector.t, o); + } + + ~ObjectMonitorResource() + { + monitorRelease(protector.t, o); + } + + private: + GcMonitor* o; + Thread::SingleProtector protector; +}; + +GcMonitor* objectMonitor(Thread* t, object o, bool createNew); + +inline void acquire(Thread* t, object o) +{ + unsigned hash; + if (DebugMonitors) { + hash = objectHash(t, o); + } + + GcMonitor* m = objectMonitor(t, o, true); + + if (DebugMonitors) { + fprintf(stderr, "thread %p acquires %p for %x\n", t, m, hash); + } + + monitorAcquire(t, m); +} + +inline void release(Thread* t, object o) +{ + unsigned hash; + if (DebugMonitors) { + hash = objectHash(t, o); + } + + GcMonitor* m = objectMonitor(t, o, false); + + if (DebugMonitors) { + fprintf(stderr, "thread %p releases %p for %x\n", t, m, hash); + } + + monitorRelease(t, m); +} + +inline void wait(Thread* t, object o, int64_t milliseconds) +{ + unsigned hash; + if (DebugMonitors) { + hash = objectHash(t, o); + } + + GcMonitor* m = objectMonitor(t, o, false); + + if (DebugMonitors) { + fprintf(stderr, + "thread %p waits %d millis on %p for %x\n", + t, + static_cast(milliseconds), + m, + hash); + } + + if (m and m->owner() == t) { + PROTECT(t, m); + + bool interrupted = monitorWait(t, m, milliseconds); + + if (interrupted) { + if (t->m->alive or (t->getFlags() & Thread::DaemonFlag) == 0) { + t->m->classpath->clearInterrupted(t); + throwNew(t, GcInterruptedException::Type); + } else { + throw_(t, roots(t)->shutdownInProgress()); + } + } + } else { + throwNew(t, GcIllegalMonitorStateException::Type); + } + + if (DebugMonitors) { + fprintf(stderr, "thread %p wakes up on %p for %x\n", t, m, hash); + } + + stress(t); +} + +inline void notify(Thread* t, object o) +{ + unsigned hash; + if (DebugMonitors) { + hash = objectHash(t, o); + } + + GcMonitor* m = objectMonitor(t, o, false); + + if (DebugMonitors) { + fprintf(stderr, "thread %p notifies on %p for %x\n", t, m, hash); + } + + if (m and m->owner() == t) { + monitorNotify(t, m); + } else { + throwNew(t, GcIllegalMonitorStateException::Type); + } +} + +inline void notifyAll(Thread* t, object o) +{ + GcMonitor* m = objectMonitor(t, o, false); + + if (DebugMonitors) { + fprintf(stderr, + "thread %p notifies all on %p for %x\n", + t, + m, + objectHash(t, o)); + } + + if (m and m->owner() == t) { + monitorNotifyAll(t, m); + } else { + throwNew(t, GcIllegalMonitorStateException::Type); + } +} + +inline void interrupt(Thread* t, Thread* target) +{ + if (acquireSystem(t, target)) { + target->systemThread->interrupt(); + releaseSystem(t, target); + } +} + +inline bool getAndClearInterrupted(Thread* t, Thread* target) +{ + if (acquireSystem(t, target)) { + bool result = target->systemThread->getAndClearInterrupted(); + releaseSystem(t, target); + return result; + } else { + return false; + } +} + +inline bool exceptionMatch(Thread* t, GcClass* type, GcThrowable* exception) +{ + return type == 0 or (exception != roots(t)->shutdownInProgress() + and instanceOf(t, type, t->exception)); +} + +object intern(Thread* t, object s); + +object clone(Thread* t, object o); + +void walk(Thread* t, Heap::Walker* w, object o, unsigned start); + +int walkNext(Thread* t, object o, int previous); + +void visitRoots(Machine* m, Heap::Visitor* v); + +inline jobject makeLocalReference(Thread* t, object o) +{ + return t->m->processor->makeLocalReference(t, o); +} + +inline void disposeLocalReference(Thread* t, jobject r) +{ + t->m->processor->disposeLocalReference(t, r); +} + +inline bool methodVirtual(Thread* t UNUSED, GcMethod* method) +{ + return (method->flags() & (ACC_STATIC | ACC_PRIVATE)) == 0 + and method->name()->body()[0] != '<'; +} + +inline unsigned singletonMaskSize(unsigned count, unsigned bitsPerWord) +{ + if (count) { + return ceilingDivide(count + 2, bitsPerWord); + } + return 0; +} + +inline unsigned singletonMaskSize(unsigned count) +{ + return singletonMaskSize(count, BitsPerWord); +} + +inline unsigned singletonMaskSize(Thread* t UNUSED, GcSingleton* singleton) +{ + unsigned length = singleton->length(); + if (length) { + return ceilingDivide(length + 2, BitsPerWord + 1); + } + return 0; +} + +inline unsigned singletonCount(Thread* t, GcSingleton* singleton) +{ + return singleton->length() - singletonMaskSize(t, singleton); +} + +inline uint32_t* singletonMask(Thread* t UNUSED, GcSingleton* singleton) +{ + assertT(t, singleton->length()); + return reinterpret_cast( + &singleton->body()[singletonCount(t, singleton)]); +} + +inline void singletonMarkObject(uint32_t* mask, unsigned index) +{ + mask[(index + 2) / 32] |= (static_cast(1) << ((index + 2) % 32)); +} + +inline void singletonMarkObject(Thread* t, + GcSingleton* singleton, + unsigned index) +{ + singletonMarkObject(singletonMask(t, singleton), index); +} + +inline bool singletonIsObject(Thread* t, GcSingleton* singleton, unsigned index) +{ + assertT(t, index < singletonCount(t, singleton)); + + return (singletonMask(t, singleton)[(index + 2) / 32] + & (static_cast(1) << ((index + 2) % 32))) != 0; +} + +inline object& singletonObject(Thread* t UNUSED, + GcSingleton* singleton, + unsigned index) +{ + assertT(t, singletonIsObject(t, singleton, index)); + return reinterpret_cast(singleton->body()[index]); +} + +inline uintptr_t& singletonValue(Thread* t UNUSED, + GcSingleton* singleton, + unsigned index) +{ + assertT(t, not singletonIsObject(t, singleton, index)); + return singleton->body()[index]; +} + +inline GcSingleton* makeSingletonOfSize(Thread* t, unsigned count) +{ + GcSingleton* o = makeSingleton(t, count + singletonMaskSize(count)); + assertT(t, o->length() == count + singletonMaskSize(t, o)); + if (count) { + singletonMask(t, o)[0] = 1; + } + return o; +} + +inline void singletonSetBit(Thread* t, + GcSingleton* singleton, + unsigned start, + unsigned index) +{ + singletonValue(t, singleton, start + (index / BitsPerWord)) + |= static_cast(1) << (index % BitsPerWord); +} + +inline bool singletonBit(Thread* t, + GcSingleton* singleton, + unsigned start, + unsigned index) +{ + return (singletonValue(t, singleton, start + (index / BitsPerWord)) + & (static_cast(1) << (index % BitsPerWord))) != 0; +} + +inline unsigned poolMaskSize(unsigned count, unsigned bitsPerWord) +{ + return ceilingDivide(count, bitsPerWord); +} + +inline unsigned poolMaskSize(unsigned count) +{ + return poolMaskSize(count, BitsPerWord); +} + +inline unsigned poolMaskSize(Thread* t, GcSingleton* pool) +{ + return ceilingDivide(singletonCount(t, pool), BitsPerWord + 1); +} + +inline unsigned poolSize(Thread* t, GcSingleton* pool) +{ + return singletonCount(t, pool) - poolMaskSize(t, pool); +} + +inline GcClass* resolveClassInObject(Thread* t, + GcClassLoader* loader, + object container, + unsigned classOffset, + bool throw_ = true) +{ + object o = fieldAtOffset(container, classOffset); + + loadMemoryBarrier(); + + if (objectClass(t, o) == type(t, GcByteArray::Type)) { + GcByteArray* name = cast(t, o); + PROTECT(t, container); + + GcClass* c = resolveClass(t, loader, name, throw_); + + if (c) { + storeStoreMemoryBarrier(); + + setField(t, container, classOffset, c); + } + + return c; + } + return cast(t, o); +} + +inline GcClass* resolveClassInPool(Thread* t, + GcClassLoader* loader, + GcMethod* method, + unsigned index, + bool throw_ = true) +{ + object o = singletonObject(t, method->code()->pool(), index); + + loadMemoryBarrier(); + + if (objectClass(t, o) == type(t, GcReference::Type)) { + PROTECT(t, method); + + GcClass* c + = resolveClass(t, loader, cast(t, o)->name(), throw_); + + if (c) { + storeStoreMemoryBarrier(); + + method->code()->pool()->setBodyElement( + t, index, reinterpret_cast(c)); + } + return c; + } + return cast(t, o); +} + +inline GcClass* resolveClassInPool(Thread* t, + GcMethod* method, + unsigned index, + bool throw_ = true) +{ + return resolveClassInPool( + t, method->class_()->loader(), method, index, throw_); +} + +inline object resolve( + Thread* t, + GcClassLoader* loader, + GcSingleton* pool, + unsigned index, + object (*find)(vm::Thread*, GcClass*, GcByteArray*, GcByteArray*), + Gc::Type errorType, + bool throw_ = true) +{ + object o = singletonObject(t, pool, index); + + loadMemoryBarrier(); + + if (objectClass(t, o) == type(t, GcReference::Type)) { + PROTECT(t, pool); + + GcReference* reference = cast(t, o); + PROTECT(t, reference); + + GcClass* class_ + = resolveClassInObject(t, loader, o, ReferenceClass, throw_); + + if (class_) { + o = findInHierarchy(t, + class_, + reference->name(), + reference->spec(), + find, + errorType, + throw_); + + if (o) { + storeStoreMemoryBarrier(); + + pool->setBodyElement(t, index, reinterpret_cast(o)); + } + } else { + o = 0; + } + } + + return o; +} + +inline GcField* resolveField(Thread* t, + GcClassLoader* loader, + GcMethod* method, + unsigned index, + bool throw_ = true) +{ + return cast(t, + resolve(t, + loader, + method->code()->pool(), + index, + findFieldInClass, + GcNoSuchFieldError::Type, + throw_)); +} + +inline GcField* resolveField(Thread* t, + GcMethod* method, + unsigned index, + bool throw_ = true) +{ + return resolveField(t, method->class_()->loader(), method, index, throw_); +} + +inline void acquireFieldForRead(Thread* t, GcField* field) +{ + if (UNLIKELY( + (field->flags() & ACC_VOLATILE) and BytesPerWord == 4 + and (field->code() == DoubleField or field->code() == LongField))) { + acquire(t, field); + } +} + +inline void releaseFieldForRead(Thread* t, GcField* field) +{ + if (UNLIKELY(field->flags() & ACC_VOLATILE)) { + if (BytesPerWord == 4 + and (field->code() == DoubleField or field->code() == LongField)) { + release(t, field); + } else { + loadMemoryBarrier(); + } + } +} + +class FieldReadResource { + public: + FieldReadResource(Thread* t, GcField* o) : o(o), protector(t, &(this->o)) + { + acquireFieldForRead(protector.t, o); + } + + ~FieldReadResource() + { + releaseFieldForRead(protector.t, o); + } + + private: + GcField* o; + Thread::SingleProtector protector; +}; + +inline void acquireFieldForWrite(Thread* t, GcField* field) +{ + if (UNLIKELY(field->flags() & ACC_VOLATILE)) { + if (BytesPerWord == 4 + and (field->code() == DoubleField or field->code() == LongField)) { + acquire(t, field); + } else { + storeStoreMemoryBarrier(); + } + } +} + +inline void releaseFieldForWrite(Thread* t, GcField* field) +{ + if (UNLIKELY(field->flags() & ACC_VOLATILE)) { + if (BytesPerWord == 4 + and (field->code() == DoubleField or field->code() == LongField)) { + release(t, field); + } else { + storeLoadMemoryBarrier(); + } + } +} + +class FieldWriteResource { + public: + FieldWriteResource(Thread* t, GcField* o) : o(o), protector(t, &(this->o)) + { + acquireFieldForWrite(protector.t, o); + } + + ~FieldWriteResource() + { + releaseFieldForWrite(protector.t, o); + } + + private: + GcField* o; + Thread::SingleProtector protector; +}; + +inline GcMethod* resolveMethod(Thread* t, + GcClassLoader* loader, + GcMethod* method, + unsigned index, + bool throw_ = true) +{ + return cast(t, + resolve(t, + loader, + method->code()->pool(), + index, + findMethodInClass, + GcNoSuchMethodError::Type, + throw_)); +} + +inline GcMethod* resolveMethod(Thread* t, + GcMethod* method, + unsigned index, + bool throw_ = true) +{ + return resolveMethod(t, method->class_()->loader(), method, index, throw_); +} + +GcVector* vectorAppend(Thread*, GcVector*, object); + +inline GcClassRuntimeData* getClassRuntimeDataIfExists(Thread* t, GcClass* c) +{ + if (c->runtimeDataIndex()) { + return cast( + t, + roots(t)->classRuntimeDataTable()->body()[c->runtimeDataIndex() - 1]); + } else { + return 0; + } +} + +inline GcClassRuntimeData* getClassRuntimeData(Thread* t, GcClass* c) +{ + if (c->runtimeDataIndex() == 0) { + PROTECT(t, c); + + ACQUIRE(t, t->m->classLock); + + if (c->runtimeDataIndex() == 0) { + GcClassRuntimeData* runtimeData = makeClassRuntimeData(t, 0, 0, 0, 0); + + { + GcVector* v + = vectorAppend(t, roots(t)->classRuntimeDataTable(), runtimeData); + // sequence point, for gc (don't recombine statements) + roots(t)->setClassRuntimeDataTable(t, v); + } + + c->runtimeDataIndex() = roots(t)->classRuntimeDataTable()->size(); + } + } + + return cast( + t, roots(t)->classRuntimeDataTable()->body()[c->runtimeDataIndex() - 1]); +} + +inline GcMethodRuntimeData* getMethodRuntimeData(Thread* t, GcMethod* method) +{ + int index = method->runtimeDataIndex(); + + loadMemoryBarrier(); + + if (index == 0) { + PROTECT(t, method); + + ACQUIRE(t, t->m->classLock); + + if (method->runtimeDataIndex() == 0) { + GcMethodRuntimeData* runtimeData = makeMethodRuntimeData(t, 0); + + { + GcVector* v + = vectorAppend(t, roots(t)->methodRuntimeDataTable(), runtimeData); + // sequence point, for gc (don't recombine statements) + roots(t)->setMethodRuntimeDataTable(t, v); + } + + storeStoreMemoryBarrier(); + + method->runtimeDataIndex() = roots(t)->methodRuntimeDataTable()->size(); + } + } + + return cast(t, + roots(t)->methodRuntimeDataTable()->body() + [method->runtimeDataIndex() - 1]); +} + +inline GcJclass* getJClass(Thread* t, GcClass* c) +{ + PROTECT(t, c); + + GcJclass* jclass = cast(t, getClassRuntimeData(t, c)->jclass()); + + loadMemoryBarrier(); + + if (jclass == 0) { + ACQUIRE(t, t->m->classLock); + + jclass = cast(t, getClassRuntimeData(t, c)->jclass()); + if (jclass == 0) { + jclass = t->m->classpath->makeJclass(t, c); + + storeStoreMemoryBarrier(); + + getClassRuntimeData(t, c)->setJclass(t, jclass); + } + } + + return jclass; +} + +inline GcClass* primitiveClass(Thread* t, char name) +{ + switch (name) { + case 'B': + return type(t, GcJbyte::Type); + case 'C': + return type(t, GcJchar::Type); + case 'D': + return type(t, GcJdouble::Type); + case 'F': + return type(t, GcJfloat::Type); + case 'I': + return type(t, GcJint::Type); + case 'J': + return type(t, GcJlong::Type); + case 'S': + return type(t, GcJshort::Type); + case 'V': + return type(t, GcJvoid::Type); + case 'Z': + return type(t, GcJboolean::Type); + default: + throwNew(t, GcIllegalArgumentException::Type); + } +} + +inline void registerNative(Thread* t, GcMethod* method, void* function) +{ + PROTECT(t, method); + + expect(t, method->flags() & ACC_NATIVE); + + GcNative* native = makeNative(t, function, false); + PROTECT(t, native); + + GcMethodRuntimeData* runtimeData = getMethodRuntimeData(t, method); + + // ensure other threads only see the methodRuntimeDataNative field + // populated once the object it points to has been populated: + storeStoreMemoryBarrier(); + + runtimeData->setNative(t, native); +} + +inline void unregisterNatives(Thread* t, GcClass* c) +{ + GcArray* table = cast(t, c->methodTable()); + if (table) { + for (unsigned i = 0; i < table->length(); ++i) { + GcMethod* method = cast(t, table->body()[i]); + if (method->flags() & ACC_NATIVE) { + getMethodRuntimeData(t, method)->setNative(t, 0); + } + } + } +} + +void populateMultiArray(Thread* t, + object array, + int32_t* counts, + unsigned index, + unsigned dimensions); + +GcMethod* getCaller(Thread* t, unsigned target, bool skipMethodInvoke = false); + +GcClass* defineClass(Thread* t, + GcClassLoader* loader, + const uint8_t* buffer, + unsigned length); + +inline GcMethod* methodClone(Thread* t, GcMethod* method) +{ + return makeMethod(t, + method->vmFlags(), + method->returnCode(), + method->parameterCount(), + method->parameterFootprint(), + method->flags(), + method->offset(), + method->nativeID(), + method->runtimeDataIndex(), + method->name(), + method->spec(), + method->addendum(), + method->class_(), + method->code()); +} + +inline uint64_t exceptionHandler(uint64_t start, + uint64_t end, + uint64_t ip, + uint64_t catchType) +{ + return (start << 48) | (end << 32) | (ip << 16) | catchType; +} + +inline unsigned exceptionHandlerStart(uint64_t eh) +{ + return eh >> 48; +} + +inline unsigned exceptionHandlerEnd(uint64_t eh) +{ + return (eh >> 32) & 0xFFFF; +} + +inline unsigned exceptionHandlerIp(uint64_t eh) +{ + return (eh >> 16) & 0xFFFF; +} + +inline unsigned exceptionHandlerCatchType(uint64_t eh) +{ + return eh & 0xFFFF; +} + +inline uint64_t lineNumber(uint64_t ip, uint64_t line) +{ + return (ip << 32) | line; +} + +inline unsigned lineNumberIp(uint64_t ln) +{ + return ln >> 32; +} + +inline unsigned lineNumberLine(uint64_t ln) +{ + return ln & 0xFFFFFFFF; +} + +object interruptLock(Thread* t, GcThread* thread); + +void clearInterrupted(Thread* t); + +void threadInterrupt(Thread* t, GcThread* thread); + +bool threadIsInterrupted(Thread* t, GcThread* thread, bool clear); + +inline FILE* errorLog(Thread* t) +{ + if (t->m->errorLog == 0) { + const char* path = findProperty(t, "avian.error.log"); + if (path) { + t->m->errorLog = vm::fopen(path, "wb"); + } else { + t->m->errorLog = stderr; + } + } + + return t->m->errorLog; +} + +} // namespace vm + +AVIAN_EXPORT void* vmAddressFromLine(vm::Thread* t, + vm::object m, + unsigned line); + +#endif // MACHINE_H diff --git a/sgx-jvm/avian/src/avian/process.h b/sgx-jvm/avian/src/avian/process.h new file mode 100644 index 0000000000..531927f421 --- /dev/null +++ b/sgx-jvm/avian/src/avian/process.h @@ -0,0 +1,61 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef PROCESS_H +#define PROCESS_H + +#include "avian/common.h" +#include +#include "avian/machine.h" +#include "avian/constants.h" + +namespace vm { + +inline int16_t codeReadInt16(Thread* t UNUSED, GcCode* code, unsigned& ip) +{ + uint8_t v1 = code->body()[ip++]; + uint8_t v2 = code->body()[ip++]; + return ((v1 << 8) | v2); +} + +inline int32_t codeReadInt32(Thread* t UNUSED, GcCode* code, unsigned& ip) +{ + uint8_t v1 = code->body()[ip++]; + uint8_t v2 = code->body()[ip++]; + uint8_t v3 = code->body()[ip++]; + uint8_t v4 = code->body()[ip++]; + return ((v1 << 24) | (v2 << 16) | (v3 << 8) | v4); +} + +inline bool isSuperclass(Thread* t UNUSED, GcClass* class_, GcClass* base) +{ + for (GcClass* oc = base->super(); oc; oc = oc->super()) { + if (oc == class_) { + return true; + } + } + return false; +} + +inline bool isSpecialMethod(Thread* t, GcMethod* method, GcClass* class_) +{ + return (class_->flags() & ACC_SUPER) + and strcmp(reinterpret_cast(""), + method->name()->body().begin()) != 0 + and isSuperclass(t, method->class_(), class_); +} + +void resolveNative(Thread* t, GcMethod* method); + +int findLineNumber(Thread* t, GcMethod* method, unsigned ip); + +} // namespace vm + +#endif // PROCESS_H diff --git a/sgx-jvm/avian/src/avian/processor.h b/sgx-jvm/avian/src/avian/processor.h new file mode 100644 index 0000000000..7e9df08ae3 --- /dev/null +++ b/sgx-jvm/avian/src/avian/processor.h @@ -0,0 +1,241 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef PROCESSOR_H +#define PROCESSOR_H + +#include "avian/common.h" +#include +#include +#include +#include "bootimage.h" +#include "avian/heapwalk.h" +#include "avian/zone.h" + +namespace avian { +namespace codegen { +class DelayedPromise; +} + +namespace util { +template +class Slice; +} +} + +namespace vm { + +class GcByteArray; +class GcCode; +class GcClass; +class GcMethod; +class GcMethodAddendum; +class GcIntArray; +class GcContinuation; +class GcThrowable; +class GcThread; +class GcClassAddendum; +class GcClassLoader; +class GcArray; +class GcSingleton; +class GcTriple; + +class Processor { + public: + class StackWalker; + + class StackVisitor { + public: + virtual bool visit(StackWalker* walker) = 0; + }; + + class StackWalker { + public: + virtual void walk(StackVisitor* v) = 0; + + virtual GcMethod* method() = 0; + + virtual int ip() = 0; + + virtual unsigned count() = 0; + }; + + class CompilationHandler { + public: + virtual void compiled(const void* code, + unsigned size, + unsigned frameSize, + const char* name) = 0; + + virtual void dispose() = 0; + }; + + virtual Thread* makeThread(Machine* m, GcThread* javaThread, Thread* parent) + = 0; + + virtual GcMethod* makeMethod(Thread* t, + uint8_t vmFlags, + uint8_t returnCode, + uint8_t parameterCount, + uint8_t parameterFootprint, + uint16_t flags, + uint16_t offset, + GcByteArray* name, + GcByteArray* spec, + GcMethodAddendum* addendum, + GcClass* class_, + GcCode* code) = 0; + + virtual GcClass* makeClass(Thread* t, + uint16_t flags, + uint16_t vmFlags, + uint16_t fixedSize, + uint8_t arrayElementSize, + uint8_t arrayDimensions, + GcClass* arrayElementClass, + GcIntArray* objectMask, + GcByteArray* name, + GcByteArray* sourceFile, + GcClass* super, + object interfaceTable, + object virtualTable, + object fieldTable, + object methodTable, + GcClassAddendum* addendum, + GcSingleton* staticTable, + GcClassLoader* loader, + unsigned vtableLength) = 0; + + virtual void initVtable(Thread* t, GcClass* c) = 0; + + virtual void visitObjects(Thread* t, Heap::Visitor* v) = 0; + + virtual void walkStack(Thread* t, StackVisitor* v) = 0; + + virtual int lineNumber(Thread* t, GcMethod* method, int ip) = 0; + + virtual object* makeLocalReference(Thread* t, object o) = 0; + + virtual void disposeLocalReference(Thread* t, object* r) = 0; + + virtual bool pushLocalFrame(Thread* t, unsigned capacity) = 0; + + virtual void popLocalFrame(Thread* t) = 0; + + virtual object invokeArray(Thread* t, + GcMethod* method, + object this_, + object arguments) = 0; + + virtual object invokeArray(Thread* t, + GcMethod* method, + object this_, + const jvalue* arguments) = 0; + + virtual object invokeList(Thread* t, + GcMethod* method, + object this_, + bool indirectObjects, + va_list arguments) = 0; + + virtual object invokeList(Thread* t, + GcClassLoader* loader, + const char* className, + const char* methodName, + const char* methodSpec, + object this_, + va_list arguments) = 0; + + virtual void dispose(Thread* t) = 0; + + virtual void dispose() = 0; + + virtual object getStackTrace(Thread* t, Thread* target) = 0; + + virtual void initialize(BootImage* image, avian::util::Slice code) + = 0; + + virtual void addCompilationHandler(CompilationHandler* handler) = 0; + + virtual void compileMethod(Thread* t, + Zone* zone, + GcTriple** constants, + GcTriple** calls, + avian::codegen::DelayedPromise** addresses, + GcMethod* method, + OffsetResolver* resolver, + Machine* hostVM) = 0; + + virtual void visitRoots(Thread* t, HeapWalker* w) = 0; + + virtual void normalizeVirtualThunks(Thread* t) = 0; + + virtual unsigned* makeCallTable(Thread* t, HeapWalker* w) = 0; + + virtual void boot(Thread* t, BootImage* image, uint8_t* code) = 0; + + virtual void callWithCurrentContinuation(Thread* t, object receiver) = 0; + + virtual void dynamicWind(Thread* t, object before, object thunk, object after) + = 0; + + virtual void feedResultToContinuation(Thread* t, + GcContinuation* continuation, + object result) = 0; + + virtual void feedExceptionToContinuation(Thread* t, + GcContinuation* continuation, + GcThrowable* exception) = 0; + + virtual void walkContinuationBody(Thread* t, + Heap::Walker* w, + object o, + unsigned start) = 0; + + object invoke(Thread* t, GcMethod* method, object this_, ...) + { + va_list a; + va_start(a, this_); + + object r = invokeList(t, method, this_, false, a); + + va_end(a); + + return r; + } + + object invoke(Thread* t, + GcClassLoader* loader, + const char* className, + const char* methodName, + const char* methodSpec, + object this_, + ...) + { + va_list a; + va_start(a, this_); + + object r + = invokeList(t, loader, className, methodName, methodSpec, this_, a); + + va_end(a); + + return r; + } +}; + +Processor* makeProcessor(System* system, + avian::util::Allocator* allocator, + const char* crashDumpDirectory, + bool useNativeFeatures); + +} // namespace vm + +#endif // PROCESSOR_H diff --git a/sgx-jvm/avian/src/avian/target-fields.h b/sgx-jvm/avian/src/avian/target-fields.h new file mode 100644 index 0000000000..ac4402ece1 --- /dev/null +++ b/sgx-jvm/avian/src/avian/target-fields.h @@ -0,0 +1,64 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_TARGET_FIELDS_H +#define AVIAN_TARGET_FIELDS_H + +#ifdef TARGET_BYTES_PER_WORD +#if (TARGET_BYTES_PER_WORD == 8) + +#define TARGET_THREAD_EXCEPTION 80 +#define TARGET_THREAD_EXCEPTIONSTACKADJUSTMENT 2264 +#define TARGET_THREAD_EXCEPTIONOFFSET 2272 +#define TARGET_THREAD_EXCEPTIONHANDLER 2280 + +#define TARGET_THREAD_IP 2224 +#define TARGET_THREAD_STACK 2232 +#define TARGET_THREAD_NEWSTACK 2240 +#define TARGET_THREAD_SCRATCH 2248 +#define TARGET_THREAD_CONTINUATION 2256 +#define TARGET_THREAD_TAILADDRESS 2288 +#define TARGET_THREAD_VIRTUALCALLTARGET 2296 +#define TARGET_THREAD_VIRTUALCALLINDEX 2304 +#define TARGET_THREAD_HEAPIMAGE 2312 +#define TARGET_THREAD_CODEIMAGE 2320 +#define TARGET_THREAD_THUNKTABLE 2328 +#define TARGET_THREAD_DYNAMICTABLE 2336 +#define TARGET_THREAD_STACKLIMIT 2384 + +#elif(TARGET_BYTES_PER_WORD == 4) + +#define TARGET_THREAD_EXCEPTION 44 +#define TARGET_THREAD_EXCEPTIONSTACKADJUSTMENT 2168 +#define TARGET_THREAD_EXCEPTIONOFFSET 2172 +#define TARGET_THREAD_EXCEPTIONHANDLER 2176 + +#define TARGET_THREAD_IP 2148 +#define TARGET_THREAD_STACK 2152 +#define TARGET_THREAD_NEWSTACK 2156 +#define TARGET_THREAD_SCRATCH 2160 +#define TARGET_THREAD_CONTINUATION 2164 +#define TARGET_THREAD_TAILADDRESS 2180 +#define TARGET_THREAD_VIRTUALCALLTARGET 2184 +#define TARGET_THREAD_VIRTUALCALLINDEX 2188 +#define TARGET_THREAD_HEAPIMAGE 2192 +#define TARGET_THREAD_CODEIMAGE 2196 +#define TARGET_THREAD_THUNKTABLE 2200 +#define TARGET_THREAD_DYNAMICTABLE 2204 +#define TARGET_THREAD_STACKLIMIT 2228 + +#else +#error +#endif +#else +#error +#endif + +#endif diff --git a/sgx-jvm/avian/src/avian/target.h b/sgx-jvm/avian/src/avian/target.h new file mode 100644 index 0000000000..d2ffa01f2b --- /dev/null +++ b/sgx-jvm/avian/src/avian/target.h @@ -0,0 +1,152 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef TARGET_H +#define TARGET_H + +#include "avian/target-fields.h" +#include "avian/common.h" + +namespace vm { + +template +inline T targetV1(T v) +{ + return v; +} + +template +inline T swapV2(T v) +{ + return (((v >> 8) & 0xFF) | ((v << 8))); +} + +template +inline T swapV4(T v) +{ + return (((v >> 24) & 0x000000FF) | ((v >> 8) & 0x0000FF00) + | ((v << 8) & 0x00FF0000) | ((v << 24))); +} + +template +inline T swapV8(T v) +{ + return (((static_cast(v) >> 56) & UINT64_C(0x00000000000000FF)) + | ((static_cast(v) >> 40) & UINT64_C(0x000000000000FF00)) + | ((static_cast(v) >> 24) & UINT64_C(0x0000000000FF0000)) + | ((static_cast(v) >> 8) & UINT64_C(0x00000000FF000000)) + | ((static_cast(v) << 8) & UINT64_C(0x000000FF00000000)) + | ((static_cast(v) << 24) & UINT64_C(0x0000FF0000000000)) + | ((static_cast(v) << 40) & UINT64_C(0x00FF000000000000)) + | ((static_cast(v) << 56))); +} + +#ifdef TARGET_OPPOSITE_ENDIAN + +template +inline T targetV2(T v) +{ + return swapV2(v); +} + +template +inline T targetV4(T v) +{ + return swapV4(v); +} + +template +inline T targetV8(T v) +{ + return swapV8(v); +} + +#else +template +inline T targetV2(T v) +{ + return v; +} + +template +inline T targetV4(T v) +{ + return v; +} + +template +inline T targetV8(T v) +{ + return v; +} +#endif + +#ifdef TARGET_BYTES_PER_WORD +#if (TARGET_BYTES_PER_WORD == 8) + +template +inline T targetVW(T v) +{ + return targetV8(v); +} + +typedef uint64_t target_uintptr_t; +typedef int64_t target_intptr_t; + +const unsigned TargetClassFixedSize = 12; +const unsigned TargetClassArrayElementSize = 14; +const unsigned TargetClassVtable = 136; + +const unsigned TargetFieldOffset = 12; + +#elif(TARGET_BYTES_PER_WORD == 4) + +template +inline T targetVW(T v) +{ + return targetV4(v); +} + +typedef uint32_t target_uintptr_t; +typedef int32_t target_intptr_t; + +const unsigned TargetClassFixedSize = 8; +const unsigned TargetClassArrayElementSize = 10; +const unsigned TargetClassVtable = 72; + +const unsigned TargetFieldOffset = 8; + +#else +#error +#endif +#else +#error +#endif + +const unsigned TargetBytesPerWord = TARGET_BYTES_PER_WORD; + +const unsigned TargetBitsPerWord = TargetBytesPerWord * 8; + +const target_uintptr_t TargetPointerMask + = ((~static_cast(0)) / TargetBytesPerWord) + * TargetBytesPerWord; + +const unsigned TargetArrayLength = TargetBytesPerWord; +const unsigned TargetArrayBody = TargetBytesPerWord * 2; + +inline void targetMarkBit(target_uintptr_t* map, unsigned i) +{ + map[wordOf(i)] |= targetVW(static_cast(1) + << bitOf(i)); +} + +} // namespace vm + +#endif // TARGET_H diff --git a/sgx-jvm/avian/src/avian/types.h b/sgx-jvm/avian/src/avian/types.h new file mode 100644 index 0000000000..43ad0bfc3c --- /dev/null +++ b/sgx-jvm/avian/src/avian/types.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef TYPES_H +#define TYPES_H + +#define VOID_TYPE 0 +#define INT8_TYPE 1 +#define INT16_TYPE 2 +#define INT32_TYPE 3 +#define INT64_TYPE 4 +#define FLOAT_TYPE 5 +#define DOUBLE_TYPE 6 +#define POINTER_TYPE 7 + +#endif // TYPES_H diff --git a/sgx-jvm/avian/src/avian/util.h b/sgx-jvm/avian/src/avian/util.h new file mode 100644 index 0000000000..dd5293bd17 --- /dev/null +++ b/sgx-jvm/avian/src/avian/util.h @@ -0,0 +1,172 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef UTIL_H +#define UTIL_H + +#include "avian/machine.h" +#include "avian/zone.h" + +namespace vm { + +GcTriple* hashMapFindNode(Thread* t, + GcHashMap* map, + object key, + uint32_t (*hash)(Thread*, object), + bool (*equal)(Thread*, object, object)); + +inline object hashMapFind(Thread* t, + GcHashMap* map, + object key, + uint32_t (*hash)(Thread*, object), + bool (*equal)(Thread*, object, object)) +{ + GcTriple* n = hashMapFindNode(t, map, key, hash, equal); + return (n ? n->second() : 0); +} + +void hashMapResize(Thread* t, + GcHashMap* map, + uint32_t (*hash)(Thread*, object), + unsigned size); + +void hashMapInsert(Thread* t, + GcHashMap* map, + object key, + object value, + uint32_t (*hash)(Thread*, object)); + +inline bool hashMapInsertOrReplace(Thread* t, + GcHashMap* map, + object key, + object value, + uint32_t (*hash)(Thread*, object), + bool (*equal)(Thread*, object, object)) +{ + GcTriple* n = hashMapFindNode(t, map, key, hash, equal); + if (n == 0) { + hashMapInsert(t, map, key, value, hash); + return true; + } else { + n->setSecond(t, value); + return false; + } +} + +inline bool hashMapInsertMaybe(Thread* t, + GcHashMap* map, + object key, + object value, + uint32_t (*hash)(Thread*, object), + bool (*equal)(Thread*, object, object)) +{ + GcTriple* n = hashMapFindNode(t, map, key, hash, equal); + if (n == 0) { + hashMapInsert(t, map, key, value, hash); + return true; + } else { + return false; + } +} + +object hashMapRemove(Thread* t, + GcHashMap* map, + object key, + uint32_t (*hash)(Thread*, object), + bool (*equal)(Thread*, object, object)); + +object hashMapIterator(Thread* t, GcHashMap* map); + +object hashMapIteratorNext(Thread* t, object it); + +void listAppend(Thread* t, GcList* list, object value); + +GcVector* vectorAppend(Thread* t, GcVector* vector, object value); + +GcArray* growArray(Thread* t, GcArray* array); + +object treeQuery(Thread* t, + GcTreeNode* tree, + intptr_t key, + GcTreeNode* sentinal, + intptr_t (*compare)(Thread* t, intptr_t key, object b)); + +GcTreeNode* treeInsert(Thread* t, + Zone* zone, + GcTreeNode* tree, + intptr_t key, + object value, + GcTreeNode* sentinal, + intptr_t (*compare)(Thread* t, intptr_t key, object b)); + +void treeUpdate(Thread* t, + GcTreeNode* tree, + intptr_t key, + object value, + GcTreeNode* sentinal, + intptr_t (*compare)(Thread* t, intptr_t key, object b)); + +class HashMapIterator : public Thread::Protector { + public: + HashMapIterator(Thread* t, GcHashMap* map) + : Protector(t), map(map), node(0), index(0) + { + find(); + } + + void find() + { + GcArray* array = map->array(); + if (array) { + for (unsigned i = index; i < array->length(); ++i) { + if (array->body()[i]) { + node = cast(t, array->body()[i]); + index = i + 1; + return; + } + } + } + node = 0; + } + + bool hasMore() + { + return node != 0; + } + + GcTriple* next() + { + if (node) { + GcTriple* n = node; + if (node->third()) { + node = cast(t, node->third()); + } else { + find(); + } + return n; + } else { + return 0; + } + } + + virtual void visit(Heap::Visitor* v) + { + v->visit(&map); + v->visit(&node); + } + + GcHashMap* map; + GcTriple* node; + unsigned index; +}; + +} // vm + +#endif // UTIL_H diff --git a/sgx-jvm/avian/src/avian/x86.h b/sgx-jvm/avian/src/avian/x86.h new file mode 100644 index 0000000000..c54a17763f --- /dev/null +++ b/sgx-jvm/avian/src/avian/x86.h @@ -0,0 +1,332 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef X86_H +#define X86_H + +#include "avian/types.h" +#include "avian/common.h" + +#ifdef _MSC_VER +#include "windows.h" +#pragma push_macro("assert") +#include "intrin.h" +#pragma pop_macro("assert") +#undef interface +#endif + +#if (defined ARCH_x86_32) || (defined PLATFORM_WINDOWS) +#define VA_LIST(x) (&(x)) +#else +#define VA_LIST(x) (x) +#endif + +#ifdef __APPLE__ +#include "mach/mach_types.h" +#include "mach/thread_act.h" +#include "mach/thread_status.h" + +#if __DARWIN_UNIX03 && defined(_STRUCT_X86_EXCEPTION_STATE32) +#define FIELD(x) __##x +#else +#define FIELD(x) x +#endif +#endif + +#ifdef ARCH_x86_32 + +#ifdef __APPLE__ +#define THREAD_STATE x86_THREAD_STATE32 +#define THREAD_STATE_TYPE x86_thread_state32_t +#define THREAD_STATE_COUNT x86_THREAD_STATE32_COUNT + +#define THREAD_STATE_IP(state) ((state).FIELD(eip)) +#define THREAD_STATE_STACK(state) ((state).FIELD(esp)) +#define THREAD_STATE_THREAD(state) ((state).FIELD(ebx)) +#define THREAD_STATE_LINK(state) ((state).FIELD(ecx)) +#define THREAD_STATE_FRAME(state) ((state).FIELD(ebp)) + +#define IP_REGISTER(context) THREAD_STATE_IP(context->uc_mcontext->FIELD(ss)) +#define STACK_REGISTER(context) \ + THREAD_STATE_STACK(context->uc_mcontext->FIELD(ss)) +#define THREAD_REGISTER(context) \ + THREAD_STATE_THREAD(context->uc_mcontext->FIELD(ss)) +#define LINK_REGISTER(context) \ + THREAD_STATE_LINK(context->uc_mcontext->FIELD(ss)) +#define FRAME_REGISTER(context) \ + THREAD_STATE_FRAME(context->uc_mcontext->FIELD(ss)) + +#elif(defined __QNX__) +#define IP_REGISTER(context) (context->uc_mcontext.cpu.eip) +#define STACK_REGISTER(context) (context->uc_mcontext.cpu.esp) +#define THREAD_REGISTER(context) (context->uc_mcontext.cpu.ebx) +#define LINK_REGISTER(context) (context->uc_mcontext.cpu.ecx) +#define FRAME_REGISTER(context) (context->uc_mcontext.cpu.ebp) +#elif(defined __FreeBSD__) +#define IP_REGISTER(context) (context->uc_mcontext.mc_eip) +#define STACK_REGISTER(context) (context->uc_mcontext.mc_esp) +#define THREAD_REGISTER(context) (context->uc_mcontext.mc_ebx) +#define LINK_REGISTER(context) (context->uc_mcontext.mc_ecx) +#define FRAME_REGISTER(context) (context->uc_mcontext.mc_ebp) +#else +#define IP_REGISTER(context) (context->uc_mcontext.gregs[REG_EIP]) +#define STACK_REGISTER(context) (context->uc_mcontext.gregs[REG_ESP]) +#define THREAD_REGISTER(context) (context->uc_mcontext.gregs[REG_EBX]) +#define LINK_REGISTER(context) (context->uc_mcontext.gregs[REG_ECX]) +#define FRAME_REGISTER(context) (context->uc_mcontext.gregs[REG_EBP]) +#endif + +extern "C" uint64_t vmNativeCall(void* function, + void* stack, + unsigned stackSize, + unsigned returnType); + +namespace vm { + +inline uint64_t dynamicCall(void* function, + uintptr_t* arguments, + uint8_t*, + unsigned, + unsigned argumentsSize, + unsigned returnType) +{ + return vmNativeCall(function, arguments, argumentsSize, returnType); +} + +} // namespace vm + +#elif defined ARCH_x86_64 + +#ifdef __APPLE__ +#define THREAD_STATE x86_THREAD_STATE64 +#define THREAD_STATE_TYPE x86_thread_state64_t +#define THREAD_STATE_COUNT x86_THREAD_STATE64_COUNT + +#define THREAD_STATE_IP(state) ((state).FIELD(rip)) +#define THREAD_STATE_STACK(state) ((state).FIELD(rsp)) +#define THREAD_STATE_THREAD(state) ((state).FIELD(rbx)) +#define THREAD_STATE_LINK(state) ((state).FIELD(rcx)) +#define THREAD_STATE_FRAME(state) ((state).FIELD(rbp)) + +#define IP_REGISTER(context) THREAD_STATE_IP(context->uc_mcontext->FIELD(ss)) +#define STACK_REGISTER(context) \ + THREAD_STATE_STACK(context->uc_mcontext->FIELD(ss)) +#define THREAD_REGISTER(context) \ + THREAD_STATE_THREAD(context->uc_mcontext->FIELD(ss)) +#define LINK_REGISTER(context) \ + THREAD_STATE_LINK(context->uc_mcontext->FIELD(ss)) +#define FRAME_REGISTER(context) \ + THREAD_STATE_FRAME(context->uc_mcontext->FIELD(ss)) + +#elif(defined __FreeBSD__) +#define IP_REGISTER(context) (context->uc_mcontext.mc_rip) +#define STACK_REGISTER(context) (context->uc_mcontext.mc_rsp) +#define THREAD_REGISTER(context) (context->uc_mcontext.mc_rbx) +#define LINK_REGISTER(context) (context->uc_mcontext.mc_rcx) +#define FRAME_REGISTER(context) (context->uc_mcontext.mc_rbp) +#else +#define IP_REGISTER(context) (context->uc_mcontext.gregs[REG_RIP]) +#define STACK_REGISTER(context) (context->uc_mcontext.gregs[REG_RSP]) +#define THREAD_REGISTER(context) (context->uc_mcontext.gregs[REG_RBX]) +#define LINK_REGISTER(context) (context->uc_mcontext.gregs[REG_RCX]) +#define FRAME_REGISTER(context) (context->uc_mcontext.gregs[REG_RBP]) +#endif + +extern "C" uint64_t +#ifdef PLATFORM_WINDOWS + vmNativeCall(void* function, + void* stack, + unsigned stackSize, + unsigned returnType); +#else + vmNativeCall(void* function, + void* stack, + unsigned stackSize, + void* gprTable, + void* sseTable, + unsigned returnType); +#endif + +namespace vm { + +#ifdef PLATFORM_WINDOWS +inline uint64_t dynamicCall(void* function, + uint64_t* arguments, + UNUSED uint8_t* argumentTypes, + unsigned argumentCount, + unsigned, + unsigned returnType) +{ + return vmNativeCall(function, arguments, argumentCount, returnType); +} +#else +inline uint64_t dynamicCall(void* function, + uintptr_t* arguments, + uint8_t* argumentTypes, + unsigned argumentCount, + unsigned, + unsigned returnType) +{ + const unsigned GprCount = 6; + uint64_t gprTable[GprCount]; + unsigned gprIndex = 0; + + const unsigned SseCount = 8; + uint64_t sseTable[SseCount]; + unsigned sseIndex = 0; + + uint64_t stack[argumentCount]; + unsigned stackIndex = 0; + + for (unsigned i = 0; i < argumentCount; ++i) { + switch (argumentTypes[i]) { + case FLOAT_TYPE: + case DOUBLE_TYPE: { + if (sseIndex < SseCount) { + sseTable[sseIndex++] = arguments[i]; + } else { + stack[stackIndex++] = arguments[i]; + } + } break; + + default: { + if (gprIndex < GprCount) { + gprTable[gprIndex++] = arguments[i]; + } else { + stack[stackIndex++] = arguments[i]; + } + } break; + } + } + + return vmNativeCall(function, + stack, + stackIndex * BytesPerWord, + (gprIndex ? gprTable : 0), + (sseIndex ? sseTable : 0), + returnType); +} +#endif + +} // namespace vm + +#else +#error unsupported architecture +#endif + +namespace vm { + +inline void trap() +{ +#ifdef _MSC_VER + __asm int 3 +#else + asm("int3"); +#endif +} + +inline void programOrderMemoryBarrier() +{ + compileTimeMemoryBarrier(); +} + +inline void storeStoreMemoryBarrier() +{ + programOrderMemoryBarrier(); +} + +inline void storeLoadMemoryBarrier() +{ +#ifdef _MSC_VER + MemoryBarrier(); +#elif defined ARCH_x86_32 + __asm__ __volatile__("lock; addl $0,0(%%esp)" : : : "memory"); +#elif defined ARCH_x86_64 + __asm__ __volatile__("mfence" : : : "memory"); +#endif // ARCH_x86_64 +} + +inline void loadMemoryBarrier() +{ + programOrderMemoryBarrier(); +} + +inline void syncInstructionCache(const void*, unsigned) +{ + programOrderMemoryBarrier(); +} + +#ifdef USE_ATOMIC_OPERATIONS +inline bool atomicCompareAndSwap32(uint32_t* p, uint32_t old, uint32_t new_) +{ +#ifdef _MSC_VER + return old + == InterlockedCompareExchange(reinterpret_cast(p), new_, old); +#elif(__GNUC__ >= 4) && (__GNUC_MINOR__ >= 1) + return __sync_bool_compare_and_swap(p, old, new_); +#else + uint8_t result; + + __asm__ __volatile__("lock; cmpxchgl %2, %0; setz %1" + : "=m"(*p), "=q"(result) + : "r"(new_), "a"(old), "m"(*p) + : "memory"); + + return result != 0; +#endif +} + +#define AVIAN_HAS_CAS64 + +inline bool atomicCompareAndSwap64(uint64_t* p, uint64_t old, uint64_t new_) +{ +#ifdef _MSC_VER + return old == InterlockedCompareExchange64( + reinterpret_cast(p), new_, old); +#elif(__GNUC__ >= 4) && (__GNUC_MINOR__ >= 1) + return __sync_bool_compare_and_swap(p, old, new_); +#elif defined ARCH_x86_32 + uint8_t result; + + __asm__ __volatile__("lock; cmpxchg8b %0; setz %1" + : "=m"(*p), "=q"(result) + : "a"(static_cast(old)), + "d"(static_cast(old >> 32)), + "b"(static_cast(new_)), + "c"(static_cast(new_ >> 32)), + "m"(*p) + : "memory"); + + return result != 0; +#else + uint8_t result; + + __asm__ __volatile__("lock; cmpxchgq %2, %0; setz %1" + : "=m"(*p), "=q"(result) + : "r"(new_), "a"(old), "m"(*p) + : "memory"); + + return result != 0; +#endif +} + +inline bool atomicCompareAndSwap(uintptr_t* p, uintptr_t old, uintptr_t new_) +{ +#ifdef ARCH_x86_32 + return atomicCompareAndSwap32(reinterpret_cast(p), old, new_); +#elif defined ARCH_x86_64 + return atomicCompareAndSwap64(reinterpret_cast(p), old, new_); +#endif // ARCH_x86_64 +} +#endif // USE_ATOMIC_OPERATIONS + +} // namespace vm + +#endif // X86_H diff --git a/sgx-jvm/avian/src/avian/zlib-custom.h b/sgx-jvm/avian/src/avian/zlib-custom.h new file mode 100644 index 0000000000..73a62c81bb --- /dev/null +++ b/sgx-jvm/avian/src/avian/zlib-custom.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "zlib.h" + +#ifdef inflateInit2 +#undef inflateInit2 +#define inflateInit2(strm, windowBits) \ + inflateInit2_( \ + (strm), (windowBits), ZLIB_VERSION, static_cast(sizeof(z_stream))) +#endif + +#ifdef deflateInit2 +#undef deflateInit2 +#define deflateInit2(strm, level, windowBits) \ + deflateInit2_((strm), \ + (level), \ + Z_DEFLATED, \ + (windowBits), \ + 8, \ + Z_DEFAULT_STRATEGY, \ + ZLIB_VERSION, \ + static_cast(sizeof(z_stream))) +#endif diff --git a/sgx-jvm/avian/src/avian/zone.h b/sgx-jvm/avian/src/avian/zone.h new file mode 100644 index 0000000000..1274abfa73 --- /dev/null +++ b/sgx-jvm/avian/src/avian/zone.h @@ -0,0 +1,156 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef ZONE_H +#define ZONE_H + +#include +#include +#include + +namespace vm { + +class Zone : public avian::util::AllocOnly { + public: + class Segment { + public: + Segment(Segment* next, unsigned size) : next(next), size(size), position(0) + { + } + + Segment* next; + uintptr_t size; + uintptr_t position; + uint8_t data[0]; + }; + + Zone(avian::util::Allocator* allocator, size_t minimumFootprint) + : allocator(allocator), + segment(0), + minimumFootprint(minimumFootprint < sizeof(Segment) + ? 0 + : minimumFootprint - sizeof(Segment)) + { + } + + ~Zone() + { + dispose(); + } + + void dispose() + { + for (Segment* seg = segment, *next; seg; seg = next) { + next = seg->next; + allocator->free(seg, sizeof(Segment) + seg->size); + } + + segment = 0; + } + + virtual void* allocate(size_t size) + { + size = pad(size); + void* p = tryAllocate(size); + if (p) { + return p; + } else { + ensure(size); + void* r = segment->data + segment->position; + segment->position += size; + return r; + } + } + + void* peek(size_t size) + { + size = pad(size); + Segment* s = segment; + while (s->position < size) { + size -= s->position; + s = s->next; + } + return s->data + (s->position - size); + } + + void pop(size_t size) + { + size = pad(size); + Segment* s = segment; + while (s->position < size) { + size -= s->position; + Segment* next = s->next; + allocator->free(s, sizeof(Segment) + s->size); + s = next; + } + s->position -= size; + segment = s; + } + + private: + static unsigned padToPage(unsigned size) + { + return (size + (LikelyPageSizeInBytes - 1)) & ~(LikelyPageSizeInBytes - 1); + } + + bool tryEnsure(unsigned space) + { + if (segment == 0 or segment->position + space > segment->size) { + unsigned size = padToPage( + avian::util::max( + space, + avian::util::max(minimumFootprint, + segment == 0 ? 0 : segment->size * 2)) + + sizeof(Segment)); + + void* p = allocator->tryAllocate(size); + if (p == 0) { + size = padToPage(space + sizeof(Segment)); + p = allocator->tryAllocate(size); + if (p == 0) { + return false; + } + } + + segment = new (p) Segment(segment, size - sizeof(Segment)); + } + return true; + } + + void ensure(unsigned space) + { + if (segment == 0 or segment->position + space > segment->size) { + unsigned size = padToPage(space + sizeof(Segment)); + + segment = new (allocator->allocate(size)) + Segment(segment, size - sizeof(Segment)); + } + } + + void* tryAllocate(size_t size) + { + size = pad(size); + if (tryEnsure(size)) { + void* r = segment->data + segment->position; + segment->position += size; + return r; + } else { + return 0; + } + } + + avian::util::Allocator* allocator; + Segment* segment; + unsigned minimumFootprint; +}; + +} // namespace vm + +#endif // ZONE_H diff --git a/sgx-jvm/avian/src/boot-javahome.cpp b/sgx-jvm/avian/src/boot-javahome.cpp new file mode 100644 index 0000000000..026910ede6 --- /dev/null +++ b/sgx-jvm/avian/src/boot-javahome.cpp @@ -0,0 +1,34 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/common.h" + +#ifdef BOOT_JAVAHOME + +#if (!defined __x86_64__) && ((defined __MINGW32__) || (defined _MSC_VER)) +#define SYMBOL(x) binary_javahome_jar_##x +#else +#define SYMBOL(x) _binary_javahome_jar_##x +#endif + +extern "C" { +extern const uint8_t SYMBOL(start)[]; +extern const uint8_t SYMBOL(end)[]; + +AVIAN_EXPORT const uint8_t* javahomeJar(size_t* size) +{ + *size = SYMBOL(end) - SYMBOL(start); + return SYMBOL(start); +} +} + +#undef SYMBOL + +#endif // BOOT_JAVAHOME diff --git a/sgx-jvm/avian/src/boot.cpp b/sgx-jvm/avian/src/boot.cpp new file mode 100644 index 0000000000..3849c9eed0 --- /dev/null +++ b/sgx-jvm/avian/src/boot.cpp @@ -0,0 +1,77 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include + +#include "avian/common.h" + +// since we aren't linking against libstdc++, we must implement this +// ourselves: +extern "C" void __cxa_pure_virtual(void) +{ + abort(); +} + +#ifdef BOOT_IMAGE + +#if (!defined __x86_64__) && ((defined __MINGW32__) || (defined _MSC_VER)) +#define BOOTIMAGE_SYMBOL(x) binary_bootimage_bin_##x +#define CODEIMAGE_SYMBOL(x) binary_codeimage_bin_##x +#else +#define BOOTIMAGE_SYMBOL(x) _binary_bootimage_bin_##x +#define CODEIMAGE_SYMBOL(x) _binary_codeimage_bin_##x +#endif + +extern "C" { +extern const uint8_t BOOTIMAGE_SYMBOL(start)[]; +extern const uint8_t BOOTIMAGE_SYMBOL(end)[]; + +AVIAN_EXPORT const uint8_t* bootimageBin(size_t* size) +{ + *size = BOOTIMAGE_SYMBOL(end) - BOOTIMAGE_SYMBOL(start); + return BOOTIMAGE_SYMBOL(start); +} + +extern const uint8_t CODEIMAGE_SYMBOL(start)[]; +extern const uint8_t CODEIMAGE_SYMBOL(end)[]; + +AVIAN_EXPORT const uint8_t* codeimageBin(size_t* size) +{ + *size = CODEIMAGE_SYMBOL(end) - CODEIMAGE_SYMBOL(start); + return CODEIMAGE_SYMBOL(start); +} +} + +#undef SYMBOL + +#endif // BOOT_IMAGE + +#ifdef BOOT_CLASSPATH + +#if (!defined __x86_64__) && ((defined __MINGW32__) || (defined _MSC_VER)) +#define SYMBOL(x) binary_classpath_jar_##x +#else +#define SYMBOL(x) _binary_classpath_jar_##x +#endif + +extern "C" { +extern const uint8_t SYMBOL(start)[]; +extern const uint8_t SYMBOL(end)[]; + +AVIAN_EXPORT const uint8_t* classpathJar(size_t* size) +{ + *size = SYMBOL(end) - SYMBOL(start); + return SYMBOL(start); +} +} + +#undef SYMBOL + +#endif // BOOT_CLASSPATH diff --git a/sgx-jvm/avian/src/bootimage-fields.cpp b/sgx-jvm/avian/src/bootimage-fields.cpp new file mode 100644 index 0000000000..1cd72c73ea --- /dev/null +++ b/sgx-jvm/avian/src/bootimage-fields.cpp @@ -0,0 +1,46 @@ +#ifndef FIELD +#define FIELD(name) +#define FIELD_DEFINED +#endif + +FIELD(magic) + +FIELD(initialized) + +FIELD(heapSize) +FIELD(codeSize) + +FIELD(bootClassCount) +FIELD(appClassCount) +FIELD(stringCount) +FIELD(callCount) + +FIELD(bootLoader) +FIELD(appLoader) +FIELD(types) +FIELD(methodTree) +FIELD(methodTreeSentinal) +FIELD(virtualThunks) + +#ifdef FIELD_DEFINED +#undef FIELD +#undef FIELD_DEFINED +#endif + +#ifndef THUNK_FIELD +#define THUNK_FIELD(name) +#define THUNK_FIELD_DEFINED +#endif + +THUNK_FIELD(default_); +THUNK_FIELD(defaultVirtual); +THUNK_FIELD(defaultDynamic); +THUNK_FIELD(native); +THUNK_FIELD(aioob); +THUNK_FIELD(stackOverflow); +THUNK_FIELD(table); + +#ifdef THUNK_FIELD_DEFINED +#undef THUNK_FIELD +#undef THUNK_FIELD_DEFINED +#endif diff --git a/sgx-jvm/avian/src/bootimage-template.cpp b/sgx-jvm/avian/src/bootimage-template.cpp new file mode 100644 index 0000000000..16dd85e11e --- /dev/null +++ b/sgx-jvm/avian/src/bootimage-template.cpp @@ -0,0 +1,26 @@ +const unsigned NAME(BootMask) = (~static_cast(0)) + / NAME(BytesPerWord); + +const unsigned NAME(BootShift) UNUSED = 32 - avian::util::log(NAME(BytesPerWord)); + +inline unsigned LABEL(codeMapSize)(unsigned codeSize) +{ + return avian::util::ceilingDivide(codeSize, TargetBitsPerWord) + * TargetBytesPerWord; +} + +inline unsigned LABEL(heapMapSize)(unsigned heapSize) +{ + return avian::util::ceilingDivide(heapSize, + TargetBitsPerWord * TargetBytesPerWord) + * TargetBytesPerWord; +} + +inline object LABEL(bootObject)(LABEL(uintptr_t) * heap, unsigned offset) +{ + if (offset) { + return reinterpret_cast(heap + offset - 1); + } else { + return 0; + } +} diff --git a/sgx-jvm/avian/src/builtin.cpp b/sgx-jvm/avian/src/builtin.cpp new file mode 100644 index 0000000000..4369ec62d2 --- /dev/null +++ b/sgx-jvm/avian/src/builtin.cpp @@ -0,0 +1,1393 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/machine.h" +#include "avian/constants.h" +#include "avian/processor.h" +#include "avian/util.h" + +#include + +using namespace vm; + +namespace { + +int64_t search(Thread* t, + GcClassLoader* loader, + GcString* name, + GcClass* (*op)(Thread*, GcClassLoader*, GcByteArray*), + bool replaceDots) +{ + if (LIKELY(name)) { + PROTECT(t, loader); + PROTECT(t, name); + + GcByteArray* n = makeByteArray(t, name->length(t) + 1); + char* s = reinterpret_cast(n->body().begin()); + stringChars(t, name, s); + + if (replaceDots) { + replace('.', '/', s); + } + + return reinterpret_cast(op(t, loader, n)); + } else { + throwNew(t, GcNullPointerException::Type); + } +} + +GcClass* resolveSystemClassThrow(Thread* t, + GcClassLoader* loader, + GcByteArray* spec) +{ + return resolveSystemClass( + t, loader, spec, true, GcClassNotFoundException::Type); +} + +GcField* fieldForOffsetInClass(Thread* t, GcClass* c, unsigned offset) +{ + GcClass* super = c->super(); + if (super) { + GcField* field = fieldForOffsetInClass(t, super, offset); + if (field) { + return field; + } + } + + object table = c->fieldTable(); + if (table) { + for (unsigned i = 0; i < objectArrayLength(t, table); ++i) { + GcField* field = cast(t, objectArrayBody(t, table, i)); + if ((field->flags() & ACC_STATIC) == 0 and field->offset() == offset) { + return field; + } + } + } + + return 0; +} + +GcField* fieldForOffset(Thread* t, object o, unsigned offset) +{ + GcClass* c = objectClass(t, o); + if (c->vmFlags() & SingletonFlag) { + GcSingleton* s = cast(t, o); + + // If the object is a Singleton, we assume it's the static table of a class - + // which will always have the parent class as the first (0th) element. + c = cast(t, singletonObject(t, s, 0)); + + object table = c->fieldTable(); + if (table) { + for (unsigned i = 0; i < objectArrayLength(t, table); ++i) { + GcField* field = cast(t, objectArrayBody(t, table, i)); + if ((field->flags() & ACC_STATIC) and field->offset() == offset) { + return field; + } + } + } + abort(t); + } else { + GcField* field = fieldForOffsetInClass(t, c, offset); + if (field) { + return field; + } else { + abort(t); + } + } +} + +} // namespace + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_Classes_toVMClass(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast( + cast(t, reinterpret_cast(arguments[0]))->vmClass()); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_Classes_toVMMethod(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast(t->m->classpath->getVMMethod( + t, cast(t, reinterpret_cast(arguments[0])))); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_avian_Classes_initialize(Thread* t, object, uintptr_t* arguments) +{ + GcClass* this_ = cast(t, reinterpret_cast(arguments[0])); + + initClass(t, this_); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_avian_Classes_acquireClassLock(Thread* t, object, uintptr_t*) +{ + acquire(t, t->m->classLock); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_avian_Classes_releaseClassLock(Thread* t, object, uintptr_t*) +{ + release(t, t->m->classLock); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_Classes_resolveVMClass(Thread* t, object, uintptr_t* arguments) +{ + GcClassLoader* loader + = cast(t, reinterpret_cast(arguments[0])); + GcByteArray* spec + = cast(t, reinterpret_cast(arguments[1])); + + return reinterpret_cast( + resolveClass(t, loader, spec, true, GcClassNotFoundException::Type)); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_Classes_defineVMClass(Thread* t, object, uintptr_t* arguments) +{ + GcClassLoader* loader + = cast(t, reinterpret_cast(arguments[0])); + GcByteArray* b = cast(t, reinterpret_cast(arguments[1])); + int offset = arguments[2]; + int length = arguments[3]; + + uint8_t* buffer = static_cast(t->m->heap->allocate(length)); + + THREAD_RESOURCE2( + t, uint8_t*, buffer, int, length, t->m->heap->free(buffer, length)); + + memcpy(buffer, &b->body()[offset], length); + + return reinterpret_cast(defineClass(t, loader, buffer, length)); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_Classes_makeString(Thread* t, object, uintptr_t* arguments) +{ + GcByteArray* array + = cast(t, reinterpret_cast(arguments[0])); + int offset = arguments[1]; + int length = arguments[2]; + + return reinterpret_cast( + 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, + uintptr_t* arguments) +{ + GcClassLoader* loader + = cast(t, reinterpret_cast(arguments[0])); + GcString* name = cast(t, reinterpret_cast(arguments[1])); + + return search(t, loader, name, findLoadedClass, true); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_SystemClassLoader_vmClass(Thread* t, + object, + uintptr_t* arguments) +{ + return reinterpret_cast( + cast(t, reinterpret_cast(arguments[0]))->vmClass()); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_SystemClassLoader_findVMClass(Thread* t, + object, + uintptr_t* arguments) +{ + GcClassLoader* loader + = cast(t, reinterpret_cast(arguments[0])); + GcString* name = cast(t, reinterpret_cast(arguments[1])); + + return search(t, loader, name, resolveSystemClassThrow, true); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_SystemClassLoader_resourceURLPrefix(Thread* t, + object, + uintptr_t* arguments) +{ + GcClassLoader* loader + = cast(t, reinterpret_cast(arguments[0])); + GcString* name = cast(t, reinterpret_cast(arguments[1])); + + if (LIKELY(name)) { + THREAD_RUNTIME_ARRAY(t, char, n, name->length(t) + 1); + stringChars(t, name, RUNTIME_ARRAY_BODY(n)); + + const char* name + = static_cast(loader->as(t)->finder()) + ->urlPrefix(RUNTIME_ARRAY_BODY(n)); + + return name ? reinterpret_cast(makeString(t, "%s", name)) : 0; + } else { + throwNew(t, GcNullPointerException::Type); + } +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_SystemClassLoader_00024ResourceEnumeration_nextResourceURLPrefix( + Thread* t, + object, + uintptr_t* arguments) +{ + GcClassLoader* loader + = cast(t, reinterpret_cast(arguments[1])); + GcString* name = cast(t, reinterpret_cast(arguments[2])); + GcLongArray* finderElementPtrPtr + = cast(t, reinterpret_cast(arguments[3])); + + if (LIKELY(name) && LIKELY(finderElementPtrPtr)) { + THREAD_RUNTIME_ARRAY(t, char, n, name->length(t) + 1); + stringChars(t, name, RUNTIME_ARRAY_BODY(n)); + + void*& finderElementPtr + = reinterpret_cast(finderElementPtrPtr->body()[0]); + const char* name + = static_cast(loader->as(t)->finder()) + ->nextUrlPrefix(RUNTIME_ARRAY_BODY(n), finderElementPtr); + + return name ? reinterpret_cast(makeString(t, "%s", name)) : 0; + } else { + throwNew(t, GcNullPointerException::Type); + } +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_SystemClassLoader_getClass(Thread* t, + object, + uintptr_t* arguments) +{ + return reinterpret_cast( + getJClass(t, cast(t, reinterpret_cast(arguments[0])))); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_SystemClassLoader_getPackageSource(Thread* t, + object, + uintptr_t* arguments) +{ + GcString* name = cast(t, reinterpret_cast(arguments[0])); + PROTECT(t, name); + + ACQUIRE(t, t->m->classLock); + + THREAD_RUNTIME_ARRAY(t, char, chars, name->length(t) + 2); + stringChars(t, name, RUNTIME_ARRAY_BODY(chars)); + replace('.', '/', RUNTIME_ARRAY_BODY(chars)); + RUNTIME_ARRAY_BODY(chars)[name->length(t)] = '/'; + RUNTIME_ARRAY_BODY(chars)[name->length(t) + 1] = 0; + + GcByteArray* key = makeByteArray(t, RUNTIME_ARRAY_BODY(chars)); + + GcByteArray* array = cast( + t, + hashMapFind( + t, roots(t)->packageMap(), key, byteArrayHash, byteArrayEqual)); + + if (array) { + return reinterpret_cast(makeLocalReference( + t, t->m->classpath->makeString(t, array, 0, array->length()))); + } else { + return 0; + } +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_avian_Machine_dumpHeap(Thread* t, object, uintptr_t* arguments) +{ + GcString* outputFile + = static_cast(reinterpret_cast(*arguments)); + + unsigned length = outputFile->length(t); + THREAD_RUNTIME_ARRAY(t, char, n, length + 1); + stringChars(t, outputFile, RUNTIME_ARRAY_BODY(n)); + FILE* out = vm::fopen(RUNTIME_ARRAY_BODY(n), "wb"); + if (out) { + { + ENTER(t, Thread::ExclusiveState); + dumpHeap(t, out); + } + fclose(out); + } else { + throwNew(t, + GcRuntimeException::Type, + "file not found: %s", + RUNTIME_ARRAY_BODY(n)); + } +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_Machine_tryNative(Thread* t, object, uintptr_t* arguments) +{ + int64_t function; + memcpy(&function, arguments, 8); + int64_t argument; + memcpy(&argument, arguments + 2, 8); + + t->setFlag(Thread::TryNativeFlag); + THREAD_RESOURCE0(t, t->clearFlag(Thread::TryNativeFlag)); + + return reinterpret_cast(function)(argument); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_Runtime_exit(Thread* t, object, uintptr_t* arguments) +{ + shutDown(t); + + t->m->system->exit(arguments[1]); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Runtime_freeMemory(Thread* t, object, uintptr_t*) +{ + return t->m->heap->remaining(); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Runtime_totalMemory(Thread* t, object, uintptr_t*) +{ + return t->m->heap->limit(); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Runtime_maxMemory(Thread* t, object, uintptr_t*) +{ + return t->m->heap->limit(); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_avianvmresource_Handler_00024ResourceInputStream_getContentLength( + Thread* t, + object, + uintptr_t* arguments) +{ + GcString* path = cast(t, reinterpret_cast(*arguments)); + + if (LIKELY(path)) { + THREAD_RUNTIME_ARRAY(t, char, p, path->length(t) + 1); + stringChars(t, path, RUNTIME_ARRAY_BODY(p)); + + System::Region* r = t->m->bootFinder->find(RUNTIME_ARRAY_BODY(p)); + if (r == 0) { + r = t->m->appFinder->find(RUNTIME_ARRAY_BODY(p)); + } + + if (r) { + jint rSize = r->length(); + r->dispose(); + return rSize; + } + } + return -1; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_avianvmresource_Handler_00024ResourceInputStream_open( + Thread* t, + object, + uintptr_t* arguments) +{ + GcString* path = cast(t, reinterpret_cast(*arguments)); + + if (LIKELY(path)) { + THREAD_RUNTIME_ARRAY(t, char, p, path->length(t) + 1); + stringChars(t, path, RUNTIME_ARRAY_BODY(p)); + + System::Region* r = t->m->bootFinder->find(RUNTIME_ARRAY_BODY(p)); + if (r == 0) { + r = t->m->appFinder->find(RUNTIME_ARRAY_BODY(p)); + } + + return reinterpret_cast(r); + } else { + throwNew(t, GcNullPointerException::Type); + } +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_avianvmresource_Handler_00024ResourceInputStream_available( + Thread*, + object, + uintptr_t* arguments) +{ + int64_t peer; + memcpy(&peer, arguments, 8); + int32_t position = arguments[2]; + + System::Region* region = reinterpret_cast(peer); + return static_cast(region->length()) - position; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_avianvmresource_Handler_00024ResourceInputStream_read__JI( + Thread*, + object, + uintptr_t* arguments) +{ + int64_t peer; + memcpy(&peer, arguments, 8); + int32_t position = arguments[2]; + + System::Region* region = reinterpret_cast(peer); + if (position >= static_cast(region->length())) { + return -1; + } else { + return region->start()[position]; + } +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_avianvmresource_Handler_00024ResourceInputStream_read__JI_3BII( + Thread* t, + object, + uintptr_t* arguments) +{ + int64_t peer; + memcpy(&peer, arguments, 8); + int32_t position = arguments[2]; + GcByteArray* buffer + = cast(t, reinterpret_cast(arguments[3])); + int32_t offset = arguments[4]; + int32_t length = arguments[5]; + + if (length == 0) + return 0; + + System::Region* region = reinterpret_cast(peer); + if (length > static_cast(region->length()) - position) { + length = static_cast(region->length()) - position; + } + if (length <= 0) { + return -1; + } else { + memcpy(&buffer->body()[offset], region->start() + position, length); + return length; + } +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_avian_avianvmresource_Handler_00024ResourceInputStream_close( + Thread*, + object, + uintptr_t* arguments) +{ + int64_t peer; + memcpy(&peer, arguments, 8); + reinterpret_cast(peer)->dispose(); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_avian_Continuations_callWithCurrentContinuation(Thread* t, + object, + uintptr_t* arguments) +{ + t->m->processor->callWithCurrentContinuation( + t, reinterpret_cast(*arguments)); + + abort(t); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_avian_Continuations_dynamicWind2(Thread* t, + object, + uintptr_t* arguments) +{ + t->m->processor->dynamicWind(t, + reinterpret_cast(arguments[0]), + reinterpret_cast(arguments[1]), + reinterpret_cast(arguments[2])); + + abort(t); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_avian_Continuations_00024Continuation_handleResult( + Thread* t, + object, + uintptr_t* arguments) +{ + t->m->processor->feedResultToContinuation( + t, + cast(t, reinterpret_cast(arguments[0])), + reinterpret_cast(arguments[1])); + + abort(t); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_avian_Continuations_00024Continuation_handleException( + Thread* t, + object, + uintptr_t* arguments) +{ + t->m->processor->feedExceptionToContinuation( + t, + cast(t, reinterpret_cast(arguments[0])), + cast(t, reinterpret_cast(arguments[1]))); + + abort(t); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_Singleton_getObject(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast(singletonObject( + t, + cast(t, reinterpret_cast(arguments[0])), + arguments[1])); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_Singleton_getInt(Thread* t, object, uintptr_t* arguments) +{ + return singletonValue( + t, + cast(t, reinterpret_cast(arguments[0])), + arguments[1]); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_Singleton_getLong(Thread* t, object, uintptr_t* arguments) +{ + int64_t v; + memcpy(&v, + &singletonValue( + t, + cast(t, reinterpret_cast(arguments[0])), + arguments[1]), + 8); + return v; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_allocateMemory(Thread* t, + object, + uintptr_t* arguments) +{ + int64_t size; + memcpy(&size, arguments + 1, 8); + void* p = malloc(size); + if (p) { + return reinterpret_cast(p); + } else { + throwNew(t, GcOutOfMemoryError::Type); + } +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_freeMemory(Thread*, object, uintptr_t* arguments) +{ + int64_t p; + memcpy(&p, arguments + 1, 8); + if (p) { + free(reinterpret_cast(p)); + } +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_setMemory(Thread* t, object, uintptr_t* arguments) +{ + object base = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + int64_t count; + memcpy(&count, arguments + 4, 8); + int8_t value = arguments[6]; + + PROTECT(t, base); + + ACQUIRE(t, t->m->referenceLock); + + if (base) { + memset(&fieldAtOffset(base, offset), value, count); + } else { + memset(reinterpret_cast(offset), value, count); + } +} + +// NB: The following primitive get/put methods are only used by the +// interpreter. The JIT/AOT compiler implements them as intrinsics, +// so these versions will be ignored. + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putByte__JB(Thread*, object, uintptr_t* arguments) +{ + int64_t p; + memcpy(&p, arguments + 1, 8); + int8_t v = arguments[3]; + + *reinterpret_cast(p) = v; +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putShort__JS(Thread*, object, uintptr_t* arguments) +{ + int64_t p; + memcpy(&p, arguments + 1, 8); + int16_t v = arguments[3]; + + *reinterpret_cast(p) = v; +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putChar__JC(Thread* t, + object method, + uintptr_t* arguments) +{ + Avian_sun_misc_Unsafe_putShort__JS(t, method, arguments); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putInt__JI(Thread*, object, uintptr_t* arguments) +{ + int64_t p; + memcpy(&p, arguments + 1, 8); + int32_t v = arguments[3]; + + *reinterpret_cast(p) = v; +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putFloat__JF(Thread* t, + object method, + uintptr_t* arguments) +{ + Avian_sun_misc_Unsafe_putInt__JI(t, method, arguments); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putLong__JJ(Thread*, object, uintptr_t* arguments) +{ + int64_t p; + memcpy(&p, arguments + 1, 8); + int64_t v; + memcpy(&v, arguments + 3, 8); + + *reinterpret_cast(p) = v; +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putDouble__JD(Thread* t, + object method, + uintptr_t* arguments) +{ + Avian_sun_misc_Unsafe_putLong__JJ(t, method, arguments); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putAddress__JJ(Thread*, object, uintptr_t* arguments) +{ + int64_t p; + memcpy(&p, arguments + 1, 8); + int64_t v; + memcpy(&v, arguments + 3, 8); + + *reinterpret_cast(p) = v; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getByte__J(Thread*, object, uintptr_t* arguments) +{ + int64_t p; + memcpy(&p, arguments + 1, 8); + + return *reinterpret_cast(p); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getShort__J(Thread*, object, uintptr_t* arguments) +{ + int64_t p; + memcpy(&p, arguments + 1, 8); + + return *reinterpret_cast(p); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getChar__J(Thread* t, + object method, + uintptr_t* arguments) +{ + return Avian_sun_misc_Unsafe_getShort__J(t, method, arguments); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getInt__J(Thread*, object, uintptr_t* arguments) +{ + int64_t p; + memcpy(&p, arguments + 1, 8); + + return *reinterpret_cast(p); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getFloat__J(Thread* t, + object method, + uintptr_t* arguments) +{ + return Avian_sun_misc_Unsafe_getInt__J(t, method, arguments); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getLong__J(Thread*, object, uintptr_t* arguments) +{ + int64_t p; + memcpy(&p, arguments + 1, 8); + + return *reinterpret_cast(p); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getDouble__J(Thread* t, + object method, + uintptr_t* arguments) +{ + return Avian_sun_misc_Unsafe_getLong__J(t, method, arguments); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getAddress__J(Thread*, object, uintptr_t* arguments) +{ + int64_t p; + memcpy(&p, arguments + 1, 8); + + return *reinterpret_cast(p); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_copyMemory(Thread* t, object, uintptr_t* arguments) +{ + object srcBase = reinterpret_cast(arguments[1]); + int64_t srcOffset; + memcpy(&srcOffset, arguments + 2, 8); + object dstBase = reinterpret_cast(arguments[4]); + int64_t dstOffset; + memcpy(&dstOffset, arguments + 5, 8); + int64_t count; + memcpy(&count, arguments + 7, 8); + + PROTECT(t, srcBase); + PROTECT(t, dstBase); + + ACQUIRE(t, t->m->referenceLock); + + void* src = srcBase ? &fieldAtOffset(srcBase, srcOffset) + : reinterpret_cast(srcOffset); + + void* dst = dstBase ? &fieldAtOffset(dstBase, dstOffset) + : reinterpret_cast(dstOffset); + + memcpy(dst, src, count); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_arrayBaseOffset(Thread*, object, uintptr_t*) +{ + return ArrayBody; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_arrayIndexScale(Thread* t, + object, + uintptr_t* arguments) +{ + GcClass* c + = cast(t, reinterpret_cast(arguments[1]))->vmClass(); + + if (c == type(t, GcBooleanArray::Type) || c == type(t, GcByteArray::Type)) + return 1; + else if (c == type(t, GcShortArray::Type) || c == type(t, GcCharArray::Type)) + return 2; + else if (c == type(t, GcIntArray::Type) || c == type(t, GcFloatArray::Type)) + return 4; + else if (c == type(t, GcLongArray::Type) || c == type(t, GcDoubleArray::Type)) + return 8; + else + return BytesPerWord; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_nio_FixedArrayByteBuffer_allocateFixed(Thread* t, + object, + uintptr_t* arguments) +{ + int capacity = arguments[0]; + GcLongArray* address + = cast(t, reinterpret_cast(arguments[1])); + PROTECT(t, address); + + GcArray* array = reinterpret_cast(allocate3( + t, t->m->heap, Machine::FixedAllocation, ArrayBody + capacity, false)); + + setObjectClass( + t, reinterpret_cast(array), type(t, GcByteArray::Type)); + array->length() = capacity; + + address->body()[0] = reinterpret_cast(array) + ArrayBody; + + return reinterpret_cast(array); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getObject(Thread*, object, uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + + return fieldAtOffset(o, offset); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putObject(Thread* t, object, uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + uintptr_t value = arguments[4]; + + setField(t, o, offset, reinterpret_cast(value)); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putObjectVolatile(Thread* t, + object, + uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + object value = reinterpret_cast(arguments[4]); + + storeStoreMemoryBarrier(); + setField(t, o, offset, value); + storeLoadMemoryBarrier(); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putOrderedObject(Thread* t, + object method, + uintptr_t* arguments) +{ + Avian_sun_misc_Unsafe_putObjectVolatile(t, method, arguments); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getObjectVolatile(Thread*, + object, + uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + + uintptr_t value = fieldAtOffset(o, offset); + loadMemoryBarrier(); + return value; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_compareAndSwapObject(Thread* t, + object, + uintptr_t* arguments) +{ + object target = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + uintptr_t expect = arguments[4]; + uintptr_t update = arguments[5]; + + bool success = atomicCompareAndSwap( + &fieldAtOffset(target, offset), expect, update); + + if (success) { + mark(t, target, offset); + } + + return success; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_compareAndSwapInt(Thread*, + object, + uintptr_t* arguments) +{ + object target = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + uint32_t expect = arguments[4]; + uint32_t update = arguments[5]; + + return atomicCompareAndSwap32( + &fieldAtOffset(target, offset), expect, update); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_compareAndSwapLong(Thread* t UNUSED, + object, + uintptr_t* arguments) +{ + object target = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + uint64_t expect; + memcpy(&expect, arguments + 4, 8); + uint64_t update; + memcpy(&update, arguments + 6, 8); + +#ifdef AVIAN_HAS_CAS64 + return atomicCompareAndSwap64( + &fieldAtOffset(target, offset), expect, update); +#else + PROTECT(t, target); + ACQUIRE_FIELD_FOR_WRITE(t, fieldForOffset(t, target, offset)); + if (fieldAtOffset(target, offset) == expect) { + fieldAtOffset(target, offset) = update; + return true; + } else { + return false; + } +#endif +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getLongVolatile(Thread* t, + object, + uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + + object lock; + if (BytesPerWord < 8) { + if (objectClass(t, o)->arrayDimensions()) { + lock = objectClass(t, o); + } else { + lock = fieldForOffset(t, cast(t, o), offset); + } + + PROTECT(t, o); + PROTECT(t, lock); + acquire(t, lock); + } + + int64_t result = fieldAtOffset(o, offset); + + if (BytesPerWord < 8) { + release(t, lock); + } else { + loadMemoryBarrier(); + } + + return result; +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putLongVolatile(Thread* t, + object, + uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + int64_t value; + memcpy(&value, arguments + 4, 8); + + object lock; + if (BytesPerWord < 8) { + if (objectClass(t, o)->arrayDimensions()) { + lock = objectClass(t, o); + } else { + lock = fieldForOffset(t, cast(t, o), offset); + } + + PROTECT(t, o); + PROTECT(t, lock); + acquire(t, lock); + } else { + storeStoreMemoryBarrier(); + } + + fieldAtOffset(o, offset) = value; + + if (BytesPerWord < 8) { + release(t, lock); + } else { + storeLoadMemoryBarrier(); + } +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putOrderedLong(Thread* t, + object method, + uintptr_t* arguments) +{ + // todo: we might be able to use weaker barriers here than + // putLongVolatile does + Avian_sun_misc_Unsafe_putLongVolatile(t, method, arguments); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_unpark(Thread* t, object, uintptr_t* arguments) +{ + GcThread* thread = cast(t, reinterpret_cast(arguments[1])); + + monitorAcquire(t, cast(t, interruptLock(t, thread))); + thread->unparked() = true; + monitorNotify(t, cast(t, interruptLock(t, thread))); + monitorRelease(t, cast(t, interruptLock(t, thread))); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_park(Thread* t, object, uintptr_t* arguments) +{ + bool absolute = arguments[1]; + int64_t time; + memcpy(&time, arguments + 2, 8); + + int64_t then = t->m->system->now(); + + if (absolute) { + time -= then; + if (time <= 0) { + return; + } + } else if (time) { + // if not absolute, interpret time as nanoseconds, but make sure + // it doesn't become zero when we convert to milliseconds, since + // zero is interpreted as infinity below + time = (time / (1000 * 1000)) + 1; + } + + monitorAcquire(t, cast(t, interruptLock(t, t->javaThread))); + bool interrupted = false; + while (time >= 0 + and (not(t->javaThread->unparked() or t->javaThread->interrupted() + or (interrupted = monitorWait( + t, + cast(t, interruptLock(t, t->javaThread)), + time))))) { + int64_t now = t->m->system->now(); + time -= now - then; + then = now; + + if (time == 0) { + break; + } + } + if (interrupted) { + t->javaThread->interrupted() = true; + } + t->javaThread->unparked() = false; + monitorRelease(t, cast(t, interruptLock(t, t->javaThread))); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putIntVolatile(Thread*, object, uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + int32_t value = arguments[4]; + + storeStoreMemoryBarrier(); + fieldAtOffset(o, offset) = value; + storeLoadMemoryBarrier(); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putOrderedInt(Thread* t, + object method, + uintptr_t* arguments) +{ + Avian_sun_misc_Unsafe_putIntVolatile(t, method, arguments); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getIntVolatile(Thread*, object, uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + + int32_t result = fieldAtOffset(o, offset); + loadMemoryBarrier(); + return result; +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putByteVolatile(Thread*, object, uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + int8_t value = arguments[4]; + + storeStoreMemoryBarrier(); + fieldAtOffset(o, offset) = value; + storeLoadMemoryBarrier(); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getByteVolatile(Thread*, object, uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + + int8_t result = fieldAtOffset(o, offset); + loadMemoryBarrier(); + return result; +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putBooleanVolatile(Thread* t, + object method, + uintptr_t* arguments) +{ + Avian_sun_misc_Unsafe_putByteVolatile(t, method, arguments); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getBooleanVolatile(Thread* t, + object method, + uintptr_t* arguments) +{ + return Avian_sun_misc_Unsafe_getByteVolatile(t, method, arguments); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putShortVolatile(Thread*, + object, + uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + int16_t value = arguments[4]; + + storeStoreMemoryBarrier(); + fieldAtOffset(o, offset) = value; + storeLoadMemoryBarrier(); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getShortVolatile(Thread*, + object, + uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + + int16_t result = fieldAtOffset(o, offset); + loadMemoryBarrier(); + return result; +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putCharVolatile(Thread* t, + object method, + uintptr_t* arguments) +{ + Avian_sun_misc_Unsafe_putShortVolatile(t, method, arguments); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getCharVolatile(Thread* t, + object method, + uintptr_t* arguments) +{ + return Avian_sun_misc_Unsafe_getShortVolatile(t, method, arguments); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putFloatVolatile(Thread* t, + object method, + uintptr_t* arguments) +{ + Avian_sun_misc_Unsafe_putIntVolatile(t, method, arguments); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getFloatVolatile(Thread* t, + object method, + uintptr_t* arguments) +{ + return Avian_sun_misc_Unsafe_getIntVolatile(t, method, arguments); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putDoubleVolatile(Thread* t, + object method, + uintptr_t* arguments) +{ + Avian_sun_misc_Unsafe_putLongVolatile(t, method, arguments); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getDoubleVolatile(Thread* t, + object method, + uintptr_t* arguments) +{ + return Avian_sun_misc_Unsafe_getLongVolatile(t, method, arguments); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_throwException(Thread* t, + object, + uintptr_t* arguments) +{ + vm::throw_(t, cast(t, reinterpret_cast(arguments[1]))); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_Classes_primitiveClass(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast(primitiveClass(t, arguments[0])); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Class_getEnclosingMethod(Thread* t, + object, + uintptr_t* arguments) +{ + GcClass* c + = cast(t, reinterpret_cast(arguments[0]))->vmClass(); + PROTECT(t, c); + + GcClassAddendum* addendum = c->addendum(); + if (addendum) { + PROTECT(t, addendum); + + GcByteArray* enclosingClass + = cast(t, addendum->enclosingClass()); + + if (enclosingClass) { + GcClass* enclosing = resolveClass(t, c->loader(), enclosingClass); + + GcPair* enclosingMethod = cast(t, addendum->enclosingMethod()); + + if (enclosingMethod) { + return reinterpret_cast(t->m->classpath->makeJMethod( + t, + cast( + t, + findMethodInClass( + t, + enclosing, + cast(t, enclosingMethod->first()), + cast(t, enclosingMethod->second()))))); + } + } + } + return 0; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Class_getEnclosingClass(Thread* t, + object, + uintptr_t* arguments) +{ + GcClass* c + = cast(t, reinterpret_cast(arguments[0]))->vmClass(); + PROTECT(t, c); + + GcClassAddendum* addendum = c->addendum(); + if (addendum) { + GcByteArray* enclosingClass + = cast(t, addendum->enclosingClass()); + + if (enclosingClass) { + return reinterpret_cast( + getJClass(t, resolveClass(t, c->loader(), enclosingClass))); + } + } + return 0; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Class_getEnclosingConstructor(Thread* t, + object method, + uintptr_t* arguments) +{ + return Avian_java_lang_Class_getEnclosingMethod(t, method, arguments); +} + + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Object_toString(Thread* t, object, uintptr_t* arguments) +{ + object this_ = reinterpret_cast(arguments[0]); + + unsigned hash = objectHash(t, this_); + GcString* s = makeString( + t, "%s@0x%x", objectClass(t, this_)->name()->body().begin(), hash); + + return reinterpret_cast(s); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Object_getVMClass(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast( + objectClass(t, reinterpret_cast(arguments[0]))); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_Object_wait(Thread* t, object, uintptr_t* arguments) +{ + object this_ = reinterpret_cast(arguments[0]); + int64_t milliseconds; + memcpy(&milliseconds, arguments + 1, 8); + + vm::wait(t, this_, milliseconds); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_Object_notify(Thread* t, object, uintptr_t* arguments) +{ + notify(t, reinterpret_cast(arguments[0])); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_Object_notifyAll(Thread* t, object, uintptr_t* arguments) +{ + notifyAll(t, reinterpret_cast(arguments[0])); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Object_hashCode(Thread* t, object, uintptr_t* arguments) +{ + return objectHash(t, reinterpret_cast(arguments[0])); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Object_clone(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast( + clone(t, reinterpret_cast(arguments[0]))); +} diff --git a/sgx-jvm/avian/src/classpath-android.cpp b/sgx-jvm/avian/src/classpath-android.cpp new file mode 100644 index 0000000000..c5db8be5e8 --- /dev/null +++ b/sgx-jvm/avian/src/classpath-android.cpp @@ -0,0 +1,1680 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include +using namespace std; + +struct JavaVM; +struct _JavaVM; +struct _JNIEnv; + +struct JniConstants { + static void init(_JNIEnv* env); +}; + +extern "C" int JNI_OnLoad(JavaVM*, void*); + +int libconscrypt_JNI_OnLoad(_JavaVM*, void*); + +#define _POSIX_C_SOURCE 200112L +#undef _GNU_SOURCE +#include "avian/machine.h" +#include "avian/classpath-common.h" +#include "avian/process.h" +#include "avian/util.h" + +#ifdef PLATFORM_WINDOWS +const char* getErrnoDescription( + int err); // This function is defined in mingw-extensions.cpp +#endif + +using namespace vm; + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_Classes_defineVMClass(Thread*, object, uintptr_t*); + +namespace { + +namespace local { + +void* getDirectBufferAddress(Thread* t, object b) +{ + PROTECT(t, b); + + GcField* field + = resolveField(t, objectClass(t, b), "effectiveDirectAddress", "J"); + + return reinterpret_cast(fieldAtOffset(b, field->offset())); +} + +void JNICALL loadLibrary(Thread* t, object, uintptr_t* arguments) +{ + GcString* name = cast(t, reinterpret_cast(arguments[1])); + + Thread::LibraryLoadStack stack( + t, cast(t, reinterpret_cast(arguments[2]))); + + unsigned length = name->length(t); + THREAD_RUNTIME_ARRAY(t, char, n, length + 1); + stringChars(t, name, RUNTIME_ARRAY_BODY(n)); + + /* org_conscrypt_NativeCrypto.o is linked statically, and in Avian build + the package is named org.conscrypt.NativeCrypto. When Android code sees + that name it thinks the library isn't linked as a part of Android, so it + tries to load in dynamically, but there's actually no need to, so we + just ignore this request. */ + if (strcmp(RUNTIME_ARRAY_BODY(n), "conscrypt_jni") != 0) { + loadLibrary(t, "", RUNTIME_ARRAY_BODY(n), true, true); + } +} + +void JNICALL gc(Thread* t, object, uintptr_t*) +{ + collect(t, Heap::MajorCollection); +} + +void JNICALL finalizeAllEnqueued(Thread*, object, uintptr_t*) +{ + // ignore +} + +int64_t JNICALL appLoader(Thread* t, object, uintptr_t*) +{ + return reinterpret_cast(roots(t)->appLoader()); +} + +int64_t JNICALL defineClass(Thread* t, object method, uintptr_t* arguments) +{ + uintptr_t args[] = {arguments[0], arguments[2], arguments[3], arguments[4]}; + + int64_t v = Avian_avian_Classes_defineVMClass(t, method, args); + + if (v) { + return reinterpret_cast( + getJClass(t, cast(t, reinterpret_cast(v)))); + } else { + return 0; + } +} + +int64_t JNICALL mapData(Thread*, object, uintptr_t*); + +void JNICALL closeMemoryMappedFile(Thread*, GcMethod*, uintptr_t*); + +object translateStackTrace(Thread* t, object raw) +{ + PROTECT(t, raw); + + object array = makeObjectArray( + t, + resolveClass(t, roots(t)->bootLoader(), "java/lang/StackTraceElement"), + objectArrayLength(t, raw)); + PROTECT(t, array); + + for (unsigned i = 0; i < objectArrayLength(t, array); ++i) { + GcStackTraceElement* e = makeStackTraceElement( + t, cast(t, objectArrayBody(t, raw, i))); + + setField(t, array, ArrayBody + (i * BytesPerWord), e); + } + + return array; +} + +class MyClasspath : public Classpath { + public: + MyClasspath(Allocator* allocator) + : allocator(allocator), tzdata(0), mayInitClasses_(false) + { + } + + virtual GcJclass* makeJclass(Thread* t, GcClass* class_) + { + PROTECT(t, class_); + + GcJclass* c + = reinterpret_cast(allocate(t, GcJclass::FixedSize, true)); + setObjectClass(t, c, type(t, GcJclass::Type)); + c->setVmClass(t, class_); + + return c; + } + + virtual GcString* makeString(Thread* t, + object array, + int32_t offset, + int32_t length) + { + if (objectClass(t, array) == type(t, GcByteArray::Type)) { + GcByteArray* byteArray = cast(t, array); + PROTECT(t, byteArray); + + assertT(t, offset + length <= static_cast(byteArray->length())); + + GcCharArray* charArray = makeCharArray(t, length); + for (int i = 0; i < length; ++i) { + expect(t, (byteArray->body()[offset + i] & 0x80) == 0); + + charArray->body()[i] = byteArray->body()[offset + i]; + } + + array = charArray; + offset = 0; + } else { + expect(t, objectClass(t, array) == type(t, GcCharArray::Type)); + + assertT(t, + offset + length + <= static_cast(cast(t, array)->length())); + } + + return vm::makeString(t, array, offset, length, 0); + } + + virtual GcThread* makeThread(Thread* t, Thread* parent) + { + const unsigned NormalPriority = 5; + + GcThreadGroup* group = 0; + PROTECT(t, group); + if (parent) { + group = parent->javaThread->group(); + } else { + resolveSystemClass(t, + roots(t)->bootLoader(), + type(t, GcThreadGroup::Type)->name(), + false); + + group = cast(t, makeNew(t, type(t, GcThreadGroup::Type))); + + GcMethod* constructor + = resolveMethod(t, type(t, GcThreadGroup::Type), "", "()V"); + + t->m->processor->invoke(t, constructor, group); + } + + resolveSystemClass( + t, roots(t)->bootLoader(), type(t, GcThread::Type)->name(), false); + + GcThread* thread = cast(t, makeNew(t, type(t, GcThread::Type))); + PROTECT(t, thread); + + GcMethod* constructor + = resolveMethod(t, + type(t, GcThread::Type), + "", + "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V"); + + t->m->processor->invoke( + t, constructor, thread, group, 0, NormalPriority, false); + + thread->setContextClassLoader(t, roots(t)->appLoader()); + + return thread; + } + + virtual object makeJMethod(Thread* t, GcMethod* vmMethod) + { + PROTECT(t, vmMethod); + + GcJmethod* jmethod = makeJmethod(t, vmMethod, false); + + return vmMethod->name()->body()[0] == '<' + ? static_cast(makeJconstructor(t, jmethod)) + : static_cast(jmethod); + } + + virtual GcMethod* getVMMethod(Thread* t, object jmethod) + { + return objectClass(t, jmethod) == type(t, GcJmethod::Type) + ? cast(t, jmethod)->vmMethod() + : cast(t, jmethod)->method()->vmMethod(); + } + + virtual object makeJField(Thread* t, GcField* vmField) + { + return makeJfield(t, vmField, false); + } + + virtual GcField* getVMField(Thread* t UNUSED, GcJfield* jfield) + { + return jfield->vmField(); + } + + virtual void clearInterrupted(Thread*) + { + // ignore + } + + virtual void runThread(Thread* t) + { + // force monitor creation so we don't get an OutOfMemory error + // later when we try to acquire it: + objectMonitor(t, t->javaThread->lock(), true); + + THREAD_RESOURCE0(t, { + vm::acquire(t, t->javaThread->lock()); + t->clearFlag(Thread::ActiveFlag); + t->javaThread->peer() = 0; + vm::notifyAll(t, t->javaThread->lock()); + vm::release(t, t->javaThread->lock()); + }); + + GcMethod* method = resolveMethod( + t, roots(t)->bootLoader(), "java/lang/Thread", "run", "()V"); + + t->m->processor->invoke(t, method, t->javaThread); + } + + virtual void resolveNative(Thread* t, GcMethod* method) + { + vm::resolveNative(t, method); + } + + void interceptMethods(Thread* t, bool updateRuntimeData) + { + { + GcClass* c + = resolveClass(t, roots(t)->bootLoader(), "java/lang/Runtime", false); + + if (c) { + PROTECT(t, c); + + intercept(t, + c, + "loadLibrary", + "(Ljava/lang/String;Ljava/lang/ClassLoader;)V", + voidPointer(loadLibrary), + updateRuntimeData); + } + } + + { + GcClass* c + = resolveClass(t, roots(t)->bootLoader(), "java/lang/System", false); + + if (c) { + PROTECT(t, c); + + intercept(t, c, "gc", "()V", voidPointer(gc), updateRuntimeData); + } + } + + { + GcClass* c = resolveClass( + t, roots(t)->bootLoader(), "java/lang/ref/FinalizerReference", false); + + if (c) { + PROTECT(t, c); + + intercept(t, + c, + "finalizeAllEnqueued", + "()V", + voidPointer(finalizeAllEnqueued), + updateRuntimeData); + } + } + + { + GcClass* c = resolveClass( + t, roots(t)->bootLoader(), "java/lang/ClassLoader", false); + + if (c) { + PROTECT(t, c); + + intercept(t, + c, + "createSystemClassLoader", + "()Ljava/lang/ClassLoader;", + voidPointer(appLoader), + updateRuntimeData); + + intercept(t, + c, + "defineClass", + "(Ljava/lang/String;[BII)Ljava/lang/Class;", + voidPointer(defineClass), + updateRuntimeData); + } + } + + { + GcClass* c = resolveClass( + t, roots(t)->bootLoader(), "libcore/util/ZoneInfoDB", false); + + if (c) { + PROTECT(t, c); + + intercept(t, + c, + "mapData", + "()Llibcore/io/MemoryMappedFile;", + voidPointer(mapData), + updateRuntimeData); + } + } + + { + GcClass* c = resolveClass( + t, roots(t)->bootLoader(), "libcore/io/MemoryMappedFile", false); + + if (c) { + PROTECT(t, c); + + intercept(t, + c, + "close", + "()V", + voidPointer(closeMemoryMappedFile), + updateRuntimeData); + } + } + } + + virtual void interceptMethods(Thread* t) + { + interceptMethods(t, false); + } + + virtual void preBoot(Thread* t) + { +// Android's System.initSystemProperties throws an NPE if +// LD_LIBRARY_PATH is not set as of this writing: +#ifdef PLATFORM_WINDOWS + _wputenv(L"LD_LIBRARY_PATH=(dummy)"); +#elif(!defined AVIAN_IOS) + setenv("LD_LIBRARY_PATH", "", false); +#endif + + interceptMethods(t, true); + + JniConstants::init(reinterpret_cast<_JNIEnv*>(t)); + + JNI_OnLoad(reinterpret_cast< ::JavaVM*>(t->m), 0); + + libconscrypt_JNI_OnLoad(reinterpret_cast< ::_JavaVM*>(t->m), 0); + + mayInitClasses_ = true; + } + + virtual bool mayInitClasses() + { + return mayInitClasses_; + } + + virtual void boot(Thread*) + { + // ignore + } + + virtual const char* bootClasspath() + { + return AVIAN_CLASSPATH; + } + + virtual object makeDirectByteBuffer(Thread* t, void* p, jlong capacity) + { + GcClass* c + = resolveClass(t, roots(t)->bootLoader(), "java/nio/DirectByteBuffer"); + PROTECT(t, c); + + object instance = makeNew(t, c); + PROTECT(t, instance); + + GcMethod* constructor = resolveMethod(t, c, "", "(JI)V"); + + t->m->processor->invoke(t, + constructor, + instance, + reinterpret_cast(p), + static_cast(capacity)); + + return instance; + } + + virtual void* getDirectBufferAddress(Thread* t, object b) + { + return local::getDirectBufferAddress(t, b); + } + + virtual int64_t getDirectBufferCapacity(Thread* t, object b) + { + PROTECT(t, b); + + GcField* field = resolveField(t, objectClass(t, b), "capacity", "I"); + + return fieldAtOffset(b, field->offset()); + } + + virtual bool canTailCall(Thread* t UNUSED, + GcMethod*, + GcByteArray* calleeClassName, + GcByteArray* calleeMethodName, + GcByteArray*) + { + // we can't tail call System.load[Library] or + // Runtime.load[Library] due to their use of + // ClassLoader.getCaller, which gets confused if we elide stack + // frames. + + return ( + (strcmp("loadLibrary", + reinterpret_cast(calleeMethodName->body().begin())) + and strcmp("load", + reinterpret_cast(calleeMethodName->body().begin()))) + or (strcmp("java/lang/System", + reinterpret_cast(calleeClassName->body().begin())) + and strcmp( + "java/lang/Runtime", + reinterpret_cast(calleeClassName->body().begin())))); + } + + virtual GcClassLoader* libraryClassLoader(Thread* t, GcMethod* caller) + { + return strcmp("java/lang/Runtime", + reinterpret_cast( + caller->class_()->name()->body().begin())) == 0 + ? t->libraryLoadStack->classLoader + : caller->class_()->loader(); + } + + virtual void shutDown(Thread*) + { + // ignore + } + + virtual void dispose() + { + if (tzdata) { + tzdata->dispose(); + } + allocator->free(this, sizeof(*this)); + } + + Allocator* allocator; + System::Region* tzdata; + bool mayInitClasses_; +}; + +int64_t JNICALL mapData(Thread* t, object, uintptr_t*) +{ + GcClass* c + = resolveClass(t, roots(t)->bootLoader(), "libcore/io/MemoryMappedFile"); + PROTECT(t, c); + + object instance = makeNew(t, c); + PROTECT(t, instance); + + GcMethod* constructor = resolveMethod(t, c, "", "(JJ)V"); + + const char* jar = "javahomeJar"; + Finder* finder = getFinder(t, jar, strlen(jar)); + if (finder) { + System::Region* r = finder->find("tzdata"); + if (r) { + MyClasspath* cp = static_cast(t->m->classpath); + + expect(t, cp->tzdata == 0); + + cp->tzdata = r; + + t->m->processor->invoke(t, + constructor, + instance, + reinterpret_cast(r->start()), + static_cast(r->length())); + + return reinterpret_cast(instance); + } + } + + throwNew(t, GcRuntimeException::Type); +} + +void JNICALL + closeMemoryMappedFile(Thread* t, GcMethod* method, uintptr_t* arguments) +{ + object file = reinterpret_cast(arguments[0]); + PROTECT(t, file); + + MyClasspath* cp = static_cast(t->m->classpath); + + if (cp->tzdata) { + GcField* field = resolveField(t, objectClass(t, file), "address", "J"); + + if (fieldAtOffset(file, field->offset()) + == reinterpret_cast(cp->tzdata->start())) { + cp->tzdata->dispose(); + cp->tzdata = 0; + + fieldAtOffset(file, field->offset()) = 0; + return; + } + } + + t->m->processor->invoke(t, + cast(t, + getMethodRuntimeData(t, method) + ->native() + ->as(t) + ->original()), + file); +} + +} // namespace local + +} // namespace + +namespace vm { + +Classpath* makeClasspath(System*, + Allocator* allocator, + const char*, + const char*) +{ + return new (allocator->allocate(sizeof(local::MyClasspath))) + local::MyClasspath(allocator); +} + +} // namespace vm + +extern "C" int jniRegisterNativeMethods(JNIEnv* e, + const char* className, + const JNINativeMethod* methods, + int methodCount) +{ + jclass c = e->vtable->FindClass(e, className); + + if (c) { + e->vtable->RegisterNatives(e, c, methods, methodCount); + } else { + e->vtable->ExceptionClear(e); + } + + return 0; +} + +extern "C" void jniLogException(JNIEnv*, int, const char*, jthrowable) +{ + // ignore +} + +extern "C" int jniThrowException(JNIEnv* e, + const char* className, + const char* message) +{ + jclass c = e->vtable->FindClass(e, className); + + if (c) { + e->vtable->ThrowNew(e, c, message); + } + + return 0; +} + +extern "C" int jniThrowExceptionFmt(JNIEnv* e, + const char* className, + const char* format, + va_list args) +{ + const unsigned size = 4096; + char buffer[size]; + ::vsnprintf(buffer, size, format, args); + return jniThrowException(e, className, buffer); +} + +extern "C" int jniThrowNullPointerException(JNIEnv* e, const char* message) +{ + return jniThrowException(e, "java/lang/NullPointerException", message); +} + +extern "C" int jniThrowRuntimeException(JNIEnv* e, const char* message) +{ + return jniThrowException(e, "java/lang/RuntimeException", message); +} + +extern "C" int jniThrowIOException(JNIEnv* e, const char* message) +{ + return jniThrowException(e, "java/lang/IOException", message); +} + +extern "C" const char* jniStrError(int error, char* buffer, size_t length) +{ +#ifdef PLATFORM_WINDOWS + const char* s = getErrnoDescription(error); + if (strlen(s) < length) { + strncpy(buffer, s, length); + return buffer; + } else { + return 0; + } +#else + if (static_cast(strerror_r(error, buffer, length)) == 0) { + return buffer; + } else { + return 0; + } +#endif +} + +/* + * Android log priority values (as text) + */ +const char* const androidLogPriorityTitles[] = {"UNKNOWN", + "DEFAULT", + "VERBOSE", + "DEBUG", + "INFO", + "WARNING", + "ERROR", + "FATAL", + "SILENT"}; + +extern "C" int __android_log_print(int priority, + const char* tag, + const char* format, + ...) +{ + va_list a; + const unsigned size = 4096; + char buffer[size]; + + va_start(a, format); + ::vsnprintf(buffer, size, format, a); + va_end(a); + +#ifndef PLATFORM_WINDOWS + return printf( + "[%s] %s: %s\n", androidLogPriorityTitles[priority], tag, buffer); +#else + return __mingw_fprintf( + stderr, "[%s] %s: %s\n", androidLogPriorityTitles[priority], tag, buffer); +#endif +} + +extern "C" int jniGetFDFromFileDescriptor(JNIEnv* e, jobject descriptor) +{ + return e->vtable->GetIntField( + e, + descriptor, + e->vtable->GetFieldID(e, + e->vtable->FindClass(e, "java/io/FileDescriptor"), + "descriptor", + "I")); +} + +extern "C" void jniSetFileDescriptorOfFD(JNIEnv* e, + jobject descriptor, + int value) +{ + e->vtable->SetIntField( + e, + descriptor, + e->vtable->GetFieldID(e, + e->vtable->FindClass(e, "java/io/FileDescriptor"), + "descriptor", + "I"), + value); +} + +extern "C" jobject jniCreateFileDescriptor(JNIEnv* e, int fd) +{ + jobject descriptor = e->vtable->NewObject( + e, + e->vtable->FindClass(e, "java/io/FileDescriptor"), + e->vtable->GetMethodID(e, + e->vtable->FindClass(e, "java/io/FileDescriptor"), + "", + "()V")); + + jniSetFileDescriptorOfFD(e, descriptor, fd); + + return descriptor; +} + +int register_org_apache_harmony_dalvik_NativeTestTarget(_JNIEnv*) +{ + // ignore + return 0; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_String_compareTo(Thread* t, object, uintptr_t* arguments) +{ + GcString* a = cast(t, reinterpret_cast(arguments[0])); + GcString* b = cast(t, reinterpret_cast(arguments[1])); + + unsigned length = a->length(t); + if (length > b->length(t)) { + length = b->length(t); + } + + for (unsigned i = 0; i < length; ++i) { + int d = stringCharAt(t, a, i) - stringCharAt(t, b, i); + if (d) { + return d; + } + } + + return a->length(t) - b->length(t); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_String_isEmpty(Thread* t, object, uintptr_t* arguments) +{ + return cast(t, reinterpret_cast(arguments[0]))->length(t) + == 0; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_String_length(Thread* t, object, uintptr_t* arguments) +{ + return cast(t, reinterpret_cast(arguments[0]))->length(t); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_String_intern(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast( + intern(t, reinterpret_cast(arguments[0]))); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_String_charAt(Thread* t, object, uintptr_t* arguments) +{ + return stringCharAt(t, + cast(t, reinterpret_cast(arguments[0])), + arguments[1]); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_String_equals(Thread* t, object, uintptr_t* arguments) +{ + return arguments[1] and stringEqual(t, + reinterpret_cast(arguments[0]), + reinterpret_cast(arguments[1])); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_String_fastIndexOf(Thread* t, object, uintptr_t* arguments) +{ + GcString* s = cast(t, reinterpret_cast(arguments[0])); + unsigned c = arguments[1]; + unsigned start = arguments[2]; + + for (unsigned i = start; i < s->length(t); ++i) { + if (stringCharAt(t, s, i) == c) { + return i; + } + } + + return -1; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_SystemClassLoader_findLoadedVMClass(Thread*, + object, + uintptr_t*); + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_dalvik_system_VMRuntime_bootClassPath(Thread* t, object, uintptr_t*) +{ + return reinterpret_cast(roots(t)->bootLoader()); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_dalvik_system_VMRuntime_classPath(Thread* t, object, uintptr_t*) +{ + return reinterpret_cast(roots(t)->appLoader()); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_dalvik_system_VMRuntime_vmVersion(Thread* t, object, uintptr_t*) +{ + return reinterpret_cast(makeString(t, "%s", AVIAN_VERSION)); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_dalvik_system_VMRuntime_properties(Thread* t, object, uintptr_t*) +{ + object array + = makeObjectArray(t, type(t, GcString::Type), t->m->propertyCount + 1); + PROTECT(t, array); + + unsigned i; + for (i = 0; i < t->m->propertyCount; ++i) { + GcString* s = makeString(t, "%s", t->m->properties[i]); + setField( + t, array, ArrayBody + (i * BytesPerWord), reinterpret_cast(s)); + } + + { + GcString* s = makeString(t, "%s", "java.protocol.handler.pkgs=avian"); + setField(t, + array, + ArrayBody + (i++ * BytesPerWord), + reinterpret_cast(s)); + } + + return reinterpret_cast(array); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_Runtime_gc(Thread* t, object, uintptr_t*) +{ + collect(t, Heap::MajorCollection); +} + +extern "C" AVIAN_EXPORT jlong JNICALL + Avian_java_lang_Runtime_maxMemory(Thread* t, object, uintptr_t*) +{ + return t->m->heap->limit(); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_Runtime_nativeExit(Thread* t, object, uintptr_t* arguments) +{ + shutDown(t); + + t->m->system->exit(arguments[0]); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Runtime_nativeLoad(Thread* t, object, uintptr_t* arguments) +{ + GcString* name = cast(t, reinterpret_cast(arguments[0])); + PROTECT(t, name); + + unsigned length = name->length(t); + THREAD_RUNTIME_ARRAY(t, char, n, length + 1); + stringChars(t, name, RUNTIME_ARRAY_BODY(n)); + + if (loadLibrary(t, "", RUNTIME_ARRAY_BODY(n), false, true)) { + return 0; + } else { + return reinterpret_cast(name); + } +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_System_arraycopy(Thread* t, object, uintptr_t* arguments) +{ + arrayCopy(t, + reinterpret_cast(arguments[0]), + arguments[1], + reinterpret_cast(arguments[2]), + arguments[3], + arguments[4]); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_System_arraycopyCharUnchecked(Thread* t, + object method, + uintptr_t* arguments) +{ + Avian_java_lang_System_arraycopy(t, method, arguments); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_System_arraycopyByteUnchecked(Thread* t, + object method, + uintptr_t* arguments) +{ + Avian_java_lang_System_arraycopy(t, method, arguments); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_objectFieldOffset(Thread* t, + object, + uintptr_t* arguments) +{ + return cast(t, reinterpret_cast(arguments[1])) + ->vmField() + ->offset(); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_Thread_nativeInterrupt(Thread* t, + object, + uintptr_t* arguments) +{ + interrupt( + t, + reinterpret_cast( + cast(t, reinterpret_cast(arguments[0]))->peer())); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Thread_interrupted(Thread* t, object, uintptr_t*) +{ + return getAndClearInterrupted(t, t); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Thread_isInterrupted(Thread* t, + object, + uintptr_t* arguments) +{ + return cast(t, reinterpret_cast(arguments[0])) + ->interrupted(); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Thread_nativeGetStatus(Thread*, + object, + uintptr_t* arguments) +{ + enum { New, Runnable, Blocked, Waiting, TimedWaiting, Terminated }; + + // todo: more detail? (e.g. waiting, terminated, etc.) + return arguments[1] ? Runnable : New; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Thread_currentThread(Thread* t, object, uintptr_t*) +{ + return reinterpret_cast(t->javaThread); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_Thread_nativeCreate(Thread* t, object, uintptr_t* arguments) +{ + startThread(t, cast(t, reinterpret_cast(arguments[0]))); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_Thread_nativeSetName(Thread*, object, uintptr_t*) +{ + // ignore +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_Thread_sleep(Thread* t, object, uintptr_t* arguments) +{ + object lock = reinterpret_cast(arguments[0]); + PROTECT(t, lock); + + int64_t milliseconds; + memcpy(&milliseconds, arguments + 1, 8); + if (arguments[2] > 0) + ++milliseconds; + if (milliseconds <= 0) + milliseconds = 1; + + acquire(t, lock); + vm::wait(t, lock, milliseconds); + release(t, lock); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Thread_nativeHoldsLock(Thread* t, + object, + uintptr_t* arguments) +{ + if (cast(t, reinterpret_cast(arguments[0])) + != t->javaThread) { + throwNew(t, + GcIllegalStateException::Type, + "VMThread.holdsLock may only be called on current thread"); + } + + GcMonitor* m + = objectMonitor(t, reinterpret_cast(arguments[1]), false); + + return m and m->owner() == t; +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_Thread_yield(Thread* t, object, uintptr_t*) +{ + t->m->system->yield(); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_dalvik_system_VMStack_getThreadStackTrace(Thread* t, + object, + uintptr_t* arguments) +{ + Thread* p = reinterpret_cast( + cast(t, reinterpret_cast(arguments[0]))->peer()); + + return reinterpret_cast(local::translateStackTrace( + t, p == t ? makeTrace(t) : t->m->processor->getStackTrace(t, p))); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_dalvik_system_VMStack_getCallingClassLoader(Thread* t, + object, + uintptr_t*) +{ + class Visitor : public Processor::StackVisitor { + public: + Visitor(Thread* t) : t(t), loader(0), counter(2) + { + } + + virtual bool visit(Processor::StackWalker* walker) + { + if (counter--) { + return true; + } else { + this->loader = walker->method()->class_()->loader(); + return false; + } + } + + Thread* t; + GcClassLoader* loader; + unsigned counter; + } v(t); + + t->m->processor->walkStack(t, &v); + + return reinterpret_cast(v.loader); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_dalvik_system_VMStack_getClosestUserClassLoader(Thread* t, + object, + uintptr_t* arguments) +{ + GcClassLoader* boot + = cast(t, reinterpret_cast(arguments[0])); + + GcClassLoader* app + = cast(t, reinterpret_cast(arguments[1])); + + class Visitor : public Processor::StackVisitor { + public: + Visitor(Thread* t, GcClassLoader* boot, GcClassLoader* app) + : t(t), loader(0), boot(boot), app(app) + { + } + + virtual bool visit(Processor::StackWalker* walker) + { + GcClassLoader* loader = walker->method()->class_()->loader(); + if (loader == boot or loader == app) { + return true; + } else { + this->loader = loader; + return false; + } + } + + Thread* t; + GcClassLoader* loader; + GcClassLoader* boot; + GcClassLoader* app; + } v(t, boot, app); + + t->m->processor->walkStack(t, &v); + + return reinterpret_cast(v.loader); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_dalvik_system_VMStack_getClasses(Thread* t, object, uintptr_t*) +{ + class Visitor : public Processor::StackVisitor { + public: + Visitor(Thread* t) : t(t), array(0), counter(0) + { + } + + virtual bool visit(Processor::StackWalker* walker) + { + if (counter < 2) { + return true; + } else { + if (array == 0) { + array = makeObjectArray(t, type(t, GcJclass::Type), walker->count()); + } + + GcJclass* c = getJClass(t, walker->method()->class_()); + + assertT(t, counter - 2 < objectArrayLength(t, array)); + + setField(t, array, ArrayBody + ((counter - 2) * BytesPerWord), c); + + return true; + } + + ++counter; + } + + Thread* t; + object array; + unsigned counter; + } v(t); + + PROTECT(t, v.array); + + t->m->processor->walkStack(t, &v); + + return reinterpret_cast( + v.array ? v.array : makeObjectArray(t, type(t, GcJclass::Type), 0)); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Math_min(Thread*, object, uintptr_t* arguments) +{ + return min(static_cast(arguments[0]), static_cast(arguments[1])); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Math_max(Thread*, object, uintptr_t* arguments) +{ + return max(static_cast(arguments[0]), static_cast(arguments[1])); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Math_cos(Thread*, object, uintptr_t* arguments) +{ + int64_t v; + memcpy(&v, arguments, 8); + return doubleToBits(cos(bitsToDouble(v))); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Math_sin(Thread*, object, uintptr_t* arguments) +{ + int64_t v; + memcpy(&v, arguments, 8); + return doubleToBits(sin(bitsToDouble(v))); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Math_sqrt(Thread*, object, uintptr_t* arguments) +{ + int64_t v; + memcpy(&v, arguments, 8); + return doubleToBits(sqrt(bitsToDouble(v))); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Math_abs__I(Thread*, object, uintptr_t* arguments) +{ + return abs(static_cast(arguments[0])); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Math_abs__J(Thread*, object, uintptr_t* arguments) +{ + int64_t v; memcpy(&v, arguments, 8); + return llabs(v); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Math_abs__F(Thread*, object, uintptr_t* arguments) +{ + return floatToBits(abs(bitsToFloat(arguments[0]))); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Float_intBitsToFloat(Thread*, object, uintptr_t* arguments) +{ + return arguments[0]; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Float_floatToIntBits(Thread*, object, uintptr_t* arguments) +{ + if (((arguments[0] & 0x7F800000) == 0x7F800000) + and ((arguments[0] & 0x007FFFFF) != 0)) { + return 0x7fc00000; + } else { + return arguments[0]; + } +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Double_doubleToRawLongBits(Thread*, + object, + uintptr_t* arguments) +{ + int64_t v; + memcpy(&v, arguments, 8); + // todo: do we need to do NaN checks as in + // Avian_java_lang_Float_floatToIntBits above? If so, update + // Double.doubleToRawLongBits in the Avian class library too. + return v; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_reflect_Method_getCaller(Thread* t, object, uintptr_t*) +{ + return reinterpret_cast(getCaller(t, 2)); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_reflect_Method_invoke(Thread* t, + object, + uintptr_t* arguments) +{ + return invokeMethod(t, + cast(t, reinterpret_cast(arguments[0])), + reinterpret_cast(arguments[1]), + reinterpret_cast(arguments[2])); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_reflect_Field_getObject(Thread*, + object, + uintptr_t* arguments) +{ + return reinterpret_cast(fieldAtOffset( + reinterpret_cast(arguments[0]), arguments[1])); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_reflect_Field_setObject(Thread* t, + object, + uintptr_t* arguments) +{ + setField(t, + reinterpret_cast(arguments[0]), + arguments[1], + reinterpret_cast(arguments[2])); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_reflect_Field_getPrimitive(Thread* t, + object, + uintptr_t* arguments) +{ + return getPrimitive( + t, reinterpret_cast(arguments[0]), arguments[1], arguments[2]); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_reflect_Field_setPrimitive(Thread* t, + object, + uintptr_t* arguments) +{ + int64_t value; + memcpy(&value, arguments + 3, 8); + + setPrimitive(t, + reinterpret_cast(arguments[0]), + arguments[1], + arguments[2], + value); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_reflect_Constructor_make(Thread* t, + object, + uintptr_t* arguments) +{ + return reinterpret_cast( + make(t, cast(t, reinterpret_cast(arguments[0])))); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_ref_Reference_get(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast( + cast(t, reinterpret_cast(arguments[0]))->target()); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Throwable_nativeFillInStackTrace(Thread* t, + object, + uintptr_t*) +{ + return reinterpret_cast(getTrace(t, 2)); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Throwable_nativeGetStackTrace(Thread* t, + object, + uintptr_t* arguments) +{ + return reinterpret_cast( + local::translateStackTrace(t, reinterpret_cast(arguments[0]))); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_Classes_getVMClass(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast( + objectClass(t, reinterpret_cast(arguments[0]))); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_Classes_makeMethod(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast( + makeMethod(t, + cast(t, reinterpret_cast(arguments[0])), + arguments[1])); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_Classes_isAssignableFrom(Thread* t, + object, + uintptr_t* arguments) +{ + GcClass* this_ = cast(t, reinterpret_cast(arguments[0])); + GcClass* that = cast(t, reinterpret_cast(arguments[1])); + + if (LIKELY(that)) { + return vm::isAssignableFrom(t, this_, that); + } else { + throwNew(t, GcNullPointerException::Type); + } +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_reflect_Array_createObjectArray(Thread* t, + object, + uintptr_t* arguments) +{ + return reinterpret_cast(makeObjectArray( + t, + cast(t, reinterpret_cast(arguments[0]))->vmClass(), + arguments[1])); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_nio_ByteOrder_isLittleEndian(Thread*, object, uintptr_t*) +{ + return true; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_dalvik_system_VMRuntime_newNonMovableArray(Thread* t, + object, + uintptr_t* arguments) +{ + if (cast(t, reinterpret_cast(arguments[1]))->vmClass() + == type(t, GcJbyte::Type)) { + GcByteArray* array + = reinterpret_cast(allocate3(t, + t->m->heap, + Machine::FixedAllocation, + ArrayBody + arguments[2], + false)); + + setObjectClass(t, array, type(t, GcByteArray::Type)); + array->length() = arguments[2]; + + return reinterpret_cast(array); + } else { + // todo + abort(t); + } +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_dalvik_system_VMRuntime_addressOf(Thread*, + object, + uintptr_t* arguments) +{ + return arguments[1] + ArrayBody; +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_libcore_io_Memory_pokeLong(Thread*, object, uintptr_t* arguments) +{ + int64_t address; + memcpy(&address, arguments, 8); + int64_t v; + memcpy(&v, arguments + 2, 8); + if (arguments[4]) { + v = swapV8(v); + } + memcpy(reinterpret_cast(address), &v, 8); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_libcore_io_Memory_peekLong(Thread*, object, uintptr_t* arguments) +{ + int64_t address; + memcpy(&address, arguments, 8); + int64_t v; + memcpy(&v, reinterpret_cast(address), 8); + return arguments[2] ? swapV8(v) : v; +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_libcore_io_Memory_pokeInt(Thread*, object, uintptr_t* arguments) +{ + int64_t address; + memcpy(&address, arguments, 8); + int32_t v = arguments[3] ? swapV4(arguments[2]) : arguments[2]; + memcpy(reinterpret_cast(address), &v, 4); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_libcore_io_Memory_peekInt(Thread*, object, uintptr_t* arguments) +{ + int64_t address; + memcpy(&address, arguments, 8); + int32_t v; + memcpy(&v, reinterpret_cast(address), 4); + return arguments[2] ? swapV4(v) : v; +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_libcore_io_Memory_pokeShort(Thread*, object, uintptr_t* arguments) +{ + int64_t address; + memcpy(&address, arguments, 8); + int16_t v = arguments[3] ? swapV2(arguments[2]) : arguments[2]; + memcpy(reinterpret_cast(address), &v, 2); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_libcore_io_Memory_peekShort(Thread*, object, uintptr_t* arguments) +{ + int64_t address; + memcpy(&address, arguments, 8); + int16_t v; + memcpy(&v, reinterpret_cast(address), 2); + return arguments[2] ? swapV2(v) : v; +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_libcore_io_Memory_pokeByte(Thread*, object, uintptr_t* arguments) +{ + int64_t address; + memcpy(&address, arguments, 8); + *reinterpret_cast(address) = arguments[2]; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_libcore_io_Memory_peekByte(Thread*, object, uintptr_t* arguments) +{ + int64_t address; + memcpy(&address, arguments, 8); + return *reinterpret_cast(address); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_System_nanoTime(Thread* t, object, uintptr_t*) +{ + return t->m->system->now() * 1000 * 1000; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_System_currentTimeMillis(Thread* t, object, uintptr_t*) +{ + return t->m->system->now(); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_System_identityHashCode(Thread* t, + object, + uintptr_t* arguments) +{ + return objectHash(t, reinterpret_cast(arguments[0])); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_util_concurrent_atomic_AtomicLong_VMSupportsCS8(Thread*, + object, + uintptr_t*) +{ + return true; +} + +#ifdef PLATFORM_WINDOWS + +#include + +void register_java_io_Console(_JNIEnv*) +{ +} +void register_java_lang_ProcessManager(_JNIEnv*) +{ +} +void register_libcore_net_RawSocket(_JNIEnv*) +{ +} +// void register_org_apache_harmony_xnet_provider_jsse_NativeCrypto(_JNIEnv*) { +// } + +extern "C" AVIAN_EXPORT void JNICALL + Avian_libcore_io_OsConstants_initConstants(Thread* t, + object m, + uintptr_t*) +{ + GcMethod* method = cast(t, m); + GcClass* c = method->class_(); + PROTECT(t, c); + + object table = c->staticTable(); + PROTECT(t, table); + + GcField* field = resolveField(t, c, "STDIN_FILENO", "I"); + fieldAtOffset(table, field->offset()) = 0; + + field = resolveField(t, c, "STDOUT_FILENO", "I"); + fieldAtOffset(table, field->offset()) = 1; + + field = resolveField(t, c, "STDERR_FILENO", "I"); + fieldAtOffset(table, field->offset()) = 2; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_libcore_io_Posix_getenv(Thread* t, object, uintptr_t* arguments) +{ + GcString* name = cast(t, reinterpret_cast(arguments[1])); + + THREAD_RUNTIME_ARRAY(t, uint16_t, chars, name->length(t) + 1); + stringChars(t, name, RUNTIME_ARRAY_BODY(chars)); + + wchar_t* value + = _wgetenv(reinterpret_cast(RUNTIME_ARRAY_BODY(chars))); + + if (value) { + unsigned size = wcslen(value); + + GcCharArray* a = makeCharArray(t, size); + if (size) { + memcpy(a->body().begin(), value, size * sizeof(jchar)); + } + + return reinterpret_cast( + t->m->classpath->makeString(t, a, 0, size)); + } else { + return 0; + } +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_libcore_io_Posix_uname(Thread* t, object, uintptr_t*) +{ + GcClass* c + = resolveClass(t, roots(t)->bootLoader(), "libcore/io/StructUtsname"); + PROTECT(t, c); + + object instance = makeNew(t, c); + PROTECT(t, instance); + +#ifdef ARCH_x86_32 + object arch = makeString(t, "x86"); +#elif defined ARCH_x86_64 + object arch = makeString(t, "x86_64"); +#elif defined ARCH_arm + object arch = makeString(t, "arm"); +#else + object arch = makeString(t, "unknown"); +#endif + + setField(t, + instance, + resolveField(t, c, "machine", "Ljava/lang/String;")->offset(), + arch); + + object platform = makeString(t, "Windows"); + + setField(t, + instance, + resolveField(t, c, "sysname", "Ljava/lang/String;")->offset(), + platform); + + object version = makeString(t, "unknown"); + + setField(t, + instance, + resolveField(t, c, "release", "Ljava/lang/String;")->offset(), + version); + + return reinterpret_cast(instance); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_libcore_io_Posix_writeBytes(Thread* t, object, uintptr_t* arguments) +{ + object fd = reinterpret_cast(arguments[1]); + PROTECT(t, fd); + + GcByteArray* buffer = cast(t, reinterpret_cast(arguments[2])); + PROTECT(t, buffer); + + int offset = arguments[3]; + int count = arguments[4]; + + int d = jniGetFDFromFileDescriptor(t, &fd); + + int r; + if (objectClass(t, buffer) == type(t, GcByteArray::Type)) { + void* tmp = t->m->heap->allocate(count); + memcpy(tmp, &buffer->body()[offset], count); + { + ENTER(t, Thread::IdleState); + r = _write(d, tmp, count); + } + t->m->heap->free(tmp, count); + } else { + void* p = local::getDirectBufferAddress(t, buffer); + { + ENTER(t, Thread::IdleState); + r = _write(d, p, count); + } + } + + if (r < 0) { + THREAD_RUNTIME_ARRAY(t, char, message, 256); + throwNew(t, + GcRuntimeException::Type, + "writeBytes %d: %s", + d, + jniStrError(errno, RUNTIME_ARRAY_BODY(message), 0)); + } else { + return r; + } +} + +#endif diff --git a/sgx-jvm/avian/src/classpath-avian.cpp b/sgx-jvm/avian/src/classpath-avian.cpp new file mode 100644 index 0000000000..41ca4b1637 --- /dev/null +++ b/sgx-jvm/avian/src/classpath-avian.cpp @@ -0,0 +1,692 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/machine.h" +#include "avian/classpath-common.h" +#include "avian/process.h" + +#include + +using namespace vm; + +namespace { + +namespace local { + +class MyClasspath : public Classpath { + public: + MyClasspath(Allocator* allocator) : allocator(allocator) + { + } + + virtual GcJclass* makeJclass(Thread* t, GcClass* class_) + { + return vm::makeJclass(t, class_); + } + + virtual GcString* makeString(Thread* t, + object array, + int32_t offset, + int32_t length) + { + return vm::makeString(t, array, offset, length, 0); + } + + virtual GcThread* makeThread(Thread* t, Thread* parent) + { + GcThreadGroup* group; + if (parent) { + group = parent->javaThread->group(); + } else { + group = makeThreadGroup(t, 0, 0, 0); + } + + const unsigned NewState = 0; + const unsigned NormalPriority = 5; + + return vm::makeThread(t, + 0, + 0, + 0, + 0, + 0, + NewState, + NormalPriority, + 0, + 0, + 0, + roots(t)->appLoader(), + 0, + 0, + group, + 0); + } + + virtual object makeJMethod(Thread* t, GcMethod* vmMethod) + { + PROTECT(t, vmMethod); + + GcJmethod* jmethod = makeJmethod(t, vmMethod, false); + + return vmMethod->name()->body()[0] == '<' + ? (object)makeJconstructor(t, jmethod) + : (object)jmethod; + } + + virtual GcMethod* getVMMethod(Thread* t, object jmethod) + { + return objectClass(t, jmethod) == type(t, GcJmethod::Type) + ? cast(t, jmethod)->vmMethod() + : cast(t, jmethod)->method()->vmMethod(); + } + + virtual object makeJField(Thread* t, GcField* vmField) + { + return makeJfield(t, vmField, false); + } + + virtual GcField* getVMField(Thread* t UNUSED, GcJfield* jfield) + { + return jfield->vmField(); + } + + virtual void clearInterrupted(Thread*) + { + // ignore + } + + virtual void runThread(Thread* t) + { + GcMethod* method = resolveMethod(t, + roots(t)->bootLoader(), + "java/lang/Thread", + "run", + "(Ljava/lang/Thread;)V"); + + t->m->processor->invoke(t, method, 0, t->javaThread); + } + + virtual void resolveNative(Thread* t, GcMethod* method) + { + vm::resolveNative(t, method); + } + + virtual void interceptMethods(Thread*) + { + // ignore + } + + virtual void preBoot(Thread*) + { + // ignore + } + + virtual bool mayInitClasses() + { + return true; + } + + virtual void boot(Thread*) + { + // ignore + } + + virtual const char* bootClasspath() + { + return AVIAN_CLASSPATH; + } + + virtual object makeDirectByteBuffer(Thread* t, void* p, jlong capacity) + { + GcClass* c + = resolveClass(t, roots(t)->bootLoader(), "java/nio/DirectByteBuffer"); + PROTECT(t, c); + + object instance = makeNew(t, c); + PROTECT(t, instance); + + GcMethod* constructor = resolveMethod(t, c, "", "(JI)V"); + + t->m->processor->invoke(t, + constructor, + instance, + reinterpret_cast(p), + static_cast(capacity)); + + return instance; + } + + virtual void* getDirectBufferAddress(Thread* t, object b) + { + PROTECT(t, b); + + GcField* field = resolveField(t, objectClass(t, b), "address", "J"); + + return reinterpret_cast(fieldAtOffset(b, field->offset())); + } + + virtual int64_t getDirectBufferCapacity(Thread* t, object b) + { + PROTECT(t, b); + + GcField* field = resolveField(t, objectClass(t, b), "capacity", "I"); + + return fieldAtOffset(b, field->offset()); + } + + virtual bool canTailCall(Thread* t UNUSED, + GcMethod*, + GcByteArray* calleeClassName, + GcByteArray* calleeMethodName, + GcByteArray*) + { + // we can't tail call System.load[Library] or + // Runtime.load[Library] due to their use of + // ClassLoader.getCaller, which gets confused if we elide stack + // frames. + + return ( + (strcmp("loadLibrary", + reinterpret_cast(calleeMethodName->body().begin())) + and strcmp("load", + reinterpret_cast(calleeMethodName->body().begin()))) + or (strcmp("java/lang/System", + reinterpret_cast(calleeClassName->body().begin())) + and strcmp( + "java/lang/Runtime", + reinterpret_cast(calleeClassName->body().begin())))); + } + + virtual GcClassLoader* libraryClassLoader(Thread* t, GcMethod* caller) + { + return (caller->class_() == type(t, Gc::ClassLoaderType) + and t->libraryLoadStack) + ? t->libraryLoadStack->classLoader + : caller->class_()->loader(); + } + + virtual void shutDown(Thread*) + { + // ignore + } + + virtual void dispose() + { + allocator->free(this, sizeof(*this)); + } + + Allocator* allocator; +}; + +void enumerateThreads(Thread* t, + Thread* x, + GcArray* array, + unsigned* index, + unsigned limit) +{ + if (*index < limit) { + array->setBodyElement(t, *index, x->javaThread); + ++(*index); + + if (x->peer) + enumerateThreads(t, x->peer, array, index, limit); + + if (x->child) + enumerateThreads(t, x->child, array, index, limit); + } +} + +} // namespace local + +} // namespace + +namespace vm { + +Classpath* makeClasspath(System*, + Allocator* allocator, + const char*, + const char*) +{ + return new (allocator->allocate(sizeof(local::MyClasspath))) + local::MyClasspath(allocator); +} + +} // namespace vm + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_io_ObjectInputStream_makeInstance(Thread* t, + object, + uintptr_t* arguments) +{ + GcClass* c = cast(t, reinterpret_cast(arguments[0])); + + return reinterpret_cast(make(t, c)); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_LegacyObjectInputStream_makeInstance(Thread* t, + object, + uintptr_t* arguments) +{ + return Avian_java_io_ObjectInputStream_makeInstance(t, NULL, arguments); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_reflect_Field_getPrimitive(Thread* t, + object, + uintptr_t* arguments) +{ + return getPrimitive( + t, reinterpret_cast(arguments[0]), arguments[1], arguments[2]); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_reflect_Field_getObject(Thread*, + object, + uintptr_t* arguments) +{ + return reinterpret_cast(fieldAtOffset( + reinterpret_cast(arguments[0]), arguments[1])); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_reflect_Field_setPrimitive(Thread* t, + object, + uintptr_t* arguments) +{ + int64_t value; + memcpy(&value, arguments + 3, 8); + + setPrimitive(t, + reinterpret_cast(arguments[0]), + arguments[1], + arguments[2], + value); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_reflect_Field_setObject(Thread* t, + object, + uintptr_t* arguments) +{ + setField(t, + reinterpret_cast(arguments[0]), + arguments[1], + reinterpret_cast(arguments[2])); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_reflect_Constructor_make(Thread* t, + object, + uintptr_t* arguments) +{ + return reinterpret_cast( + make(t, cast(t, reinterpret_cast(arguments[0])))); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_reflect_Method_getCaller(Thread* t, object, uintptr_t*) +{ + return reinterpret_cast(getCaller(t, 2)); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_reflect_Method_invoke(Thread* t, + object, + uintptr_t* arguments) +{ + return invokeMethod(t, + cast(t, reinterpret_cast(arguments[0])), + reinterpret_cast(arguments[1]), + reinterpret_cast(arguments[2])); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_reflect_Array_getLength(Thread* t, + object, + uintptr_t* arguments) +{ + object array = reinterpret_cast(arguments[0]); + + if (LIKELY(array)) { + unsigned elementSize = objectClass(t, array)->arrayElementSize(); + + if (LIKELY(elementSize)) { + return fieldAtOffset(array, BytesPerWord); + } else { + throwNew(t, GcIllegalArgumentException::Type); + } + } else { + throwNew(t, GcNullPointerException::Type); + } +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_reflect_Array_makeObjectArray(Thread* t, + object, + uintptr_t* arguments) +{ + GcJclass* elementType + = cast(t, reinterpret_cast(arguments[0])); + int length = arguments[1]; + + return reinterpret_cast( + makeObjectArray(t, elementType->vmClass(), length)); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Float_floatToRawIntBits(Thread*, + object, + uintptr_t* arguments) +{ + return static_cast(*arguments); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Float_intBitsToFloat(Thread*, object, uintptr_t* arguments) +{ + return static_cast(*arguments); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Double_doubleToRawLongBits(Thread*, + object, + uintptr_t* arguments) +{ + int64_t v; + memcpy(&v, arguments, 8); + return v; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Double_longBitsToDouble(Thread*, + object, + uintptr_t* arguments) +{ + int64_t v; + memcpy(&v, arguments, 8); + return v; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_String_intern(Thread* t, object, uintptr_t* arguments) +{ + object this_ = reinterpret_cast(arguments[0]); + + return reinterpret_cast(intern(t, this_)); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_System_getVMProperties(Thread* t, object, uintptr_t*) +{ + object array + = makeObjectArray(t, type(t, GcString::Type), t->m->propertyCount); + PROTECT(t, array); + + for (unsigned i = 0; i < t->m->propertyCount; ++i) { + GcString* s = makeString(t, "%s", t->m->properties[i]); + reinterpret_cast(array)->setBodyElement(t, i, s); + } + + return reinterpret_cast(array); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_System_arraycopy(Thread* t, object, uintptr_t* arguments) +{ + arrayCopy(t, + reinterpret_cast(arguments[0]), + arguments[1], + reinterpret_cast(arguments[2]), + arguments[3], + arguments[4]); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_System_identityHashCode(Thread* t, + object, + uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[0]); + + if (LIKELY(o)) { + return objectHash(t, o); + } else { + throwNew(t, GcNullPointerException::Type); + } +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_ClassLoader_getCaller(Thread* t, object, uintptr_t*) +{ + return reinterpret_cast(getJClass(t, getCaller(t, 2)->class_())); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_ClassLoader_load(Thread* t, object, uintptr_t* arguments) +{ + GcString* name = cast(t, reinterpret_cast(arguments[0])); + + Thread::LibraryLoadStack stack( + t, + cast(t, reinterpret_cast(arguments[1])) + ->vmClass() + ->loader()); + + bool mapName = arguments[2]; + + unsigned length = name->length(t); + THREAD_RUNTIME_ARRAY(t, char, n, length + 1); + stringChars(t, name, RUNTIME_ARRAY_BODY(n)); + + loadLibrary(t, "", RUNTIME_ARRAY_BODY(n), mapName, true); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_Runtime_gc(Thread* t, object, uintptr_t*) +{ + collect(t, Heap::MajorCollection); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_Runtime_addShutdownHook(Thread* t, + object, + uintptr_t* arguments) +{ + object hook = reinterpret_cast(arguments[1]); + PROTECT(t, hook); + + ACQUIRE(t, t->m->shutdownLock); + + GcPair* p = makePair(t, hook, roots(t)->shutdownHooks()); + // sequence point, for gc (don't recombine statements) + roots(t)->setShutdownHooks(t, p); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Throwable_trace(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast(getTrace(t, arguments[0])); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Throwable_resolveTrace(Thread* t, + object, + uintptr_t* arguments) +{ + object trace = reinterpret_cast(*arguments); + PROTECT(t, trace); + + unsigned length = objectArrayLength(t, trace); + GcClass* elementType = type(t, GcStackTraceElement::Type); + object array = makeObjectArray(t, elementType, length); + PROTECT(t, array); + + for (unsigned i = 0; i < length; ++i) { + GcStackTraceElement* ste = makeStackTraceElement( + t, cast(t, objectArrayBody(t, trace, i))); + reinterpret_cast(array)->setBodyElement(t, i, ste); + } + + return reinterpret_cast(array); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Thread_currentThread(Thread* t, object, uintptr_t*) +{ + return reinterpret_cast(t->javaThread); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Thread_doStart(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast( + startThread(t, cast(t, reinterpret_cast(*arguments)))); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_Thread_interrupt(Thread* t, object, uintptr_t* arguments) +{ + int64_t peer; + memcpy(&peer, arguments, 8); + + threadInterrupt(t, reinterpret_cast(peer)->javaThread); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Thread_interrupted(Thread* t, object, uintptr_t* arguments) +{ + int64_t peer; + memcpy(&peer, arguments, 8); + + return threadIsInterrupted( + t, reinterpret_cast(peer)->javaThread, true); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Thread_getStackTrace(Thread* t, + object, + uintptr_t* arguments) +{ + int64_t peer; + memcpy(&peer, arguments, 8); + + if (reinterpret_cast(peer) == t) { + return reinterpret_cast(makeTrace(t)); + } else { + return reinterpret_cast( + t->m->processor->getStackTrace(t, reinterpret_cast(peer))); + } +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Thread_activeCount(Thread* t, object, uintptr_t*) +{ + return t->m->liveCount; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Thread_enumerate(Thread* t, object, uintptr_t* arguments) +{ + GcArray* array = cast(t, reinterpret_cast(*arguments)); + + ACQUIRE_RAW(t, t->m->stateLock); + + unsigned count = min(t->m->liveCount, + objectArrayLength(t, reinterpret_cast(array))); + unsigned index = 0; + local::enumerateThreads(t, t->m->rootThread, array, &index, count); + return count; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Thread_holdsLock(Thread* t, object, uintptr_t* arguments) +{ + GcMonitor* m + = objectMonitor(t, reinterpret_cast(arguments[0]), false); + + return m and m->owner() == t; +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_Thread_yield(Thread* t, object, uintptr_t*) +{ + t->m->system->yield(); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_Atomic_getOffset(Thread* t, object, uintptr_t* arguments) +{ + return cast(t, reinterpret_cast(arguments[0])) + ->vmField() + ->offset(); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_objectFieldOffset(Thread* t, + object, + uintptr_t* arguments) +{ + return cast(t, reinterpret_cast(arguments[1])) + ->vmField() + ->offset(); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_Atomic_compareAndSwapObject(Thread* t, + object, + uintptr_t* arguments) +{ + object target = reinterpret_cast(arguments[0]); + int64_t offset; + memcpy(&offset, arguments + 1, 8); + uintptr_t expect = arguments[3]; + uintptr_t update = arguments[4]; + + bool success = atomicCompareAndSwap( + &fieldAtOffset(target, offset), expect, update); + + if (success) { + mark(t, target, offset); + } + + return success; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_Classes_isAssignableFrom(Thread* t, + object, + uintptr_t* arguments) +{ + GcClass* this_ = cast(t, reinterpret_cast(arguments[0])); + GcClass* that = cast(t, reinterpret_cast(arguments[1])); + + if (LIKELY(that)) { + return vm::isAssignableFrom(t, this_, that); + } else { + throwNew(t, GcNullPointerException::Type); + } +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_Classes_getVMClass(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast( + objectClass(t, reinterpret_cast(arguments[0]))); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_Classes_makeMethod(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast( + makeMethod(t, + cast(t, reinterpret_cast(arguments[0])), + arguments[1])); +} diff --git a/sgx-jvm/avian/src/classpath-openjdk.cpp b/sgx-jvm/avian/src/classpath-openjdk.cpp new file mode 100644 index 0000000000..dfc8464c2e --- /dev/null +++ b/sgx-jvm/avian/src/classpath-openjdk.cpp @@ -0,0 +1,6149 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/machine.h" +#include "avian/classpath-common.h" +#include "avian/util.h" +#include "avian/process.h" + +#ifdef PLATFORM_WINDOWS + +#include +#include +#include +#include +#include +#include +#include +#include + +#undef interface + +#define CLOSE _close +#define READ _read +#define WRITE _write +#define FSTAT _fstat +#define STAT _stat +#define LSEEK _lseek + +#define S_ISSOCK(x) false + +#ifdef _MSC_VER +#define S_ISREG(x) ((x) | _S_IFREG) +#define S_ISDIR(x) ((x) | _S_IFDIR) +#define S_IRUSR _S_IREAD +#define S_IWUSR _S_IWRITE +#else +#define OPEN _open +#endif + +#define O_RDONLY _O_RDONLY + +#if (defined AVIAN_OPENJDK_SRC) \ + || ((defined __x86_64__) && (defined __MINGW32__)) +#define EXPORT(x) x +#else +#define EXPORT(x) _##x +#endif + +typedef int socklen_t; + +#define RTLD_DEFAULT 0 + +#else // not PLATFORM_WINDOWS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define OPEN open +#define CLOSE close +#define READ read +#define WRITE write +#define STAT stat +#define FSTAT fstat +#define LSEEK lseek + +#define EXPORT(x) x + +#endif // not PLATFORM_WINDOWS + +#define JVM_EEXIST -100 + +using namespace vm; + +namespace { + +#ifdef _MSC_VER +inline int OPEN(string_t path, int mask, int mode) +{ + int fd; + if (_wsopen_s(&fd, path, mask, _SH_DENYNO, mode) == 0) { + return fd; + } else { + return -1; + } +} +#endif + +namespace local { + +const int JMM_VERSION_1_0 = 0x20010000; + +struct jmmOptionalSupport { + unsigned isLowMemoryDetectionSupported : 1; + unsigned isCompilationTimeMonitoringSupported : 1; + unsigned isThreadContentionMonitoringSupported : 1; + unsigned isCurrentThreadCpuTimeSupported : 1; + unsigned isOtherThreadCpuTimeSupported : 1; + unsigned isBootClassPathSupported : 1; + unsigned isObjectMonitorUsageSupported : 1; + unsigned isSynchronizerUsageSupported : 1; +}; + +typedef unsigned jmmLongAttribute; +typedef unsigned jmmBoolAttribute; +typedef unsigned jmmStatisticType; +typedef unsigned jmmThresholdType; +typedef unsigned jmmVMGlobalType; +typedef unsigned jmmVMGlobalOrigin; + +struct jmmVMGlobal { + jstring name; + jvalue value; + jmmVMGlobalType type; + jmmVMGlobalOrigin origin; + unsigned writeable : 1; + unsigned external : 1; + unsigned reserved : 30; + void* reserved1; + void* reserved2; +}; + +struct jmmExtAttributeInfo { + const char* name; + char type; + const char* description; +}; + +struct jmmGCStat { + jlong gc_index; + jlong start_time; + jlong end_time; + jobjectArray usage_before_gc; + jobjectArray usage_after_gc; + jint gc_ext_attribute_values_size; + jvalue* gc_ext_attribute_values; + jint num_gc_ext_attributes; +}; + +struct JmmInterface { + void* reserved1; + void* reserved2; + + jint(JNICALL* GetVersion)(JNIEnv*); + + jint(JNICALL* GetOptionalSupport)(JNIEnv*, jmmOptionalSupport*); + + jobject(JNICALL* GetInputArguments)(JNIEnv*); + + jint(JNICALL* GetThreadInfo)(JNIEnv*, jlongArray, jint, jobjectArray); + + jobjectArray(JNICALL* GetInputArgumentArray)(JNIEnv*); + + jobjectArray(JNICALL* GetMemoryPools)(JNIEnv*, jobject); + + jobjectArray(JNICALL* GetMemoryManagers)(JNIEnv*, jobject); + + jobject(JNICALL* GetMemoryPoolUsage)(JNIEnv*, jobject); + + jobject(JNICALL* GetPeakMemoryPoolUsage)(JNIEnv*, jobject); + + void* reserved4; + + jobject(JNICALL* GetMemoryUsage)(JNIEnv*, jboolean); + + jlong(JNICALL* GetLongAttribute)(JNIEnv*, jobject, jmmLongAttribute); + + jboolean(JNICALL* GetBoolAttribute)(JNIEnv*, jmmBoolAttribute); + + jboolean(JNICALL* SetBoolAttribute)(JNIEnv*, jmmBoolAttribute, jboolean); + + jint(JNICALL* GetLongAttributes)(JNIEnv*, + jobject, + jmmLongAttribute*, + jint, + jlong*); + + jobjectArray(JNICALL* FindCircularBlockedThreads)(JNIEnv*); + + jlong(JNICALL* GetThreadCpuTime)(JNIEnv*, jlong); + + jobjectArray(JNICALL* GetVMGlobalNames)(JNIEnv*); + + jint(JNICALL* GetVMGlobals)(JNIEnv*, jobjectArray, jmmVMGlobal*, jint); + + jint(JNICALL* GetInternalThreadTimes)(JNIEnv*, jobjectArray, jlongArray); + + jboolean(JNICALL* ResetStatistic)(JNIEnv*, jvalue, jmmStatisticType); + + void(JNICALL* SetPoolSensor)(JNIEnv*, jobject, jmmThresholdType, jobject); + + jlong(JNICALL* SetPoolThreshold)(JNIEnv*, jobject, jmmThresholdType, jlong); + + jobject(JNICALL* GetPoolCollectionUsage)(JNIEnv*, jobject); + + jint(JNICALL* GetGCExtAttributeInfo)(JNIEnv*, + jobject, + jmmExtAttributeInfo*, + jint); + + void(JNICALL* GetLastGCStat)(JNIEnv*, jobject, jmmGCStat*); + + jlong(JNICALL* GetThreadCpuTimeWithKind)(JNIEnv*, jlong, jboolean); + + void* reserved5; + + jint(JNICALL* DumpHeap0)(JNIEnv*, jstring, jboolean); + + jobjectArray(JNICALL* FindDeadlocks)(JNIEnv*, jboolean); + + void(JNICALL* SetVMGlobal)(JNIEnv*, jstring, jvalue); + + void* reserved6; + + jobjectArray(JNICALL* DumpThreads)(JNIEnv*, jlongArray, jboolean, jboolean); +}; + +const unsigned InterfaceVersion = 4; +const unsigned PageSize = 4 * 1024; +#ifdef AVIAN_OPENJDK_SRC +const int VirtualFileBase = 1000000000; +#endif + +Machine* globalMachine; + +const char* primitiveName(Thread* t, GcClass* c) +{ + if (c == primitiveClass(t, 'V')) { + return "void"; + } else if (c == primitiveClass(t, 'Z')) { + return "boolean"; + } else if (c == primitiveClass(t, 'B')) { + return "byte"; + } else if (c == primitiveClass(t, 'C')) { + return "char"; + } else if (c == primitiveClass(t, 'S')) { + return "short"; + } else if (c == primitiveClass(t, 'I')) { + return "int"; + } else if (c == primitiveClass(t, 'F')) { + return "float"; + } else if (c == primitiveClass(t, 'J')) { + return "long"; + } else if (c == primitiveClass(t, 'D')) { + return "double"; + } else { + abort(t); + } +} + +GcByteArray* getClassName(Thread* t, GcClass* c) +{ + if (c->name() == 0) { + if (c->vmFlags() & PrimitiveFlag) { + PROTECT(t, c); + + GcByteArray* name = makeByteArray(t, primitiveName(t, c)); + + c->setName(t, name); + } else { + abort(t); + } + } + + return c->name(); +} + +GcString* makeClassNameString(Thread* t, GcByteArray* name) +{ + THREAD_RUNTIME_ARRAY(t, char, s, name->length()); + replace('/', + '.', + RUNTIME_ARRAY_BODY(s), + reinterpret_cast(name->body().begin())); + + return makeString(t, "%s", RUNTIME_ARRAY_BODY(s)); +} + +object makeJmethod(Thread* t, GcMethod* vmMethod, int index = -1); + +object makeJconstructor(Thread* t, GcMethod* vmMethod, int index = -1); + +object makeJfield(Thread* t, GcField* vmField, int index = -1); + +#ifdef AVIAN_OPENJDK_SRC +void interceptFileOperations(Thread*, bool); +#endif + +class MyClasspath : public Classpath { + public: + MyClasspath(System* s, + Allocator* allocator, + const char* javaHome, + const char* embedPrefix) + : allocator(allocator), ranNetOnLoad(0), ranManagementOnLoad(0) + { + class StringBuilder { + public: + StringBuilder(System* s, Allocator* allocator) + : s(s), + allocator(allocator), + bufferSize(1024), + buffer(static_cast(allocator->allocate(bufferSize))), + offset(0) + { + } + + void ensure(unsigned capacity) + { + if (capacity > bufferSize) { + unsigned size = max(bufferSize * 2, capacity); + char* b = static_cast(allocator->allocate(size)); + + if (offset) { + memcpy(b, buffer, offset); + } + + allocator->free(buffer, bufferSize); + + buffer = b; + bufferSize = size; + } + } + + void append(const char* append) + { + unsigned length = strlen(append); + ensure(offset + length + 1); + + strncpy(buffer + offset, append, length + 1); + + offset += length; + } + + void append(char c) + { + ensure(2); + + buffer[offset] = c; + buffer[offset + 1] = 0; + + ++offset; + } + + System* s; + Allocator* allocator; + unsigned bufferSize; + char* buffer; + unsigned offset; + } sb(s, allocator); + + unsigned javaHomeOffset = sb.offset; + sb.append(javaHome); + sb.append('\0'); + + unsigned classpathOffset = sb.offset; + sb.append(AVIAN_CLASSPATH); + sb.append(s->pathSeparator()); + sb.append(javaHome); + sb.append("/lib/rt.jar"); + sb.append(s->pathSeparator()); + sb.append(javaHome); + sb.append("/lib/jsse.jar"); + sb.append(s->pathSeparator()); + sb.append(javaHome); + sb.append("/lib/jce.jar"); + sb.append(s->pathSeparator()); + sb.append(javaHome); + sb.append("/lib/ext/sunjce_provider.jar"); + sb.append(s->pathSeparator()); + sb.append(javaHome); + sb.append("/lib/resources.jar"); + sb.append('\0'); + + unsigned libraryPathOffset = sb.offset; + sb.append(javaHome); +#ifdef PLATFORM_WINDOWS +#define LIB_DIR "/bin" +#elif defined __APPLE__ +#define LIB_DIR "/lib" +#elif defined ARCH_x86_64 +#define LIB_DIR "/lib/amd64" +#elif defined ARCH_arm +#define LIB_DIR "/lib/arm" +#else +// todo: handle other architectures +#define LIB_DIR "/lib/i386" +#endif + +#ifdef PLATFORM_WINDOWS + sb.append(LIB_DIR); +#else + sb.append(LIB_DIR ":"); + sb.append(javaHome); + sb.append(LIB_DIR "/xawt"); +#endif + sb.append('\0'); + + unsigned tzMappingsOffset = sb.offset; + sb.append(javaHome); + sb.append("/lib/tzmappings"); + this->tzMappingsLength = sb.offset - tzMappingsOffset; + sb.append('\0'); + + unsigned embedPrefixOffset = sb.offset; + sb.append(embedPrefix); + this->embedPrefixLength = sb.offset - embedPrefixOffset; + + this->javaHome = sb.buffer + javaHomeOffset; + this->classpath = sb.buffer + classpathOffset; + this->libraryPath = sb.buffer + libraryPathOffset; + this->tzMappings = sb.buffer + tzMappingsOffset; + this->embedPrefix = sb.buffer + embedPrefixOffset; + this->buffer = sb.buffer; + this->bufferSize = sb.bufferSize; + } + + virtual GcJclass* makeJclass(Thread* t, GcClass* class_) + { + PROTECT(t, class_); + + GcString* name = makeClassNameString(t, getClassName(t, class_)); + PROTECT(t, name); + + GcJclass* c + = reinterpret_cast(allocate(t, GcJclass::FixedSize, true)); + setObjectClass(t, c, type(t, GcJclass::Type)); + c->setName(t, name); + c->setVmClass(t, class_); +#ifdef HAVE_JclassClassLoader + if (class_->loader() != roots(t)->bootLoader()) { + c->setClassLoader(t, class_->loader()); + } +#endif + + return c; + } + + virtual GcString* makeString(Thread* t, + object oarray, + int32_t offset, + int32_t length) + { + if (objectClass(t, oarray) == type(t, GcByteArray::Type)) { + GcByteArray* array = cast(t, oarray); + PROTECT(t, array); + + GcCharArray* charArray = makeCharArray(t, length); + for (int i = 0; i < length; ++i) { + if (array->body()[offset + i] & 0x80) { + GcMethod* constructor = resolveMethod(t, + type(t, GcString::Type), + "", + "([BIILjava/lang/String;)V"); + PROTECT(t, constructor); + + GcString* utf8 = vm::makeString(t, "UTF8"); + PROTECT(t, utf8); + + object s = makeNew(t, type(t, GcString::Type)); + PROTECT(t, s); + + t->m->processor->invoke( + t, constructor, s, array, offset, length, utf8); + + return cast(t, s); + } + + charArray->body()[i] = array->body()[offset + i]; + } + + oarray = charArray; + offset = 0; + } else { + expect(t, objectClass(t, oarray) == type(t, GcCharArray::Type)); + } + + return vm::makeString(t, oarray, offset, length, 0); + } + + virtual GcThread* makeThread(Thread* t, Thread* parent) + { + const unsigned MaxPriority = 10; + const unsigned NormalPriority = 5; + + GcThreadGroup* group; + if (parent) { + group = parent->javaThread->group(); + } else { + group = reinterpret_cast( + allocate(t, GcThreadGroup::FixedSize, true)); + setObjectClass(t, group, type(t, GcThreadGroup::Type)); + group->maxPriority() = MaxPriority; + } + + PROTECT(t, group); + + GcThread* thread + = reinterpret_cast(allocate(t, GcThread::FixedSize, true)); + setObjectClass(t, thread, type(t, GcThread::Type)); + thread->priority() = NormalPriority; + + thread->setGroup(t, group); + + thread->setContextClassLoader(t, roots(t)->appLoader()); + + PROTECT(t, thread); + + GcJobject* blockerLock = makeJobject(t); + thread->setBlockerLock(t, blockerLock); + +#if HAVE_ThreadName_Ljava_lang_String_ + GcString* name = vm::makeString(t, "Thread-%p", thread); +#else + const unsigned BufferSize = 256; + char buffer[BufferSize]; + unsigned length = vm::snprintf(buffer, BufferSize, "Thread-%p", thread); + GcCharArray* name = makeCharArray(t, length); + for (unsigned i = 0; i < length; ++i) { + name->body()[i] = buffer[i]; + } +#endif + thread->setName(t, name); + + return thread; + } + + virtual object makeJMethod(Thread* t, GcMethod* vmMethod) + { + PROTECT(t, vmMethod); + + return vmMethod->name()->body()[0] == '<' ? makeJconstructor(t, vmMethod) + : makeJmethod(t, vmMethod); + } + + virtual GcMethod* getVMMethod(Thread* t, object jmethod) + { + return cast( + t, + objectClass(t, jmethod) == type(t, GcJmethod::Type) + ? cast(t, + cast(t, jmethod) + ->clazz() + ->vmClass() + ->methodTable()) + ->body()[cast(t, jmethod)->slot()] + : cast(t, + cast(t, jmethod) + ->clazz() + ->vmClass() + ->methodTable()) + ->body()[cast(t, jmethod)->slot()]); + } + + virtual object makeJField(Thread* t, GcField* vmField) + { + return makeJfield(t, vmField); + } + + virtual GcField* getVMField(Thread* t, GcJfield* jfield) + { + return cast( + t, + cast(t, jfield->clazz()->vmClass()->fieldTable()) + ->body()[jfield->slot()]); + } + + virtual void clearInterrupted(Thread* t) + { + vm::clearInterrupted(t); + } + + virtual void runThread(Thread* t) + { + // force monitor creation so we don't get an OutOfMemory error + // later when we try to acquire it: + objectMonitor(t, t->javaThread, true); + + THREAD_RESOURCE0(t, { + vm::acquire(t, t->javaThread); + t->clearFlag(Thread::ActiveFlag); + vm::notifyAll(t, t->javaThread); + vm::release(t, t->javaThread); + + GcThrowable* e = t->exception; + PROTECT(t, e); + + t->exception = 0; + + t->m->processor->invoke(t, + cast(t, roots(t)->threadTerminated()), + t->javaThread->group(), + t->javaThread); + + t->exception = e; + }); + + GcMethod* method = resolveMethod( + t, roots(t)->bootLoader(), "java/lang/Thread", "run", "()V"); + + t->m->processor->invoke(t, method, t->javaThread); + } + + virtual void resolveNative(Thread* t, GcMethod* method) + { + if (strcmp(reinterpret_cast("sun/font/SunFontManager"), + method->class_()->name()->body().begin()) == 0 + and strcmp(reinterpret_cast("initIDs"), + method->name()->body().begin()) == 0 + and strcmp(reinterpret_cast("()V"), + method->spec()->body().begin()) == 0) { + PROTECT(t, method); + + expect(t, loadLibrary(t, libraryPath, "fontmanager", true, true)); + } + + vm::resolveNative(t, method); + } + + virtual void interceptMethods(Thread* t UNUSED) + { +#ifdef AVIAN_OPENJDK_SRC + interceptFileOperations(t, false); +#endif + } + + virtual void preBoot(Thread*) + { + // ignore + } + + virtual bool mayInitClasses() + { + return true; + } + + virtual void boot(Thread* t) + { + globalMachine = t->m; + + resolveSystemClass( + t, roots(t)->bootLoader(), type(t, GcClassLoader::Type)->name()); + + GcMethod* method = resolveMethod(t, + roots(t)->bootLoader(), + "java/lang/ThreadGroup", + "threadTerminated", + "(Ljava/lang/Thread;)V"); + // sequence point, for gc (don't recombine statements) + roots(t)->setThreadTerminated(t, method); + +#ifdef AVIAN_OPENJDK_SRC + interceptFileOperations(t, true); +#else // not AVIAN_OPENJDK_SRC +# ifdef PLATFORM_WINDOWS + expect(t, loadLibrary(t, libraryPath, "msvcr100", true, true)); +# endif + // necessary for using OpenJDK builds from + // https://github.com/ojdkbuild/ojdkbuild: + loadLibrary(t, libraryPath, "ojdkbuild_zlib", true, true, false); + + expect(t, loadLibrary(t, libraryPath, "verify", true, true)); + expect(t, loadLibrary(t, libraryPath, "java", true, true)); +#endif // not AVIAN_OPENJDK_SRC + + { + GcField* assertionLock = resolveField(t, + type(t, GcClassLoader::Type), + "assertionLock", + "Ljava/lang/Object;"); + + setField(t, + roots(t)->bootLoader(), + assertionLock->offset(), + roots(t)->bootLoader()); + } + + { + GcClass* class_ = resolveClass(t, + roots(t)->bootLoader(), + "java/util/Properties", + true, + GcNoClassDefFoundError::Type); + + PROTECT(t, class_); + + object instance = makeNew(t, class_); + + PROTECT(t, instance); + + GcMethod* constructor = resolveMethod(t, class_, "", "()V"); + + t->m->processor->invoke(t, constructor, instance); + + t->m->processor->invoke(t, + roots(t)->bootLoader(), + "java/lang/System", + "setProperties", + "(Ljava/util/Properties;)V", + 0, + instance); + } + + { + GcMethod* constructor = resolveMethod(t, + type(t, GcClassLoader::Type), + "", + "(Ljava/lang/ClassLoader;)V"); + + PROTECT(t, constructor); + + t->m->processor->invoke(t, constructor, roots(t)->bootLoader(), 0); + + t->m->processor->invoke( + t, constructor, roots(t)->appLoader(), roots(t)->bootLoader()); + } + + { + GcField* scl = resolveField( + t, type(t, GcClassLoader::Type), "scl", "Ljava/lang/ClassLoader;"); + + PROTECT(t, scl); + + GcField* sclSet + = resolveField(t, type(t, GcClassLoader::Type), "sclSet", "Z"); + + setField(t, + type(t, GcClassLoader::Type)->staticTable(), + scl->offset(), + roots(t)->appLoader()); + + fieldAtOffset(type(t, GcClassLoader::Type)->staticTable(), + sclSet->offset()) = true; + } + + t->m->processor->invoke(t, + roots(t)->bootLoader(), + "java/lang/System", + "initializeSystemClass", + "()V", + 0); + + t->m->processor->invoke(t, + roots(t)->bootLoader(), + "sun/misc/Launcher", + "getLauncher", + "()Lsun/misc/Launcher;", + 0); + + t->javaThread->setContextClassLoader(t, roots(t)->appLoader()); + } + + virtual const char* bootClasspath() + { + return classpath; + } + + virtual object makeDirectByteBuffer(Thread* t, void* p, jlong capacity) + { + GcClass* c + = resolveClass(t, roots(t)->bootLoader(), "java/nio/DirectByteBuffer"); + PROTECT(t, c); + + object instance = makeNew(t, c); + PROTECT(t, instance); + + GcMethod* constructor = resolveMethod(t, c, "", "(JI)V"); + + t->m->processor->invoke(t, + constructor, + instance, + reinterpret_cast(p), + static_cast(capacity)); + + return instance; + } + + virtual void* getDirectBufferAddress(Thread* t, object b) + { + PROTECT(t, b); + + GcField* field = resolveField(t, objectClass(t, b), "address", "J"); + + return reinterpret_cast(fieldAtOffset(b, field->offset())); + } + + virtual int64_t getDirectBufferCapacity(Thread* t, object b) + { + PROTECT(t, b); + + GcField* field = resolveField(t, objectClass(t, b), "capacity", "I"); + + return fieldAtOffset(b, field->offset()); + } + + virtual bool canTailCall(Thread* t UNUSED, + GcMethod*, + GcByteArray* calleeClassName, + GcByteArray* calleeMethodName, + GcByteArray*) + { + // we can't tail call System.loadLibrary or Runtime.loadLibrary + // due to their use of System.getCallerClass, which gets confused + // if we elide stack frames. + + return (strcmp("loadLibrary", + reinterpret_cast(calleeMethodName->body().begin())) + or (strcmp("java/lang/System", + reinterpret_cast(calleeClassName->body().begin())) + and strcmp("java/lang/Runtime", + reinterpret_cast( + calleeClassName->body().begin())))) + + // and we can't tail call Reflection.getCallerClass because the + // number of stack frames will be wrong + and (strcmp( + "getCallerClass", + reinterpret_cast(calleeMethodName->body().begin())) + or strcmp("sun/reflect/Reflection", + reinterpret_cast( + calleeClassName->body().begin()))); + } + + virtual GcClassLoader* libraryClassLoader(Thread* t, GcMethod* caller) + { +#ifdef AVIAN_OPENJDK_SRC + return (caller->class_() == type(t, GcClassLoader::Type) + and t->libraryLoadStack) + ? t->libraryLoadStack->classLoader +#else + return strcmp("java/lang/ClassLoader$NativeLibrary", + reinterpret_cast( + caller->class_()->name()->body().begin())) == 0 + ? cast( + t, + cast(t, + t->m->processor->invoke( + t, + resolveMethod(t, + caller->class_(), + "getFromClass", + "()Ljava/lang/Class;"), + 0))->vmClass())->loader() +#endif + : caller->class_()->loader(); + } + + virtual void shutDown(Thread* t) + { + GcClass* c + = resolveClass(t, roots(t)->bootLoader(), "java/lang/Shutdown", false); + + if (c) { + GcMethod* m = findMethodOrNull(t, c, "shutdown", "()V"); + + if (m) { + t->m->processor->invoke(t, m, 0); + } + } + } + + virtual void dispose() + { + allocator->free(buffer, bufferSize); + allocator->free(this, sizeof(*this)); + } + + Allocator* allocator; + const char* javaHome; + const char* classpath; + const char* libraryPath; + const char* tzMappings; + const char* embedPrefix; + char* buffer; + unsigned bufferSize; + unsigned tzMappingsLength; + unsigned embedPrefixLength; + unsigned filePathField; + unsigned fileDescriptorFdField; + unsigned fileInputStreamFdField; + unsigned zipFileJzfileField; + unsigned zipEntryNameField; + unsigned zipEntryTimeField; + unsigned zipEntryCrcField; + unsigned zipEntrySizeField; + unsigned zipEntryCsizeField; + unsigned zipEntryMethodField; + bool ranNetOnLoad; + bool ranManagementOnLoad; + JmmInterface jmmInterface; +}; + +struct JVM_ExceptionTableEntryType { + jint start_pc; + jint end_pc; + jint handler_pc; + jint catchType; +}; + +struct jvm_version_info { + unsigned jvm_version; + unsigned update_version : 8; + unsigned special_update_version : 8; + unsigned reserved1 : 16; + unsigned reserved2; + unsigned is_attach_supported : 1; + unsigned is_kernel_jvm : 1; + unsigned : 30; + unsigned : 32; + unsigned : 32; +}; + +bool pathEqual(const char* a, const char* b, unsigned length) +{ +#ifdef PLATFORM_WINDOWS + return strncasecmp(a, b, length) == 0; +#else + return strncmp(a, b, length) == 0; +#endif +} + +class EmbeddedFile { + public: + EmbeddedFile(MyClasspath* cp, const char* path, unsigned pathLength) + { + if (pathEqual(cp->embedPrefix, path, cp->embedPrefixLength)) { + const char* p = path + cp->embedPrefixLength; + while (*p == '/') + ++p; + + this->jar = p; + + if (*p == 0) { + this->jarLength = 0; + this->path = 0; + this->pathLength = 0; + return; + } + + while (*p and *p != '/') + ++p; + + this->jarLength = p - this->jar; + + while (*p == '/') + ++p; + + this->path = p; + this->pathLength = pathLength - (p - path); + } else { + this->jar = 0; + this->jarLength = 0; + this->path = 0; + this->pathLength = 0; + } + } + + const char* jar; + const char* path; + unsigned jarLength; + unsigned pathLength; +}; + +#ifdef AVIAN_OPENJDK_SRC +int64_t JNICALL + getFileAttributes(Thread* t, GcMethod* method, uintptr_t* arguments) +{ + const unsigned Exists = 1; + const unsigned Regular = 2; + const unsigned Directory = 4; + + MyClasspath* cp = static_cast(t->m->classpath); + + object file = reinterpret_cast(arguments[1]); + GcString* path + = cast(t, fieldAtOffset(file, cp->filePathField)); + + THREAD_RUNTIME_ARRAY(t, char, p, path->length(t) + 1); + stringChars(t, path, RUNTIME_ARRAY_BODY(p)); + replace('\\', '/', RUNTIME_ARRAY_BODY(p)); + + EmbeddedFile ef(cp, RUNTIME_ARRAY_BODY(p), path->length(t)); + if (ef.jar) { + if (ef.jarLength == 0) { + return Exists | Directory; + } + + Finder* finder = getFinder(t, ef.jar, ef.jarLength); + if (finder) { + if (ef.pathLength == 0) { + return Exists | Directory; + } + + size_t length; + System::FileType type = finder->stat(ef.path, &length, true); + switch (type) { + case System::TypeUnknown: + return Exists; + case System::TypeDoesNotExist: + return 0; + case System::TypeFile: + return Exists | Regular; + case System::TypeDirectory: + return Exists | Directory; + default: + abort(t); + } + } else { + return 0; + } + } else { + return cast(t, + t->m->processor->invoke( + t, + cast( + t, + cast( + t, getMethodRuntimeData(t, method)->native()) + ->original()), + reinterpret_cast(arguments[0]), + file))->value(); + } +} + +int64_t JNICALL + checkFileAccess(Thread* t, GcMethod* method, uintptr_t* arguments) +{ + const unsigned Read = 4; + + MyClasspath* cp = static_cast(t->m->classpath); + + object file = reinterpret_cast(arguments[1]); + unsigned mask = arguments[2]; + GcString* path + = cast(t, fieldAtOffset(file, cp->filePathField)); + + THREAD_RUNTIME_ARRAY(t, char, p, path->length(t) + 1); + stringChars(t, path, RUNTIME_ARRAY_BODY(p)); + replace('\\', '/', RUNTIME_ARRAY_BODY(p)); + + EmbeddedFile ef(cp, RUNTIME_ARRAY_BODY(p), path->length(t)); + if (ef.jar) { + if (ef.jarLength == 0) { + return mask == Read; + } + + Finder* finder = getFinder(t, ef.jar, ef.jarLength); + if (finder) { + if (ef.pathLength == 0) { + return mask == Read; + } + + size_t length; + System::FileType type = finder->stat(ef.path, &length, true); + switch (type) { + case System::TypeDoesNotExist: + return false; + case System::TypeUnknown: + case System::TypeFile: + case System::TypeDirectory: + return mask == Read; + default: + abort(t); + } + } else { + return 0; + } + } else { + return cast(t, + t->m->processor->invoke( + t, + cast( + t, + cast( + t, getMethodRuntimeData(t, method)->native()) + ->original()), + reinterpret_cast(arguments[0]), + file, + mask))->value() != 0; + } +} + +int64_t JNICALL getFileLength(Thread* t, GcMethod* method, uintptr_t* arguments) +{ + MyClasspath* cp = static_cast(t->m->classpath); + + object file = reinterpret_cast(arguments[1]); + GcString* path + = cast(t, fieldAtOffset(file, cp->filePathField)); + + THREAD_RUNTIME_ARRAY(t, char, p, path->length(t) + 1); + stringChars(t, path, RUNTIME_ARRAY_BODY(p)); + replace('\\', '/', RUNTIME_ARRAY_BODY(p)); + + EmbeddedFile ef(cp, RUNTIME_ARRAY_BODY(p), path->length(t)); + if (ef.jar) { + if (ef.jarLength == 0) { + return 0; + } + + Finder* finder = getFinder(t, ef.jar, ef.jarLength); + if (finder) { + if (ef.pathLength == 0) { + return 0; + } + + size_t fileLength; + finder->stat(ef.path, &fileLength); + return fileLength; + } + + return 0; + } else { + return cast(t, + t->m->processor->invoke( + t, + cast(t, + cast( + t, + getMethodRuntimeData(t, method) + ->native())->original()), + reinterpret_cast(arguments[0]), + file))->value(); + } +} + +void JNICALL openFile(Thread* t, GcMethod* method, uintptr_t* arguments) +{ + object this_ = reinterpret_cast(arguments[0]); + GcString* path = cast(t, reinterpret_cast(arguments[1])); + + MyClasspath* cp = static_cast(t->m->classpath); + + THREAD_RUNTIME_ARRAY(t, char, p, path->length(t) + 1); + stringChars(t, path, RUNTIME_ARRAY_BODY(p)); + replace('\\', '/', RUNTIME_ARRAY_BODY(p)); + + EmbeddedFile ef(cp, RUNTIME_ARRAY_BODY(p), path->length(t)); + if (ef.jar) { + if (ef.jarLength == 0 or ef.pathLength == 0) { + throwNew(t, GcFileNotFoundException::Type); + } + + Finder* finder = getFinder(t, ef.jar, ef.jarLength); + if (finder == 0) { + throwNew(t, GcFileNotFoundException::Type); + } + + System::Region* r = finder->find(ef.path); + if (r == 0) { + throwNew(t, GcFileNotFoundException::Type); + } + + PROTECT(t, this_); + + ACQUIRE(t, t->m->referenceLock); + + int index = -1; + unsigned oldLength + = roots(t)->virtualFiles() ? roots(t)->virtualFiles()->length() : 0; + + for (unsigned i = 0; i < oldLength; ++i) { + if (roots(t)->virtualFiles()->body()[i] == 0) { + index = i; + break; + } + } + + if (index == -1) { + GcArray* newArray = growArray(t, roots(t)->virtualFiles()); + roots(t)->setVirtualFiles(t, newArray); + index = oldLength; + } + + object region = makeRegion(t, r, 0); + roots(t)->virtualFiles()->setBodyElement(t, index, region); + + fieldAtOffset( + fieldAtOffset(this_, cp->fileInputStreamFdField), + cp->fileDescriptorFdField) = index + VirtualFileBase; + } else { + t->m->processor->invoke( + t, + cast( + t, + cast( + t, getMethodRuntimeData(t, method)->native())->original()), + this_, + path); + } +} + +int64_t JNICALL + readByteFromFile(Thread* t, GcMethod* method, uintptr_t* arguments) +{ + object this_ = reinterpret_cast(arguments[0]); + + MyClasspath* cp = static_cast(t->m->classpath); + + int fd = fieldAtOffset( + fieldAtOffset(this_, cp->fileInputStreamFdField), + cp->fileDescriptorFdField); + + if (fd >= VirtualFileBase) { + ACQUIRE(t, t->m->referenceLock); + + GcRegion* region = cast( + t, roots(t)->virtualFiles()->body()[fd - VirtualFileBase]); + + if (region) { + System::Region* r = static_cast(region->region()); + + if (r->length() > region->position()) { + return r->start()[region->position()++]; + } else { + return -1; + } + } else { + throwNew(t, GcIoException::Type); + } + } else { + return cast(t, + t->m->processor->invoke( + t, + cast( + t, + cast( + t, getMethodRuntimeData(t, method)->native()) + ->original()), + this_))->value(); + } +} + +int64_t JNICALL + readBytesFromFile(Thread* t, GcMethod* method, uintptr_t* arguments) +{ + object this_ = reinterpret_cast(arguments[0]); + GcByteArray* dst + = cast(t, reinterpret_cast(arguments[1])); + int32_t offset = arguments[2]; + int32_t length = arguments[3]; + + MyClasspath* cp = static_cast(t->m->classpath); + + int fd = fieldAtOffset( + fieldAtOffset(this_, cp->fileInputStreamFdField), + cp->fileDescriptorFdField); + + if (fd >= VirtualFileBase) { + PROTECT(t, dst); + + ACQUIRE(t, t->m->referenceLock); + + GcRegion* region = cast( + t, roots(t)->virtualFiles()->body()[fd - VirtualFileBase]); + + if (region) { + System::Region* r = static_cast(region->region()); + + int available = r->length() - region->position(); + if (available == 0) { + return -1; + } + + if (length > available) { + length = available; + } + + memcpy(&dst->body()[offset], r->start() + region->position(), length); + + region->position() += length; + + return length; + } else { + throwNew(t, GcIoException::Type); + } + } else { + return cast(t, + t->m->processor->invoke( + t, + cast( + t, + cast( + t, getMethodRuntimeData(t, method)->native()) + ->original()), + this_, + dst, + offset, + length))->value(); + } +} + +int64_t JNICALL + skipBytesInFile(Thread* t, GcMethod* method, uintptr_t* arguments) +{ + object this_ = reinterpret_cast(arguments[0]); + int64_t count; + memcpy(&count, arguments + 1, 8); + + MyClasspath* cp = static_cast(t->m->classpath); + + int fd = fieldAtOffset( + fieldAtOffset(this_, cp->fileInputStreamFdField), + cp->fileDescriptorFdField); + + if (fd >= VirtualFileBase) { + ACQUIRE(t, t->m->referenceLock); + + GcRegion* region = cast( + t, roots(t)->virtualFiles()->body()[fd - VirtualFileBase]); + + if (region) { + System::Region* r = static_cast(region->region()); + + int available = r->length() - region->position(); + if (count > available) { + count = available; + } + + region->position() += count; + + return count; + } else { + throwNew(t, GcIoException::Type); + } + } else { + return cast(t, + t->m->processor->invoke( + t, + cast(t, + cast( + t, + getMethodRuntimeData(t, method) + ->native())->original()), + this_, + count))->value(); + } +} + +int64_t JNICALL + availableBytesInFile(Thread* t, GcMethod* method, uintptr_t* arguments) +{ + object this_ = reinterpret_cast(arguments[0]); + + MyClasspath* cp = static_cast(t->m->classpath); + + int fd = fieldAtOffset( + fieldAtOffset(this_, cp->fileInputStreamFdField), + cp->fileDescriptorFdField); + + if (fd >= VirtualFileBase) { + ACQUIRE(t, t->m->referenceLock); + + GcRegion* region = cast( + t, roots(t)->virtualFiles()->body()[fd - VirtualFileBase]); + + if (region) { + return static_cast(region->region())->length() + - region->position(); + } else { + throwNew(t, GcIoException::Type); + } + } else { + object r = t->m->processor->invoke( + t, + cast( + t, + cast( + t, getMethodRuntimeData(t, method)->native())->original()), + this_); + + return r ? cast(t, r)->value() : 0; + } +} + +void JNICALL closeFile(Thread* t, GcMethod* method, uintptr_t* arguments) +{ + object this_ = reinterpret_cast(arguments[0]); + + MyClasspath* cp = static_cast(t->m->classpath); + + int fd = fieldAtOffset( + fieldAtOffset(this_, cp->fileInputStreamFdField), + cp->fileDescriptorFdField); + + if (fd >= VirtualFileBase) { + ACQUIRE(t, t->m->referenceLock); + + int index = fd - VirtualFileBase; + GcRegion* region + = cast(t, roots(t)->virtualFiles()->body()[index]); + + if (region) { + static_cast(region->region())->dispose(); + } + + roots(t)->virtualFiles()->setBodyElement(t, index, 0); + } else { + t->m->processor->invoke( + t, + cast( + t, + cast( + t, getMethodRuntimeData(t, method)->native())->original()), + this_); + } +} + +class ZipFile { + public: + class Entry { + public: + Entry(unsigned hash, const uint8_t* start, Entry* next) + : hash(hash), start(start), next(next), entry(0) + { + } + + Entry(int64_t entry) : hash(0), start(0), next(0), entry(entry) + { + } + + Entry() : hash(0), start(0), next(0), entry(0) + { + } + + unsigned hash; + const uint8_t* start; + Entry* next; + int64_t entry; + }; + + ZipFile(Thread* t, System::Region* region, unsigned entryCount) + : region(region), + entryCount(entryCount), + indexSize(nextPowerOfTwo(entryCount)), + index(reinterpret_cast( + t->m->heap->allocate(sizeof(ZipFile::Entry*) * indexSize))), + file(0) + { + memset(index, 0, sizeof(ZipFile::Entry*) * indexSize); + } + + ZipFile(int64_t file) + : region(0), entryCount(0), indexSize(0), index(0), file(file) + { + } + + System::Region* region; + unsigned entryCount; + unsigned indexSize; + Entry** index; + int64_t file; + Entry entries[0]; +}; + +int64_t JNICALL openZipFile(Thread* t, GcMethod* method, uintptr_t* arguments) +{ + GcString* path = cast(t, reinterpret_cast(arguments[0])); + int mode = arguments[1]; + int64_t lastModified; + memcpy(&lastModified, arguments + 2, 8); + + MyClasspath* cp = static_cast(t->m->classpath); + + THREAD_RUNTIME_ARRAY(t, char, p, path->length(t) + 1); + stringChars(t, path, RUNTIME_ARRAY_BODY(p)); + replace('\\', '/', RUNTIME_ARRAY_BODY(p)); + + EmbeddedFile ef(cp, RUNTIME_ARRAY_BODY(p), path->length(t)); + if (ef.jar) { + if (ef.jarLength == 0 or ef.pathLength == 0) { + throwNew(t, GcFileNotFoundException::Type); + } + + Finder* finder = getFinder(t, ef.jar, ef.jarLength); + if (finder == 0) { + throwNew(t, GcFileNotFoundException::Type); + } + + System::Region* r = finder->find(ef.path); + if (r == 0) { + throwNew(t, GcFileNotFoundException::Type); + } + + const uint8_t* start = r->start(); + const uint8_t* end = start + r->length(); + unsigned entryCount = 0; + for (const uint8_t* p = end - CentralDirectorySearchStart; p > start;) { + if (get4(p) == CentralDirectorySignature) { + p = start + centralDirectoryOffset(p); + + while (p < end) { + if (get4(p) == EntrySignature) { + ++entryCount; + + p = endOfEntry(p); + } else { + goto make; + } + } + } else { + --p; + } + } + + make: + ZipFile* file = new (t->m->heap->allocate( + sizeof(ZipFile) + (sizeof(ZipFile::Entry) * entryCount))) + ZipFile(t, r, entryCount); + + { + unsigned position = 0; + for (const uint8_t* p = end - CentralDirectorySearchStart; p > start;) { + if (get4(p) == CentralDirectorySignature) { + p = start + centralDirectoryOffset(p); + + while (p < end) { + if (get4(p) == EntrySignature) { + unsigned h + = hash(Slice(fileName(p), fileNameLength(p))); + unsigned i = h & (file->indexSize - 1); + + file->index[i] = new (file->entries + (position++)) + ZipFile::Entry(h, p, file->index[i]); + + p = endOfEntry(p); + } else { + goto exit; + } + } + } else { + --p; + } + } + } + + exit: + return reinterpret_cast(file); + } else { + return reinterpret_cast( + new (t->m->heap->allocate(sizeof(ZipFile))) ZipFile( + cast(t, + t->m->processor->invoke( + t, + cast(t, + cast( + t, + getMethodRuntimeData(t, method) + ->native())->original()), + 0, + path, + mode, + lastModified))->value())); + } +} + +int64_t JNICALL + getZipFileEntryCount(Thread* t, GcMethod* method, uintptr_t* arguments) +{ + int64_t peer; + memcpy(&peer, arguments, 8); + + ZipFile* file = reinterpret_cast(peer); + if (file->region) { + return file->entryCount; + } else { + return cast(t, + t->m->processor->invoke( + t, + cast( + t, + cast( + t, getMethodRuntimeData(t, method)->native()) + ->original()), + 0, + file->file))->value(); + } +} + +ZipFile::Entry* find(ZipFile* file, const char* path, unsigned pathLength) +{ + if (pathLength > 0 && path[0] == '/') { + ++path; + --pathLength; + } + unsigned i = hash(path) & (file->indexSize - 1); + for (ZipFile::Entry* e = file->index[i]; e; e = e->next) { + const uint8_t* p = e->start; + if (equal(path, pathLength, fileName(p), fileNameLength(p))) { + return e; + } + } + return 0; +} + +int64_t JNICALL + getZipFileEntry(Thread* t, GcMethod* method, uintptr_t* arguments) +{ + int64_t peer; + memcpy(&peer, arguments, 8); + GcByteArray* path + = cast(t, reinterpret_cast(arguments[2])); + bool addSlash = arguments[3]; + + ZipFile* file = reinterpret_cast(peer); + if (file->region) { + THREAD_RUNTIME_ARRAY(t, char, p, path->length() + 2); + memcpy(RUNTIME_ARRAY_BODY(p), path->body().begin(), path->length()); + RUNTIME_ARRAY_BODY(p)[path->length()] = 0; + replace('\\', '/', RUNTIME_ARRAY_BODY(p)); + + ZipFile::Entry *e = find(file, RUNTIME_ARRAY_BODY(p), path->length()); + + if (e == 0 and addSlash and RUNTIME_ARRAY_BODY(p)[path->length()] != '/') { + RUNTIME_ARRAY_BODY(p)[path->length()] = '/'; + RUNTIME_ARRAY_BODY(p)[path->length() + 1] = 0; + + e = find(file, RUNTIME_ARRAY_BODY(p), path->length()); + } + + return reinterpret_cast(e); + } else { + int64_t entry + = cast(t, + t->m->processor->invoke( + t, + cast( + t, + cast( + t, getMethodRuntimeData(t, method)->native()) + ->original()), + 0, + file->file, + path, + addSlash))->value(); + + return entry ? reinterpret_cast(new (t->m->heap->allocate( + sizeof(ZipFile::Entry))) ZipFile::Entry(entry)) + : 0; + } +} + +int64_t JNICALL + getZipFileEntryBytes(Thread* t, GcMethod* method, uintptr_t* arguments) +{ + int64_t peer; + memcpy(&peer, arguments, 8); + int type = arguments[2]; + + ZipFile::Entry* entry = reinterpret_cast(peer); + if (entry->start) { + switch (type) { + case 0: { // name + unsigned nameLength = fileNameLength(entry->start); + GcByteArray* array = makeByteArray(t, nameLength); + memcpy(array->body().begin(), fileName(entry->start), nameLength); + return reinterpret_cast(array); + } break; + + case 1: { // extra + return 0; + } break; + + case 2: { // comment + return 0; + } break; + + default: + abort(t); + } + return compressedSize(entry->start); + } else { + return reinterpret_cast(t->m->processor->invoke( + t, + cast( + t, + cast( + t, getMethodRuntimeData(t, method)->native())->original()), + 0, + entry->entry, + type)); + } +} + +int64_t JNICALL + getNextZipFileEntry(Thread* t, GcMethod* method, uintptr_t* arguments) +{ + int64_t peer; + memcpy(&peer, arguments, 8); + int index = arguments[2]; + + ZipFile* file = reinterpret_cast(peer); + if (file->region) { + return reinterpret_cast(file->entries + index); + } else { + int64_t entry + = cast(t, + t->m->processor->invoke( + t, + cast( + t, + cast( + t, getMethodRuntimeData(t, method)->native()) + ->original()), + 0, + file->file, + index))->value(); + + return entry ? reinterpret_cast(new (t->m->heap->allocate( + sizeof(ZipFile::Entry))) ZipFile::Entry(entry)) + : 0; + } +} + +int64_t JNICALL + getZipFileEntryMethod(Thread* t, GcMethod* method, uintptr_t* arguments) +{ + int64_t peer; + memcpy(&peer, arguments, 8); + + ZipFile::Entry* entry = reinterpret_cast(peer); + if (entry->start) { + return compressionMethod(entry->start); + } else { + return cast(t, + t->m->processor->invoke( + t, + cast( + t, + cast( + t, getMethodRuntimeData(t, method)->native()) + ->original()), + 0, + entry->entry))->value(); + } +} + +int64_t JNICALL getZipFileEntryCompressedSize(Thread* t, + GcMethod* method, + uintptr_t* arguments) +{ + int64_t peer; + memcpy(&peer, arguments, 8); + + ZipFile::Entry* entry = reinterpret_cast(peer); + if (entry->start) { + return compressedSize(entry->start); + } else { + return cast(t, + t->m->processor->invoke( + t, + cast(t, + cast( + t, + getMethodRuntimeData(t, method) + ->native())->original()), + 0, + entry->entry))->value(); + } +} + +int64_t JNICALL getZipFileEntryUncompressedSize(Thread* t, + GcMethod* method, + uintptr_t* arguments) +{ + int64_t peer; + memcpy(&peer, arguments, 8); + + ZipFile::Entry* entry = reinterpret_cast(peer); + if (entry->start) { + return uncompressedSize(entry->start); + } else { + return cast(t, + t->m->processor->invoke( + t, + cast(t, + cast( + t, + getMethodRuntimeData(t, method) + ->native())->original()), + 0, + entry->entry))->value(); + } +} + +void JNICALL freeZipFileEntry(Thread* t, GcMethod* method, uintptr_t* arguments) +{ + int64_t filePeer; + memcpy(&filePeer, arguments, 8); + int64_t entryPeer; + memcpy(&entryPeer, arguments + 2, 8); + + ZipFile* file = reinterpret_cast(filePeer); + ZipFile::Entry* entry = reinterpret_cast(entryPeer); + if (file->region == 0) { + t->m->processor->invoke( + t, + cast( + t, + cast( + t, getMethodRuntimeData(t, method)->native())->original()), + 0, + file->file, + entry->entry); + + t->m->heap->free(entry, sizeof(ZipFile::Entry)); + } +} + +int64_t JNICALL + readZipFileEntry(Thread* t, GcMethod* method, uintptr_t* arguments) +{ + int64_t filePeer; + memcpy(&filePeer, arguments, 8); + int64_t entryPeer; + memcpy(&entryPeer, arguments + 2, 8); + int64_t position; + memcpy(&position, arguments + 4, 8); + GcByteArray* buffer + = cast(t, reinterpret_cast(arguments[6])); + int offset = arguments[7]; + int length = arguments[8]; + + ZipFile* file = reinterpret_cast(filePeer); + ZipFile::Entry* entry = reinterpret_cast(entryPeer); + if (file->region) { + unsigned size = uncompressedSize(entry->start); + if (position >= size) { + return -1; + } + + if (position + length > size) { + length = size - position; + } + + memcpy(&buffer->body()[offset], + fileData(file->region->start() + localHeaderOffset(entry->start)) + + position, + length); + + return length; + } else { + return cast(t, + t->m->processor->invoke( + t, + cast( + t, + cast( + t, getMethodRuntimeData(t, method)->native()) + ->original()), + 0, + file->file, + entry->entry, + position, + buffer, + offset, + length))->value(); + } +} + +int64_t JNICALL getZipMessage(Thread* t, GcMethod* method, uintptr_t* arguments) +{ + int64_t peer; + memcpy(&peer, arguments, 8); + + ZipFile* file = reinterpret_cast(peer); + if (file->region) { + return 0; + } else { + return reinterpret_cast(t->m->processor->invoke( + t, + cast( + t, + cast( + t, getMethodRuntimeData(t, method)->native())->original()), + 0, + file->file)); + } +} + +int64_t JNICALL getJarFileMetaInfEntryNames(Thread* t, + GcMethod* method, + uintptr_t* arguments) +{ + object this_ = reinterpret_cast(arguments[0]); + + MyClasspath* cp = static_cast(t->m->classpath); + + int64_t peer = fieldAtOffset(this_, cp->zipFileJzfileField); + ZipFile* file = reinterpret_cast(peer); + if (file->region) { + return 0; + } else { + PROTECT(t, method); + + // OpenJDK's Java_java_util_jar_JarFile_getMetaInfEntryNames + // implementation expects to find a pointer to an instance of its + // jzfile structure in the ZipFile.jzfile field of the object we + // pass in. However, we can't pass this_ in, because its + // ZipFile.jzfile field points to a ZipFile instance, not a + // jzfile. So we pass in a temporary object instead which has the + // desired pointer at the same offset. We assume here that + // ZipFile.jzfile is the first field in that class and that + // Java_java_util_jar_JarFile_getMetaInfEntryNames will not look + // for any other fields in the object. + object pseudoThis = makeLong(t, file->file); + + return reinterpret_cast(t->m->processor->invoke( + t, + cast( + t, + cast( + t, getMethodRuntimeData(t, method)->native())->original()), + pseudoThis)); + } +} + +void JNICALL closeZipFile(Thread* t, GcMethod* method, uintptr_t* arguments) +{ + int64_t peer; + memcpy(&peer, arguments, 8); + + ZipFile* file = reinterpret_cast(peer); + if (file->region) { + file->region->dispose(); + t->m->heap->free( + file, sizeof(ZipFile) + (sizeof(ZipFile::Entry) * file->entryCount)); + } else { + t->m->processor->invoke( + t, + cast( + t, + cast( + t, getMethodRuntimeData(t, method)->native())->original()), + 0, + file->file); + + t->m->heap->free(file, sizeof(ZipFile)); + } +} + +int64_t JNICALL getBootstrapResource(Thread* t, object, uintptr_t* arguments) +{ + object name = reinterpret_cast(arguments[0]); + PROTECT(t, name); + + GcMethod* m = findMethodOrNull(t, + type(t, GcSystemClassLoader::Type), + "findResource", + "(Ljava/lang/String;)Ljava/net/URL;"); + + if (m) { + return reinterpret_cast( + t->m->processor->invoke(t, m, roots(t)->bootLoader(), name)); + } else { + return 0; + } +} + +int64_t JNICALL getBootstrapResources(Thread* t, object, uintptr_t* arguments) +{ + object name = reinterpret_cast(arguments[0]); + PROTECT(t, name); + + GcMethod* m = findMethodOrNull(t, + type(t, GcSystemClassLoader::Type), + "findResources", + "(Ljava/lang/String;)Ljava/util/Enumeration;"); + + if (m) { + return reinterpret_cast( + t->m->processor->invoke(t, m, roots(t)->bootLoader(), name)); + } else { + return 0; + } +} + +extern "C" AVIAN_EXPORT jint JNICALL net_JNI_OnLoad(JavaVM*, void*); + +extern "C" AVIAN_EXPORT jint JNICALL management_JNI_OnLoad(JavaVM*, void*); + +void JNICALL loadLibrary(Thread* t, object, uintptr_t* arguments) +{ + Thread::LibraryLoadStack stack( + t, + cast(t, reinterpret_cast(arguments[0])) + ->vmClass() + ->loader()); + + GcString* name = cast(t, reinterpret_cast(arguments[1])); + THREAD_RUNTIME_ARRAY(t, char, n, name->length(t) + 1); + stringChars(t, name, RUNTIME_ARRAY_BODY(n)); + + bool absolute = arguments[2]; + + if (not absolute) { + if (strcmp(RUNTIME_ARRAY_BODY(n), "net") == 0) { + bool ran; + + { + ACQUIRE(t, t->m->classLock); + + local::MyClasspath* c + = static_cast(t->m->classpath); + + ran = c->ranNetOnLoad; + c->ranNetOnLoad = true; + } + + if (not ran) { + net_JNI_OnLoad(t->m, 0); + } + + return; + } else if (strcmp(RUNTIME_ARRAY_BODY(n), "management") == 0) { + bool ran; + + { + ACQUIRE(t, t->m->classLock); + + local::MyClasspath* c + = static_cast(t->m->classpath); + + ran = c->ranManagementOnLoad; + c->ranManagementOnLoad = true; + } + + if (not ran) { + management_JNI_OnLoad(t->m, 0); + } + + return; + } else if (strcmp(RUNTIME_ARRAY_BODY(n), "zip") == 0 + or strcmp(RUNTIME_ARRAY_BODY(n), "nio") == 0) { + return; + } + } + + loadLibrary(t, + static_cast(t->m->classpath)->libraryPath, + RUNTIME_ARRAY_BODY(n), + not absolute, + true); +} + +void interceptFileOperations(Thread* t, bool updateRuntimeData) +{ + MyClasspath* cp = static_cast(t->m->classpath); + + { + GcClass* fileClass + = resolveClass(t, roots(t)->bootLoader(), "java/io/File", false); + + if (fileClass) { + GcField* filePathField + = findFieldInClass2(t, fileClass, "path", "Ljava/lang/String;"); + + if (filePathField) { + cp->filePathField = filePathField->offset(); + } + } + } + + { + GcClass* fileDescriptorClass = resolveClass( + t, roots(t)->bootLoader(), "java/io/FileDescriptor", false); + + if (fileDescriptorClass) { + GcField* fileDescriptorFdField + = findFieldInClass2(t, fileDescriptorClass, "fd", "I"); + + if (fileDescriptorFdField) { + cp->fileDescriptorFdField = fileDescriptorFdField->offset(); + } + } + } + + { + GcClass* fileInputStreamClass = resolveClass( + t, roots(t)->bootLoader(), "java/io/FileInputStream", false); + + if (fileInputStreamClass) { + PROTECT(t, fileInputStreamClass); + + GcField* fileInputStreamFdField = findFieldInClass2( + t, fileInputStreamClass, "fd", "Ljava/io/FileDescriptor;"); + + if (fileInputStreamFdField) { + cp->fileInputStreamFdField = fileInputStreamFdField->offset(); + + if (findMethodOrNull(t, fileInputStreamClass, "open0", "(Ljava/lang/String;)V") != 0) { + intercept(t, + fileInputStreamClass, + "open0", + "(Ljava/lang/String;)V", + voidPointer(openFile), + updateRuntimeData); + } else { + intercept(t, + fileInputStreamClass, + "open", + "(Ljava/lang/String;)V", + voidPointer(openFile), + updateRuntimeData); + } + + if (findMethodOrNull(t, fileInputStreamClass, "read0", "()I") != 0) { + intercept(t, + fileInputStreamClass, + "read0", + "()I", + voidPointer(readByteFromFile), + updateRuntimeData); + } else { + intercept(t, + fileInputStreamClass, + "read", + "()I", + voidPointer(readByteFromFile), + updateRuntimeData); + } + + intercept(t, + fileInputStreamClass, + "readBytes", + "([BII)I", + voidPointer(readBytesFromFile), + updateRuntimeData); + + intercept(t, + fileInputStreamClass, + "skip", + "(J)J", + voidPointer(skipBytesInFile), + updateRuntimeData); + + intercept(t, + fileInputStreamClass, + "available", + "()I", + voidPointer(availableBytesInFile), + updateRuntimeData); + + intercept(t, + fileInputStreamClass, + "close0", + "()V", + voidPointer(closeFile), + updateRuntimeData); + } + } + } + + { + GcClass* zipFileClass = resolveClass( + t, roots(t)->bootLoader(), "java/util/zip/ZipFile", false); + + if (zipFileClass) { + PROTECT(t, zipFileClass); + + GcField* zipFileJzfileField + = findFieldInClass2(t, zipFileClass, "jzfile", "J"); + + if (zipFileJzfileField) { + cp->zipFileJzfileField = zipFileJzfileField->offset(); + + intercept(t, + zipFileClass, + "open", + "(Ljava/lang/String;IJZ)J", + voidPointer(openZipFile), + updateRuntimeData); + + intercept(t, + zipFileClass, + "getTotal", + "(J)I", + voidPointer(getZipFileEntryCount), + updateRuntimeData); + + intercept(t, + zipFileClass, + "getEntry", + "(J[BZ)J", + voidPointer(getZipFileEntry), + updateRuntimeData); + + intercept(t, + zipFileClass, + "getEntryBytes", + "(JI)[B", + voidPointer(getZipFileEntryBytes), + updateRuntimeData); + + intercept(t, + zipFileClass, + "getNextEntry", + "(JI)J", + voidPointer(getNextZipFileEntry), + updateRuntimeData); + + intercept(t, + zipFileClass, + "getEntryMethod", + "(J)I", + voidPointer(getZipFileEntryMethod), + updateRuntimeData); + + intercept(t, + zipFileClass, + "freeEntry", + "(JJ)V", + voidPointer(freeZipFileEntry), + updateRuntimeData); + + intercept(t, + zipFileClass, + "read", + "(JJJ[BII)I", + voidPointer(readZipFileEntry), + updateRuntimeData); + + intercept(t, + zipFileClass, + "getEntryCSize", + "(J)J", + voidPointer(getZipFileEntryCompressedSize), + updateRuntimeData); + + intercept(t, + zipFileClass, + "getEntrySize", + "(J)J", + voidPointer(getZipFileEntryUncompressedSize), + updateRuntimeData); + + intercept(t, + zipFileClass, + "getZipMessage", + "(J)Ljava/lang/String;", + voidPointer(getZipMessage), + updateRuntimeData); + + intercept(t, + zipFileClass, + "close", + "(J)V", + voidPointer(closeZipFile), + updateRuntimeData); + } + } + } + + { + GcClass* jarFileClass = resolveClass( + t, roots(t)->bootLoader(), "java/util/jar/JarFile", false); + + if (jarFileClass) { + intercept(t, + jarFileClass, + "getMetaInfEntryNames", + "()[Ljava/lang/String;", + voidPointer(getJarFileMetaInfEntryNames), + updateRuntimeData); + } + } + + { +#ifdef PLATFORM_WINDOWS + const char* const fsClassName = "java/io/WinNTFileSystem"; + const char* const gbaMethodName = "getBooleanAttributes"; +#else + const char* const fsClassName = "java/io/UnixFileSystem"; + const char* const gbaMethodName = "getBooleanAttributes0"; +#endif + + GcClass* fsClass + = resolveClass(t, roots(t)->bootLoader(), fsClassName, false); + + if (fsClass) { + PROTECT(t, fsClass); + + intercept(t, + fsClass, + gbaMethodName, + "(Ljava/io/File;)I", + voidPointer(getFileAttributes), + updateRuntimeData); + + intercept(t, + fsClass, + "checkAccess", + "(Ljava/io/File;I)Z", + voidPointer(checkFileAccess), + updateRuntimeData); + + intercept(t, + fsClass, + "getLength", + "(Ljava/io/File;)J", + voidPointer(getFileLength), + updateRuntimeData); + } + } + + intercept(t, + type(t, GcClassLoader::Type), + "loadLibrary", + "(Ljava/lang/Class;Ljava/lang/String;Z)V", + voidPointer(loadLibrary), + updateRuntimeData); + + intercept(t, + type(t, GcClassLoader::Type), + "getBootstrapResource", + "(Ljava/lang/String;)Ljava/net/URL;", + voidPointer(getBootstrapResource), + updateRuntimeData); + + intercept(t, + type(t, GcClassLoader::Type), + "getBootstrapResources", + "(Ljava/lang/String;)Ljava/util/Enumeration;", + voidPointer(getBootstrapResources), + updateRuntimeData); +} +#endif // AVIAN_OPENJDK_SRC + +unsigned classDeclaredMethodCount(Thread* t, GcClass* c) +{ + GcClassAddendum* addendum = c->addendum(); + if (addendum) { + int count = addendum->declaredMethodCount(); + if (count >= 0) { + return count; + } + } + GcArray* table = cast(t, c->methodTable()); + return table == 0 ? 0 : table->length(); +} + +unsigned countMethods(Thread* t, GcClass* c, bool publicOnly) +{ + GcArray* table = cast(t, c->methodTable()); + unsigned count = 0; + for (unsigned i = 0, j = classDeclaredMethodCount(t, c); i < j; ++i) { + GcMethod* vmMethod = cast(t, table->body()[i]); + if (((not publicOnly) or (vmMethod->flags() & ACC_PUBLIC)) + and vmMethod->name()->body()[0] != '<') { + ++count; + } + } + return count; +} + +unsigned countFields(Thread* t, GcClass* c, bool publicOnly) +{ + GcArray* table = cast(t, c->fieldTable()); + if (publicOnly) { + unsigned count = 0; + for (unsigned i = 0; i < table->length(); ++i) { + GcField* vmField = cast(t, table->body()[i]); + if (vmField->flags() & ACC_PUBLIC) { + ++count; + } + } + return count; + } else { + return objectArrayLength(t, table); + } +} + +unsigned countConstructors(Thread* t, GcClass* c, bool publicOnly) +{ + GcArray* table = cast(t, c->methodTable()); + unsigned count = 0; + for (unsigned i = 0, j = classDeclaredMethodCount(t, c); i < j; ++i) { + GcMethod* vmMethod = cast(t, table->body()[i]); + if (((not publicOnly) or (vmMethod->flags() & ACC_PUBLIC)) + and strcmp(reinterpret_cast(vmMethod->name()->body().begin()), + "") == 0) { + ++count; + } + } + return count; +} + +#ifdef HAVE_JexecutableHasRealParameterData +object makeJmethod(Thread* t, + uint8_t override, + object securityCheckCache, + object clazz, + uint32_t slot, + object name, + object returnType, + object parameterTypes, + object exceptionTypes, + uint32_t modifiers, + object signature, + object genericInfo, + object annotations, + object parameterAnnotations, + object annotationDefault, + object methodAccessor, + object root, + object declaredAnnotations) +{ + return makeJmethod(t, + override, + securityCheckCache, + 0, + 0, + declaredAnnotations, + cast(t, clazz), + slot, + cast(t, name), + cast(t, returnType), + parameterTypes, + exceptionTypes, + modifiers, + cast(t, signature), + genericInfo, + cast(t, annotations), + cast(t, parameterAnnotations), + cast(t, annotationDefault), + methodAccessor, + cast(t, root)); +} + +object makeJconstructor(Thread* t, + uint8_t override, + object securityCheckCache, + object clazz, + uint32_t slot, + object parameterTypes, + object exceptionTypes, + uint32_t modifiers, + object signature, + object genericInfo, + object annotations, + object parameterAnnotations, + object constructorAccessor, + object root, + object declaredAnnotations) +{ + return makeJconstructor(t, + override, + securityCheckCache, + 0, + 0, + declaredAnnotations, + cast(t, clazz), + slot, + parameterTypes, + exceptionTypes, + modifiers, + cast(t, signature), + genericInfo, + cast(t, annotations), + cast(t, parameterAnnotations), + constructorAccessor, + cast(t, root)); +} +#endif // HAVE_JexecutableHasRealParameterData + +object makeJmethod(Thread* t, GcMethod* vmMethod, int index) +{ + PROTECT(t, vmMethod); + + object name + = intern(t, + t->m->classpath->makeString( + t, vmMethod->name(), 0, vmMethod->name()->length() - 1)); + PROTECT(t, name); + + unsigned parameterCount; + unsigned returnTypeSpec; + object parameterTypes = resolveParameterJTypes(t, + vmMethod->class_()->loader(), + vmMethod->spec(), + ¶meterCount, + &returnTypeSpec); + PROTECT(t, parameterTypes); + + GcJclass* returnType = resolveJType( + t, + vmMethod->class_()->loader(), + reinterpret_cast(&vmMethod->spec()->body()[returnTypeSpec]), + vmMethod->spec()->length() - 1 - returnTypeSpec); + PROTECT(t, returnType); + + object exceptionTypes = resolveExceptionJTypes( + t, vmMethod->class_()->loader(), vmMethod->addendum()); + PROTECT(t, exceptionTypes); + + object signature; + object annotationTable; + object parameterAnnotationTable; + object annotationDefault; + GcMethodAddendum* addendum = vmMethod->addendum(); + if (addendum) { + signature = addendum->signature(); + if (signature) { + PROTECT(t, addendum); + + signature = t->m->classpath->makeString( + t, signature, 0, cast(t, signature)->length() - 1); + } + + annotationTable = addendum->annotationTable(); + + parameterAnnotationTable = addendum->parameterAnnotationTable(); + + annotationDefault = addendum->annotationDefault(); + } else { + signature = 0; + annotationTable = 0; + parameterAnnotationTable = 0; + annotationDefault = 0; + } + + PROTECT(t, signature); + PROTECT(t, annotationTable); + PROTECT(t, parameterAnnotationTable); + PROTECT(t, annotationDefault); + + if (annotationTable or parameterAnnotationTable or annotationDefault) { + GcClassRuntimeData* runtimeData + = getClassRuntimeData(t, vmMethod->class_()); + + runtimeData->setPool(t, vmMethod->addendum()->pool()); + } + + if (index == -1) { + GcArray* table = cast(t, vmMethod->class_()->methodTable()); + for (unsigned i = 0; i < table->length(); ++i) { + if (vmMethod == table->body()[i]) { + index = i; + break; + } + } + } + + expect(t, index != -1); + + GcJclass* jclass = getJClass(t, vmMethod->class_()); + + return makeJmethod(t, + true, + 0, + jclass, + index, + cast(t, name), + returnType, + parameterTypes, + exceptionTypes, + vmMethod->flags(), + cast(t, signature), + 0, + cast(t, annotationTable), + cast(t, parameterAnnotationTable), + cast(t, annotationDefault), + 0, + 0, + 0); +} + +object makeJconstructor(Thread* t, GcMethod* vmMethod, int index) +{ + PROTECT(t, vmMethod); + + unsigned parameterCount; + unsigned returnTypeSpec; + object parameterTypes = resolveParameterJTypes(t, + vmMethod->class_()->loader(), + vmMethod->spec(), + ¶meterCount, + &returnTypeSpec); + PROTECT(t, parameterTypes); + + object exceptionTypes = resolveExceptionJTypes( + t, vmMethod->class_()->loader(), vmMethod->addendum()); + PROTECT(t, exceptionTypes); + + object signature; + object annotationTable; + object parameterAnnotationTable; + GcMethodAddendum* addendum = vmMethod->addendum(); + if (addendum) { + signature = addendum->signature(); + if (signature) { + PROTECT(t, addendum); + + signature = t->m->classpath->makeString( + t, signature, 0, cast(t, signature)->length() - 1); + } + + annotationTable = addendum->annotationTable(); + parameterAnnotationTable = addendum->parameterAnnotationTable(); + } else { + signature = 0; + annotationTable = 0; + parameterAnnotationTable = 0; + } + + PROTECT(t, signature); + PROTECT(t, annotationTable); + PROTECT(t, parameterAnnotationTable); + + if (annotationTable or parameterAnnotationTable) { + GcClassRuntimeData* runtimeData + = getClassRuntimeData(t, vmMethod->class_()); + + runtimeData->setPool(t, vmMethod->addendum()->pool()); + } + + if (index == -1) { + GcArray* table = cast(t, vmMethod->class_()->methodTable()); + for (unsigned i = 0; i < table->length(); ++i) { + if (vmMethod == table->body()[i]) { + index = i; + break; + } + } + } + + expect(t, index != -1); + + GcJclass* jclass = getJClass(t, vmMethod->class_()); + + return makeJconstructor(t, + true, + 0, + jclass, + index, + parameterTypes, + exceptionTypes, + vmMethod->flags(), + cast(t, signature), + 0, + cast(t, annotationTable), + cast(t, parameterAnnotationTable), + 0, + 0, + 0); +} + +object makeJfield(Thread* t, GcField* vmField, int index) +{ + PROTECT(t, vmField); + + object name + = intern(t, + t->m->classpath->makeString( + t, vmField->name(), 0, vmField->name()->length() - 1)); + PROTECT(t, name); + + GcClass* type = resolveClassBySpec( + t, + vmField->class_()->loader(), + reinterpret_cast(vmField->spec()->body().begin()), + vmField->spec()->length() - 1); + PROTECT(t, type); + + GcJclass* jtype = getJClass(t, type); + + object signature; + object annotationTable; + GcFieldAddendum* addendum = vmField->addendum(); + if (addendum) { + signature = addendum->signature(); + if (signature) { + PROTECT(t, addendum); + + signature = t->m->classpath->makeString( + t, signature, 0, cast(t, signature)->length() - 1); + } + + annotationTable = addendum->annotationTable(); + } else { + signature = 0; + annotationTable = 0; + } + + PROTECT(t, signature); + PROTECT(t, annotationTable); + + if (annotationTable) { + GcClassRuntimeData* runtimeData = getClassRuntimeData(t, vmField->class_()); + + runtimeData->setPool(t, vmField->addendum()->pool()); + } + + if (index == -1) { + GcArray* table = cast(t, vmField->class_()->fieldTable()); + for (unsigned i = 0; i < table->length(); ++i) { + if (vmField == table->body()[i]) { + index = i; + break; + } + } + } + + expect(t, index != -1); + + GcJclass* jclass = getJClass(t, vmField->class_()); + + return makeJfield(t, + true, + 0, + jclass, + index, + cast(t, name), + jtype, + vmField->flags(), + cast(t, signature), + 0, + cast(t, annotationTable), + 0, + 0, + 0, + 0); +} + +void setProperty(Thread* t, + GcMethod* method, + object properties, + const char* name, + const void* value, + const char* format = "%s") +{ + PROTECT(t, method); + PROTECT(t, properties); + + GcString* n = makeString(t, "%s", name); + PROTECT(t, n); + + GcString* v = makeString(t, format, value); + + t->m->processor->invoke(t, method, properties, n, v); +} + +bool pipeAvailable(int fd, int* available) +{ +#ifdef PLATFORM_WINDOWS + HANDLE h = reinterpret_cast(_get_osfhandle(fd)); + if (h == INVALID_HANDLE_VALUE) { + return false; + } + + DWORD n; + if (PeekNamedPipe(h, 0, 0, 0, &n, 0)) { + *available = n; + } else { + if (GetLastError() != ERROR_BROKEN_PIPE) { + return false; + } + *available = 0; + } + + return true; +#else + return ioctl(fd, FIONREAD, available) >= 0; +#endif +} + +} // namespace local + +} // namespace + +namespace vm { + +Classpath* makeClasspath(System* s, + Allocator* allocator, + const char* javaHome, + const char* embedPrefix) +{ + return new (allocator->allocate(sizeof(local::MyClasspath))) + local::MyClasspath(s, allocator, javaHome, embedPrefix); +} + +} // namespace vm + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Class_getSuperclass(Thread* t, object, uintptr_t* arguments) +{ + GcClass* class_ + = cast(t, reinterpret_cast(arguments[0]))->vmClass(); + if (class_->flags() & ACC_INTERFACE) { + return 0; + } else { + GcClass* super = class_->super(); + return super ? reinterpret_cast(getJClass(t, super)) : 0; + } +} + +extern "C" AVIAN_EXPORT void Avian_sun_misc_Unsafe_registerNatives(Thread*, + object, + uintptr_t*) +{ + // ignore +} + +extern "C" AVIAN_EXPORT void Avian_sun_misc_Perf_registerNatives(Thread*, + object, + uintptr_t*) +{ + // ignore +} + +extern "C" AVIAN_EXPORT int64_t + Avian_sun_misc_Perf_createLong(Thread* t, object, uintptr_t*) +{ + return reinterpret_cast( + t->m->processor->invoke(t, + resolveMethod(t, + roots(t)->bootLoader(), + "java/nio/ByteBuffer", + "allocate", + "(I)Ljava/nio/ByteBuffer;"), + 0, + 8)); +} + +extern "C" AVIAN_EXPORT int64_t + Avian_sun_misc_Unsafe_addressSize(Thread*, object, uintptr_t*) +{ + return BytesPerWord; +} + +extern "C" AVIAN_EXPORT int64_t + Avian_sun_misc_Unsafe_defineClass__Ljava_lang_String_2_3BIILjava_lang_ClassLoader_2Ljava_security_ProtectionDomain_2( + Thread* t, + object, + uintptr_t* arguments) +{ + // object name = reinterpret_cast(arguments[1]); + GcByteArray* data + = cast(t, reinterpret_cast(arguments[2])); + int32_t offset = arguments[3]; + int32_t length = arguments[4]; + GcClassLoader* loader + = cast(t, reinterpret_cast(arguments[5])); + // object domain = reinterpret_cast(arguments[6]); + + uint8_t* buffer = static_cast(t->m->heap->allocate(length)); + + THREAD_RESOURCE2( + t, uint8_t*, buffer, int, length, t->m->heap->free(buffer, length)); + + memcpy(buffer, &data->body()[offset], length); + + return reinterpret_cast( + getJClass(t, cast(t, defineClass(t, loader, buffer, length)))); +} + +extern "C" AVIAN_EXPORT int64_t + Avian_sun_misc_Unsafe_allocateInstance(Thread* t, + object, + uintptr_t* arguments) +{ + GcClass* c + = cast(t, reinterpret_cast(arguments[1]))->vmClass(); + PROTECT(t, c); + + initClass(t, c); + + return reinterpret_cast(make(t, c)); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_staticFieldOffset(Thread* t, + object, + uintptr_t* arguments) +{ + GcJfield* jfield = cast(t, reinterpret_cast(arguments[1])); + return cast( + t, + cast(t, jfield->clazz()->vmClass()->fieldTable()) + ->body()[jfield->slot()])->offset(); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_staticFieldBase(Thread* t, + object, + uintptr_t* arguments) +{ + return reinterpret_cast( + cast(t, reinterpret_cast(arguments[1])) + ->clazz() + ->vmClass() + ->staticTable()); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_objectFieldOffset(Thread* t, + object, + uintptr_t* arguments) +{ + GcJfield* jfield = cast(t, reinterpret_cast(arguments[1])); + return cast( + t, + cast(t, jfield->clazz()->vmClass()->fieldTable()) + ->body()[jfield->slot()])->offset(); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getShort__Ljava_lang_Object_2J(Thread*, + object, + uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + + return fieldAtOffset(o, offset); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getChar__Ljava_lang_Object_2J(Thread*, + object, + uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + + return fieldAtOffset(o, offset); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getInt__Ljava_lang_Object_2J(Thread*, + object, + uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + + return fieldAtOffset(o, offset); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getFloat__Ljava_lang_Object_2J(Thread*, + object, + uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + + return fieldAtOffset(o, offset); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getLong__Ljava_lang_Object_2J(Thread*, + object, + uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + + return fieldAtOffset(o, offset); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getDouble__Ljava_lang_Object_2J(Thread* t, + GcMethod* method, + uintptr_t* arguments) +{ + return Avian_sun_misc_Unsafe_getLong__Ljava_lang_Object_2J( + t, method, arguments); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putByte__Ljava_lang_Object_2JB(Thread*, + object, + uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + int8_t value = arguments[4]; + + fieldAtOffset(o, offset) = value; +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putShort__Ljava_lang_Object_2JS(Thread*, + object, + uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + int16_t value = arguments[4]; + + fieldAtOffset(o, offset) = value; +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putChar__Ljava_lang_Object_2JC(Thread*, + object, + uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + uint16_t value = arguments[4]; + + fieldAtOffset(o, offset) = value; +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putInt__Ljava_lang_Object_2JI(Thread*, + object, + uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + int32_t value = arguments[4]; + + fieldAtOffset(o, offset) = value; +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putFloat__Ljava_lang_Object_2JF(Thread*, + object, + uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + int32_t value = arguments[4]; + + fieldAtOffset(o, offset) = value; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getByte__Ljava_lang_Object_2J(Thread*, + object, + uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + + return fieldAtOffset(o, offset); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_getBoolean__Ljava_lang_Object_2J(Thread* t, + object method, + uintptr_t* arguments) +{ + return Avian_sun_misc_Unsafe_getByte__Ljava_lang_Object_2J( + t, method, arguments); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putBoolean__Ljava_lang_Object_2JZ( + Thread*, + object, + uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + uint8_t value = arguments[4]; + + fieldAtOffset(o, offset) = value; +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_putLong__Ljava_lang_Object_2JJ(Thread*, + object, + uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + int64_t value; + memcpy(&value, arguments + 4, 8); + + fieldAtOffset(o, offset) = value; +} + +extern "C" AVIAN_EXPORT void JNICALL +Avian_sun_misc_Unsafe_putDouble__Ljava_lang_Object_2JD(Thread*, + object, + uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; + memcpy(&offset, arguments + 2, 8); + jdouble value; + memcpy(&value, arguments + 4, 8); + + fieldAtOffset(o, offset) = value; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_sun_misc_Unsafe_pageSize(Thread*, object, uintptr_t*) +{ + return local::PageSize; +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_ensureClassInitialized(Thread* t, + object, + uintptr_t* arguments) +{ + initClass( + t, cast(t, reinterpret_cast(arguments[1]))->vmClass()); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_monitorEnter(Thread* t, object, uintptr_t* arguments) +{ + acquire(t, reinterpret_cast(arguments[1])); +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_sun_misc_Unsafe_monitorExit(Thread* t, object, uintptr_t* arguments) +{ + release(t, reinterpret_cast(arguments[1])); +} + +extern "C" AVIAN_EXPORT jboolean JNICALL + Avian_sun_misc_Unsafe_isBigEndian0(Thread*, object, uintptr_t*) +{ + return false; +} + +extern "C" AVIAN_EXPORT jboolean JNICALL + Avian_sun_misc_Unsafe_unalignedAccess0(Thread*, object, uintptr_t*) +{ + return false; +} + +namespace { + +namespace local { + +extern "C" AVIAN_EXPORT jobjectArray JNICALL EXPORT(JVM_GetMethodParameters)(Thread*, jobject) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jint JNICALL EXPORT(JVM_GetInterfaceVersion)() +{ + return local::InterfaceVersion; +} + +extern "C" AVIAN_EXPORT jint JNICALL EXPORT(JVM_IHashCode)(Thread* t, jobject o) +{ + ENTER(t, Thread::ActiveState); + + return o ? objectHash(t, *o) : 0; +} + +uint64_t jvmWait(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + jlong milliseconds; + memcpy(&milliseconds, arguments + 1, sizeof(jlong)); + + vm::wait(t, *o, milliseconds); + + return 1; +} + +extern "C" AVIAN_EXPORT void JNICALL + EXPORT(JVM_MonitorWait)(Thread* t, jobject o, jlong milliseconds) +{ + uintptr_t arguments[1 + (sizeof(jlong) / BytesPerWord)]; + arguments[0] = reinterpret_cast(o); + memcpy(arguments + 1, &milliseconds, sizeof(jlong)); + + run(t, jvmWait, arguments); +} + +uint64_t jvmNotify(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + + notify(t, *o); + + return 1; +} + +extern "C" AVIAN_EXPORT void JNICALL + EXPORT(JVM_MonitorNotify)(Thread* t, jobject o) +{ + uintptr_t arguments[] = {reinterpret_cast(o)}; + + run(t, jvmNotify, arguments); +} + +uint64_t jvmNotifyAll(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + + notifyAll(t, *o); + + return 1; +} + +extern "C" AVIAN_EXPORT void JNICALL + EXPORT(JVM_MonitorNotifyAll)(Thread* t, jobject o) +{ + uintptr_t arguments[] = {reinterpret_cast(o)}; + + run(t, jvmNotifyAll, arguments); +} + +uint64_t jvmClone(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + + return reinterpret_cast(makeLocalReference(t, clone(t, *o))); +} + +extern "C" AVIAN_EXPORT jobject JNICALL EXPORT(JVM_Clone)(Thread* t, jobject o) +{ + uintptr_t arguments[] = {reinterpret_cast(o)}; + + return reinterpret_cast(run(t, jvmClone, arguments)); +} + +uint64_t jvmInternString(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + + return reinterpret_cast(makeLocalReference(t, intern(t, *o))); +} + +extern "C" AVIAN_EXPORT jstring JNICALL + EXPORT(JVM_InternString)(Thread* t, jstring s) +{ + uintptr_t arguments[] = {reinterpret_cast(s)}; + + return reinterpret_cast(run(t, jvmInternString, arguments)); +} + +extern "C" AVIAN_EXPORT jlong JNICALL + EXPORT(JVM_CurrentTimeMillis)(Thread* t, jclass) +{ + return t->m->system->now(); +} + +extern "C" AVIAN_EXPORT jlong JNICALL EXPORT(JVM_NanoTime)(Thread* t, jclass) +{ + return t->m->system->now() * 1000 * 1000; +} + +uint64_t jvmArrayCopy(Thread* t, uintptr_t* arguments) +{ + jobject src = reinterpret_cast(arguments[0]); + jint srcOffset = arguments[1]; + jobject dst = reinterpret_cast(arguments[2]); + jint dstOffset = arguments[3]; + jint length = arguments[4]; + + arrayCopy(t, *src, srcOffset, *dst, dstOffset, length); + + return 1; +} + +extern "C" AVIAN_EXPORT void JNICALL EXPORT(JVM_ArrayCopy)(Thread* t, + jclass, + jobject src, + jint srcOffset, + jobject dst, + jint dstOffset, + jint length) +{ + uintptr_t arguments[] = {reinterpret_cast(src), + static_cast(srcOffset), + reinterpret_cast(dst), + static_cast(dstOffset), + static_cast(length)}; + + run(t, jvmArrayCopy, arguments); +} + +uint64_t jvmInitProperties(Thread* t, uintptr_t* arguments) +{ + jobject properties = reinterpret_cast(arguments[0]); + + GcMethod* method = resolveMethod( + t, + roots(t)->bootLoader(), + "java/util/Properties", + "setProperty", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;"); + + PROTECT(t, method); + +#ifdef PLATFORM_WINDOWS + local::setProperty(t, method, *properties, "line.separator", "\r\n"); + local::setProperty(t, method, *properties, "file.separator", "\\"); + local::setProperty(t, method, *properties, "path.separator", ";"); + local::setProperty(t, method, *properties, "os.name", "Windows"); + + TCHAR buffer[MAX_PATH]; + GetTempPath(MAX_PATH, buffer); + + local::setProperty(t, method, *properties, "java.io.tmpdir", buffer); + local::setProperty(t, method, *properties, "java.home", buffer); + local::setProperty( + t, method, *properties, "user.home", _wgetenv(L"USERPROFILE"), "%ls"); + + GetCurrentDirectory(MAX_PATH, buffer); + + local::setProperty(t, method, *properties, "user.dir", buffer); +#else // not PLATFORM_WINDOWS + local::setProperty(t, method, *properties, "line.separator", "\n"); + local::setProperty(t, method, *properties, "file.separator", "/"); + local::setProperty(t, method, *properties, "path.separator", ":"); +#ifdef __APPLE__ + local::setProperty(t, method, *properties, "os.name", "Mac OS X"); +#elif defined __FreeBSD__ + local::setProperty(t, method, *properties, "os.name", "FreeBSD"); +#else // not __APPLE__ + local::setProperty(t, method, *properties, "os.name", "Linux"); +#endif // not __APPLE__ + local::setProperty(t, method, *properties, "java.io.tmpdir", "/tmp"); + local::setProperty(t, method, *properties, "user.home", getenv("HOME")); + + char buffer[PATH_MAX]; + local::setProperty( + t, method, *properties, "user.dir", getcwd(buffer, PATH_MAX)); +#endif // not PLATFORM_WINDOWS + + local::setProperty( + t, method, *properties, "java.protocol.handler.pkgs", "avian"); + + local::setProperty( + t, method, *properties, "java.vm.vendor", "Avian Contributors"); + + local::setProperty(t, method, *properties, "java.vm.name", "Avian"); +#ifdef AVIAN_VERSION + local::setProperty(t, method, *properties, "java.vm.version", AVIAN_VERSION); +#endif +#ifdef AVIAN_INFO + local::setProperty(t, method, *properties, "java.vm.info", AVIAN_INFO); +#endif + + local::setProperty(t, + method, + *properties, + "java.vm.specification.name", + "Java Virtual Machine Specification"); + + local::setProperty( + t, + method, + *properties, + "java.home", + static_cast(t->m->classpath)->javaHome); + + local::setProperty( + t, + method, + *properties, + "sun.boot.library.path", + static_cast(t->m->classpath)->libraryPath); + + local::setProperty( + t, + method, + *properties, + "sun.boot.class.path", + static_cast( + roots(t)->bootLoader()->as(t)->finder()) + ->path()); + + local::setProperty(t, method, *properties, "file.encoding", "UTF-8"); + + local::setProperty(t, method, *properties, "jdk.lang.Process.launchMechanism", "fork"); + +#ifdef ARCH_x86_32 + local::setProperty(t, method, *properties, "os.arch", "x86"); +#elif defined ARCH_x86_64 + local::setProperty(t, method, *properties, "os.arch", "x86_64"); +#elif defined ARCH_arm + local::setProperty(t, method, *properties, "os.arch", "arm"); +#else + local::setProperty(t, method, *properties, "os.arch", "unknown"); +#endif + + for (unsigned i = 0; i < t->m->propertyCount; ++i) { + const char* start = t->m->properties[i]; + const char* p = start; + while (*p and *p != '=') + ++p; + + if (*p == '=') { + THREAD_RUNTIME_ARRAY(t, char, name, (p - start) + 1); + memcpy(RUNTIME_ARRAY_BODY(name), start, p - start); + RUNTIME_ARRAY_BODY(name)[p - start] = 0; + local::setProperty( + t, method, *properties, RUNTIME_ARRAY_BODY(name), p + 1); + } + } + + return reinterpret_cast(properties); +} + +extern "C" AVIAN_EXPORT jobject JNICALL + EXPORT(JVM_InitProperties)(Thread* t, jobject properties) +{ + uintptr_t arguments[] = {reinterpret_cast(properties)}; + + return reinterpret_cast(run(t, jvmInitProperties, arguments)); +} + +extern "C" AVIAN_EXPORT void JNICALL EXPORT(JVM_OnExit)(void (*)(void)) +{ + abort(); +} + +extern "C" AVIAN_EXPORT void JNICALL EXPORT(JVM_Exit)(jint code) +{ + exit(code); +} + +extern "C" AVIAN_EXPORT void JNICALL EXPORT(JVM_Halt)(jint code) +{ + exit(code); +} + +uint64_t jvmGC(Thread* t, uintptr_t*) +{ + collect(t, Heap::MajorCollection); + + return 1; +} + +extern "C" AVIAN_EXPORT void JNICALL EXPORT(JVM_GC)() +{ + Thread* t = static_cast(local::globalMachine->localThread->get()); + + run(t, jvmGC, 0); +} + +extern "C" AVIAN_EXPORT jlong JNICALL EXPORT(JVM_MaxObjectInspectionAge)(void) +{ + return 0; +} + +extern "C" AVIAN_EXPORT void JNICALL EXPORT(JVM_TraceInstructions)(jboolean) +{ + abort(); +} + +extern "C" AVIAN_EXPORT void JNICALL EXPORT(JVM_TraceMethodCalls)(jboolean) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jlong JNICALL EXPORT(JVM_TotalMemory)() +{ + return 0; +} + +extern "C" AVIAN_EXPORT jlong JNICALL EXPORT(JVM_FreeMemory)() +{ + return 0; +} + +extern "C" AVIAN_EXPORT jlong JNICALL EXPORT(JVM_MaxMemory)() +{ + return local::globalMachine->heap->limit(); +} + +extern "C" AVIAN_EXPORT jint JNICALL EXPORT(JVM_ActiveProcessorCount)() +{ +#ifdef PLATFORM_WINDOWS + SYSTEM_INFO si; + GetSystemInfo(&si); + return si.dwNumberOfProcessors; +#else + return sysconf(_SC_NPROCESSORS_ONLN); +#endif +} + +uint64_t jvmLoadLibrary(Thread* t, uintptr_t* arguments) +{ + const char* path = reinterpret_cast(arguments[0]); + + THREAD_RUNTIME_ARRAY(t, char, p, strlen(path) + 1); + replace('\\', '/', RUNTIME_ARRAY_BODY(p), path); + + return reinterpret_cast(loadLibrary( + t, + static_cast(t->m->classpath)->libraryPath, + RUNTIME_ARRAY_BODY(p), + false, + false)); +} + +extern "C" AVIAN_EXPORT void* JNICALL EXPORT(JVM_LoadLibrary)(const char* path) +{ + Thread* t = static_cast(local::globalMachine->localThread->get()); + + uintptr_t arguments[] = {reinterpret_cast(path)}; + + return reinterpret_cast(run(t, jvmLoadLibrary, arguments)); +} + +extern "C" AVIAN_EXPORT void JNICALL EXPORT(JVM_UnloadLibrary)(void*) +{ + // todo: implement this correctly for POSIX and Windows +} + +extern "C" AVIAN_EXPORT void* JNICALL + EXPORT(JVM_FindLibraryEntry)(void* library, const char* name) +{ + Thread* t = static_cast(local::globalMachine->localThread->get()); + + ENTER(t, Thread::ActiveState); + + if (library == RTLD_DEFAULT) { + library = t->m->libraries; + } + + for (System::Library* lib = t->m->libraries; lib; lib = lib->next()) { + if (library == lib) { + return lib->resolve(name); + } + } + + return 0; +} + +extern "C" AVIAN_EXPORT jboolean JNICALL + EXPORT(JVM_IsSupportedJNIVersion)(jint version) +{ + return version <= JNI_VERSION_1_6; +} + +extern "C" AVIAN_EXPORT jboolean JNICALL EXPORT(JVM_IsNaN)(jdouble v) +{ + return isnan(v); +} + +uint64_t jvmFillInStackTrace(Thread* t, uintptr_t* arguments) +{ + GcThrowable* throwable + = cast(t, *reinterpret_cast(arguments[0])); + PROTECT(t, throwable); + + object trace = getTrace(t, 2); + throwable->setTrace(t, trace); + + return 1; +} + +extern "C" AVIAN_EXPORT void JNICALL + EXPORT(JVM_FillInStackTrace)(Thread* t, jobject throwable) +{ + uintptr_t arguments[] = {reinterpret_cast(throwable)}; + + run(t, jvmFillInStackTrace, arguments); +} + +extern "C" AVIAN_EXPORT void JNICALL + EXPORT(JVM_PrintStackTrace)(Thread*, jobject, jobject) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_GetStackTraceDepth)(Thread* t, jobject throwable) +{ + ENTER(t, Thread::ActiveState); + + return objectArrayLength(t, cast(t, *throwable)->trace()); +} + +uint64_t jvmGetStackTraceElement(Thread* t, uintptr_t* arguments) +{ + jobject throwable = reinterpret_cast(arguments[0]); + jint index = arguments[1]; + + return reinterpret_cast(makeLocalReference( + t, + makeStackTraceElement( + t, + cast( + t, + objectArrayBody( + t, cast(t, *throwable)->trace(), index))))); +} + +extern "C" AVIAN_EXPORT jobject JNICALL + EXPORT(JVM_GetStackTraceElement)(Thread* t, jobject throwable, jint index) +{ + uintptr_t arguments[] + = {reinterpret_cast(throwable), static_cast(index)}; + + return reinterpret_cast(run(t, jvmGetStackTraceElement, arguments)); +} + +extern "C" AVIAN_EXPORT void JNICALL + EXPORT(JVM_InitializeCompiler)(Thread*, jclass) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jboolean JNICALL + EXPORT(JVM_IsSilentCompiler)(Thread*, jclass) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jboolean JNICALL + EXPORT(JVM_CompileClass)(Thread*, jclass, jclass) +{ + return false; +} + +extern "C" AVIAN_EXPORT jboolean JNICALL + EXPORT(JVM_CompileClasses)(Thread*, jclass, jstring) +{ + return false; +} + +extern "C" AVIAN_EXPORT jobject JNICALL + EXPORT(JVM_CompilerCommand)(Thread*, jclass, jobject) +{ + abort(); +} + +extern "C" AVIAN_EXPORT void JNICALL EXPORT(JVM_EnableCompiler)(Thread*, jclass) +{ + // ignore +} + +extern "C" AVIAN_EXPORT void JNICALL + EXPORT(JVM_DisableCompiler)(Thread*, jclass) +{ + // ignore +} + +uint64_t jvmStartThread(Thread* t, uintptr_t* arguments) +{ + jobject thread = reinterpret_cast(arguments[0]); + + return startThread(t, cast(t, *thread)) != 0; +} + +extern "C" AVIAN_EXPORT void JNICALL + EXPORT(JVM_StartThread)(Thread* t, jobject thread) +{ + uintptr_t arguments[] = {reinterpret_cast(thread)}; + + run(t, jvmStartThread, arguments); +} + +extern "C" AVIAN_EXPORT void JNICALL + EXPORT(JVM_StopThread)(Thread*, jobject, jobject) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jboolean JNICALL + EXPORT(JVM_IsThreadAlive)(Thread* t, jobject thread) +{ + ENTER(t, Thread::ActiveState); + + Thread* p = reinterpret_cast(cast(t, *thread)->peer()); + return p and (p->getFlags() & Thread::ActiveFlag) != 0; +} + +extern "C" AVIAN_EXPORT void JNICALL EXPORT(JVM_SuspendThread)(Thread*, jobject) +{ + abort(); +} + +extern "C" AVIAN_EXPORT void JNICALL EXPORT(JVM_ResumeThread)(Thread*, jobject) +{ + abort(); +} + +extern "C" AVIAN_EXPORT void JNICALL + EXPORT(JVM_SetThreadPriority)(Thread*, jobject, jint) +{ + // ignore +} + +extern "C" AVIAN_EXPORT void JNICALL EXPORT(JVM_Yield)(Thread* t, jclass) +{ + t->m->system->yield(); +} + +uint64_t jvmSleep(Thread* t, uintptr_t* arguments) +{ + jlong milliseconds; + memcpy(&milliseconds, arguments, sizeof(jlong)); + + if (milliseconds <= 0) { + milliseconds = 1; + } + + if (t->javaThread->sleepLock() == 0) { + GcJobject* lock = makeJobject(t); + t->javaThread->setSleepLock(t, lock); + } + + acquire(t, t->javaThread->sleepLock()); + vm::wait(t, t->javaThread->sleepLock(), milliseconds); + release(t, t->javaThread->sleepLock()); + + return 1; +} + +extern "C" AVIAN_EXPORT void JNICALL + EXPORT(JVM_Sleep)(Thread* t, jclass, jlong milliseconds) +{ + uintptr_t arguments[sizeof(jlong) / BytesPerWord]; + memcpy(arguments, &milliseconds, sizeof(jlong)); + + run(t, jvmSleep, arguments); +} + +extern "C" AVIAN_EXPORT jobject JNICALL + EXPORT(JVM_CurrentThread)(Thread* t, jclass) +{ + ENTER(t, Thread::ActiveState); + + return makeLocalReference(t, t->javaThread); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_CountStackFrames)(Thread*, jobject) +{ + abort(); +} + +uint64_t jvmInterrupt(Thread* t, uintptr_t* arguments) +{ + threadInterrupt(t, + cast(t, *reinterpret_cast(arguments[0]))); + + return 1; +} + +extern "C" AVIAN_EXPORT void JNICALL + EXPORT(JVM_Interrupt)(Thread* t, jobject thread) +{ + uintptr_t arguments[] = {reinterpret_cast(thread)}; + + run(t, jvmInterrupt, arguments); +} + +uint64_t jvmIsInterrupted(Thread* t, uintptr_t* arguments) +{ + jobject thread = reinterpret_cast(arguments[0]); + jboolean clear = arguments[1]; + + return threadIsInterrupted(t, cast(t, *thread), clear); +} + +extern "C" AVIAN_EXPORT jboolean JNICALL + EXPORT(JVM_IsInterrupted)(Thread* t, jobject thread, jboolean clear) +{ + uintptr_t arguments[] = {reinterpret_cast(thread), clear}; + + return run(t, jvmIsInterrupted, arguments); +} + +uint64_t jvmHoldsLock(Thread* t, uintptr_t* arguments) +{ + GcMonitor* m + = objectMonitor(t, *reinterpret_cast(arguments[0]), false); + + return m and m->owner() == t; +} + +extern "C" AVIAN_EXPORT jboolean JNICALL + EXPORT(JVM_HoldsLock)(Thread* t, jclass, jobject o) +{ + uintptr_t arguments[] = {reinterpret_cast(o)}; + + return run(t, jvmHoldsLock, arguments); +} + +extern "C" AVIAN_EXPORT void JNICALL EXPORT(JVM_DumpAllStacks)(Thread*, jclass) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jobjectArray JNICALL + EXPORT(JVM_GetAllThreads)(Thread*, jclass) +{ + abort(); +} + +uint64_t jvmDumpThreads(Thread* t, uintptr_t* arguments) +{ + jobjectArray threads = reinterpret_cast(arguments[0]); + + unsigned threadsLength + = objectArrayLength(t, reinterpret_cast(*threads)); + GcClass* arrayClass + = resolveObjectArrayClass(t, + type(t, GcStackTraceElement::Type)->loader(), + type(t, GcStackTraceElement::Type)); + object result = makeObjectArray(t, arrayClass, threadsLength); + PROTECT(t, result); + + for (unsigned threadsIndex = 0; threadsIndex < threadsLength; + ++threadsIndex) { + Thread* peer = reinterpret_cast( + cast(t, + objectArrayBody( + t, reinterpret_cast(*threads), threadsIndex)) + ->peer()); + + if (peer) { + object trace = t->m->processor->getStackTrace(t, peer); + PROTECT(t, trace); + + unsigned traceLength = objectArrayLength(t, trace); + object array + = makeObjectArray(t, type(t, GcStackTraceElement::Type), traceLength); + PROTECT(t, array); + + for (unsigned traceIndex = 0; traceIndex < traceLength; ++traceIndex) { + object ste = makeStackTraceElement( + t, cast(t, objectArrayBody(t, trace, traceIndex))); + setField(t, array, ArrayBody + (traceIndex * BytesPerWord), ste); + } + + setField(t, result, ArrayBody + (threadsIndex * BytesPerWord), array); + } + } + + return reinterpret_cast(makeLocalReference(t, result)); +} + +extern "C" AVIAN_EXPORT jobjectArray JNICALL + EXPORT(JVM_DumpThreads)(Thread* t, jclass, jobjectArray threads) +{ + uintptr_t arguments[] = {reinterpret_cast(threads)}; + + return reinterpret_cast(run(t, jvmDumpThreads, arguments)); +} + +extern "C" AVIAN_EXPORT jclass JNICALL EXPORT(JVM_CurrentLoadedClass)(Thread*) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jobject JNICALL EXPORT(JVM_CurrentClassLoader)(Thread*) +{ + // just return null, i.e. tell SecurityManager.currentClassLoader + // all permissions are granted, since Avian doesn't do any internal + // security checks: + return 0; +} + +uint64_t jvmGetClassContext(Thread* t, uintptr_t*) +{ + object trace = getTrace(t, 1); + PROTECT(t, trace); + + object context = makeObjectArray( + t, type(t, GcJclass::Type), objectArrayLength(t, trace)); + PROTECT(t, context); + + for (unsigned i = 0; i < objectArrayLength(t, trace); ++i) { + object c = getJClass( + t, + cast( + t, cast(t, objectArrayBody(t, trace, i))->method()) + ->class_()); + + setField(t, context, ArrayBody + (i * BytesPerWord), c); + } + + return reinterpret_cast(makeLocalReference(t, context)); +} + +extern "C" AVIAN_EXPORT jobjectArray JNICALL + EXPORT(JVM_GetClassContext)(Thread* t) +{ + return reinterpret_cast(run(t, jvmGetClassContext, 0)); +} + +extern "C" AVIAN_EXPORT jint JNICALL EXPORT(JVM_ClassDepth)(Thread*, jstring) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jint JNICALL EXPORT(JVM_ClassLoaderDepth)(Thread*) +{ + abort(); +} + +uint64_t jvmGetSystemPackage(Thread* t, uintptr_t* arguments) +{ + jstring s = reinterpret_cast(arguments[0]); + + ACQUIRE(t, t->m->classLock); + + THREAD_RUNTIME_ARRAY(t, char, chars, (*s)->length(t) + 1); + stringChars(t, *s, RUNTIME_ARRAY_BODY(chars)); + + object key = makeByteArray(t, RUNTIME_ARRAY_BODY(chars)); + + GcByteArray* array = cast( + t, + hashMapFind( + t, roots(t)->packageMap(), key, byteArrayHash, byteArrayEqual)); + + if (array) { + return reinterpret_cast(makeLocalReference( + t, t->m->classpath->makeString(t, array, 0, array->length()))); + } else { + return 0; + } +} + +extern "C" AVIAN_EXPORT jstring JNICALL + EXPORT(JVM_GetSystemPackage)(Thread* t, jstring s) +{ + uintptr_t arguments[] = {reinterpret_cast(s)}; + + return reinterpret_cast(run(t, jvmGetSystemPackage, arguments)); +} + +uint64_t jvmGetSystemPackages(Thread* t, uintptr_t*) +{ + return reinterpret_cast(makeLocalReference( + t, + makeObjectArray( + t, resolveClass(t, roots(t)->bootLoader(), "java/lang/Package"), 0))); +} + +extern "C" AVIAN_EXPORT jobjectArray JNICALL + EXPORT(JVM_GetSystemPackages)(Thread* t) +{ + return reinterpret_cast(run(t, jvmGetSystemPackages, 0)); +} + +extern "C" AVIAN_EXPORT jobject JNICALL + EXPORT(JVM_AllocateNewObject)(Thread*, jobject, jclass, jclass) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jobject JNICALL + EXPORT(JVM_AllocateNewArray)(Thread*, jobject, jclass, jint) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jobject JNICALL + EXPORT(JVM_LatestUserDefinedLoader)(Thread* t) +{ + ENTER(t, Thread::ActiveState); + + class Visitor : public Processor::StackVisitor { + public: + Visitor(Thread* t) : t(t), loader(0) + { + } + + virtual bool visit(Processor::StackWalker* walker) + { + GcClassLoader* loader = walker->method()->class_()->loader(); + if (loader and loader != roots(t)->bootLoader() + and strcmp(objectClass(t, loader)->name()->body().begin(), + reinterpret_cast( + "sun/reflect/DelegatingClassLoader"))) { + this->loader = loader; + return false; + } else { + return true; + } + } + + Thread* t; + GcClassLoader* loader; + } v(t); + + t->m->processor->walkStack(t, &v); + + return makeLocalReference(t, v.loader); +} + +extern "C" AVIAN_EXPORT jclass JNICALL + EXPORT(JVM_LoadClass0)(Thread*, jobject, jclass, jstring) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_GetArrayLength)(Thread* t, jobject array) +{ + ENTER(t, Thread::ActiveState); + + return fieldAtOffset(*array, BytesPerWord); +} + +uint64_t jvmGetArrayElement(Thread* t, uintptr_t* arguments) +{ + jobject array = reinterpret_cast(arguments[0]); + jint index = arguments[1]; + + switch (objectClass(t, *array)->name()->body()[1]) { + case 'Z': + return reinterpret_cast(makeLocalReference( + t, makeBoolean(t, fieldAtOffset(*array, ArrayBody + index)))); + case 'B': + return reinterpret_cast(makeLocalReference( + t, makeByte(t, fieldAtOffset(*array, ArrayBody + index)))); + case 'C': + return reinterpret_cast(makeLocalReference( + t, + makeChar(t, fieldAtOffset(*array, ArrayBody + (index * 2))))); + case 'S': + return reinterpret_cast(makeLocalReference( + t, + makeShort(t, fieldAtOffset(*array, ArrayBody + (index * 2))))); + case 'I': + return reinterpret_cast(makeLocalReference( + t, + makeInt(t, fieldAtOffset(*array, ArrayBody + (index * 4))))); + case 'F': + return reinterpret_cast(makeLocalReference( + t, + makeFloat(t, fieldAtOffset(*array, ArrayBody + (index * 4))))); + case 'J': + return reinterpret_cast(makeLocalReference( + t, + makeLong(t, fieldAtOffset(*array, ArrayBody + (index * 8))))); + case 'D': + return reinterpret_cast(makeLocalReference( + t, + makeDouble(t, + fieldAtOffset(*array, ArrayBody + (index * 8))))); + case 'L': + case '[': + return reinterpret_cast(makeLocalReference( + t, fieldAtOffset(*array, ArrayBody + (index * BytesPerWord)))); + default: + abort(t); + } +} + +extern "C" AVIAN_EXPORT jobject JNICALL + EXPORT(JVM_GetArrayElement)(Thread* t, jobject array, jint index) +{ + uintptr_t arguments[] + = {reinterpret_cast(array), static_cast(index)}; + + return reinterpret_cast(run(t, jvmGetArrayElement, arguments)); +} + +extern "C" AVIAN_EXPORT jvalue JNICALL + EXPORT(JVM_GetPrimitiveArrayElement)(Thread*, jobject, jint, jint) +{ + abort(); +} + +extern "C" AVIAN_EXPORT void JNICALL EXPORT(JVM_SetArrayElement)(Thread* t, + jobject array, + jint index, + jobject value) +{ + ENTER(t, Thread::ActiveState); + + switch (objectClass(t, *array)->name()->body()[1]) { + case 'Z': + fieldAtOffset(*array, ArrayBody + index) + = cast(t, *value)->value(); + break; + case 'B': + fieldAtOffset(*array, ArrayBody + index) + = cast(t, *value)->value(); + break; + case 'C': + fieldAtOffset(*array, ArrayBody + (index * 2)) + = cast(t, *value)->value(); + break; + case 'S': + fieldAtOffset(*array, ArrayBody + (index * 2)) + = cast(t, *value)->value(); + break; + case 'I': + fieldAtOffset(*array, ArrayBody + (index * 4)) + = cast(t, *value)->value(); + break; + case 'F': + fieldAtOffset(*array, ArrayBody + (index * 4)) + = cast(t, *value)->value(); + break; + case 'J': + fieldAtOffset(*array, ArrayBody + (index * 8)) + = cast(t, *value)->value(); + break; + case 'D': + fieldAtOffset(*array, ArrayBody + (index * 8)) + = cast(t, *value)->value(); + break; + case 'L': + case '[': + setField( + t, *array, ArrayBody + (index * BytesPerWord), (value ? *value : 0)); + break; + default: + abort(t); + } +} + +extern "C" AVIAN_EXPORT void JNICALL EXPORT( + JVM_SetPrimitiveArrayElement)(Thread*, jobject, jint, jvalue, unsigned char) +{ + abort(); +} + +object makeNewArray(Thread* t, GcClass* c, unsigned length) +{ + if (c->vmFlags() & PrimitiveFlag) { + const char* name + = reinterpret_cast(local::getClassName(t, c)->body().begin()); + + switch (*name) { + case 'b': + if (name[1] == 'o') { + return makeBooleanArray(t, length); + } else { + return makeByteArray(t, length); + } + case 'c': + return makeCharArray(t, length); + case 'd': + return makeDoubleArray(t, length); + case 'f': + return makeFloatArray(t, length); + case 'i': + return makeIntArray(t, length); + case 'l': + return makeLongArray(t, length); + case 's': + return makeShortArray(t, length); + default: + abort(t); + } + } else { + return makeObjectArray(t, c, length); + } +} + +uint64_t jvmNewArray(Thread* t, uintptr_t* arguments) +{ + jclass elementClass = reinterpret_cast(arguments[0]); + jint length = arguments[1]; + + return reinterpret_cast(makeLocalReference( + t, makeNewArray(t, (*elementClass)->vmClass(), length))); +} + +extern "C" AVIAN_EXPORT jobject JNICALL + EXPORT(JVM_NewArray)(Thread* t, jclass elementClass, jint length) +{ + uintptr_t arguments[] = {reinterpret_cast(elementClass), + static_cast(length)}; + + return reinterpret_cast(run(t, jvmNewArray, arguments)); +} + +uint64_t jvmNewMultiArray(Thread* t, uintptr_t* arguments) +{ + jclass elementClass = reinterpret_cast(arguments[0]); + jintArray dimensions = reinterpret_cast(arguments[1]); + + THREAD_RUNTIME_ARRAY(t, int32_t, counts, (*dimensions)->length()); + for (int i = (*dimensions)->length() - 1; i >= 0; --i) { + RUNTIME_ARRAY_BODY(counts)[i] = (*dimensions)->body()[i]; + if (UNLIKELY(RUNTIME_ARRAY_BODY(counts)[i] < 0)) { + throwNew(t, + GcNegativeArraySizeException::Type, + "%d", + RUNTIME_ARRAY_BODY(counts)[i]); + return 0; + } + } + + object array = makeNewArray( + t, (*elementClass)->vmClass(), RUNTIME_ARRAY_BODY(counts)[0]); + PROTECT(t, array); + + populateMultiArray( + t, array, RUNTIME_ARRAY_BODY(counts), 0, (*dimensions)->length()); + + return reinterpret_cast(makeLocalReference(t, array)); +} + +extern "C" AVIAN_EXPORT jobject JNICALL + EXPORT(JVM_NewMultiArray)(Thread* t, + jclass elementClass, + jintArray dimensions) +{ + uintptr_t arguments[] = {reinterpret_cast(elementClass), + reinterpret_cast(dimensions)}; + + return reinterpret_cast(run(t, jvmNewMultiArray, arguments)); +} + +extern "C" AVIAN_EXPORT jclass JNICALL + EXPORT(JVM_GetCallerClass)(Thread* t, int target) +{ + ENTER(t, Thread::ActiveState); + + GcMethod* method = getCaller(t, target, true); + + return method ? reinterpret_cast( + makeLocalReference(t, getJClass(t, method->class_()))) + : 0; +} + +extern "C" AVIAN_EXPORT jclass JNICALL + EXPORT(JVM_FindPrimitiveClass)(Thread* t, const char* name) +{ + ENTER(t, Thread::ActiveState); + + switch (*name) { + case 'b': + if (name[1] == 'o') { + return reinterpret_cast( + makeLocalReference(t, getJClass(t, type(t, GcJboolean::Type)))); + } else { + return reinterpret_cast( + makeLocalReference(t, getJClass(t, type(t, GcJbyte::Type)))); + } + case 'c': + return reinterpret_cast( + makeLocalReference(t, getJClass(t, type(t, GcJchar::Type)))); + case 'd': + return reinterpret_cast( + makeLocalReference(t, getJClass(t, type(t, GcJdouble::Type)))); + case 'f': + return reinterpret_cast( + makeLocalReference(t, getJClass(t, type(t, GcJfloat::Type)))); + case 'i': + return reinterpret_cast( + makeLocalReference(t, getJClass(t, type(t, GcJint::Type)))); + case 'l': + return reinterpret_cast( + makeLocalReference(t, getJClass(t, type(t, GcJlong::Type)))); + case 's': + return reinterpret_cast( + makeLocalReference(t, getJClass(t, type(t, GcJshort::Type)))); + case 'v': + return reinterpret_cast( + makeLocalReference(t, getJClass(t, type(t, GcJvoid::Type)))); + default: + throwNew(t, GcIllegalArgumentException::Type); + } +} + +uint64_t jvmResolveClass(Thread* t, uintptr_t* arguments) +{ + jclass c = reinterpret_cast(arguments[0]); + + GcMethod* method = resolveMethod( + t, roots(t)->bootLoader(), "avian/Classes", "link", "(Lavian/VMClass;)V"); + + t->m->processor->invoke(t, method, 0, (*c)->vmClass()); + + return 1; +} + +extern "C" AVIAN_EXPORT void JNICALL + EXPORT(JVM_ResolveClass)(Thread* t, jclass c) +{ + uintptr_t arguments[] = {reinterpret_cast(c)}; + + run(t, jvmResolveClass, arguments); +} + +uint64_t jvmFindClassFromCaller(Thread* t, uintptr_t* arguments) +{ + const char* name = reinterpret_cast(arguments[0]); + jboolean init = arguments[1]; + jobject loader = reinterpret_cast(arguments[2]); + // jclass caller = reinterpret_cast(arguments[3]); + + /* XXX The caller's protection domain should be used during + the resolveClass but there is no specification or + unit-test in OpenJDK documenting the desired effect */ + + GcClass* c = resolveClass( + t, + loader ? cast(t, *loader) : roots(t)->bootLoader(), + name, + true, + static_cast(GcClassNotFoundException::Type)); + + if (init) { + PROTECT(t, c); + + initClass(t, c); + } + + return reinterpret_cast(makeLocalReference(t, getJClass(t, c))); +} + +extern "C" AVIAN_EXPORT jclass JNICALL + EXPORT(JVM_FindClassFromCaller)(Thread* t, + const char* name, + jboolean init, + jobject loader, + jclass caller) +{ + uintptr_t arguments[] = {reinterpret_cast(name), + init, + reinterpret_cast(loader), + reinterpret_cast(caller)}; + + return reinterpret_cast( + run(t, jvmFindClassFromCaller, arguments)); +} + +uint64_t jvmFindClassFromClassLoader(Thread* t, uintptr_t* arguments) +{ + const char* name = reinterpret_cast(arguments[0]); + jboolean init = arguments[1]; + jobject loader = reinterpret_cast(arguments[2]); + jboolean throwError = arguments[3]; + + GcClass* c = resolveClass( + t, + loader ? cast(t, *loader) : roots(t)->bootLoader(), + name, + true, + throwError ? static_cast(GcNoClassDefFoundError::Type) + : static_cast(GcClassNotFoundException::Type)); + + if (init) { + PROTECT(t, c); + + initClass(t, c); + } + + return reinterpret_cast(makeLocalReference(t, getJClass(t, c))); +} + +extern "C" AVIAN_EXPORT jclass JNICALL + EXPORT(JVM_FindClassFromClassLoader)(Thread* t, + const char* name, + jboolean init, + jobject loader, + jboolean throwError) +{ + uintptr_t arguments[] = {reinterpret_cast(name), + init, + reinterpret_cast(loader), + throwError}; + + return reinterpret_cast( + run(t, jvmFindClassFromClassLoader, arguments)); +} + +extern "C" AVIAN_EXPORT jclass JNICALL + JVM_FindClassFromBootLoader(Thread* t, const char* name) +{ + return EXPORT(JVM_FindClassFromClassLoader)(t, name, false, 0, false); +} + +extern "C" AVIAN_EXPORT jclass JNICALL + EXPORT(JVM_FindClassFromClass)(Thread*, const char*, jboolean, jclass) +{ + abort(); +} + +uint64_t jvmFindLoadedClass(Thread* t, uintptr_t* arguments) +{ + jobject loader = reinterpret_cast(arguments[0]); + jstring name = reinterpret_cast(arguments[1]); + + GcByteArray* spec = makeByteArray(t, (*name)->length(t) + 1); + + { + char* s = reinterpret_cast(spec->body().begin()); + stringChars(t, (*name), s); + replace('.', '/', s); + } + + GcClass* c = findLoadedClass(t, cast(t, *loader), spec); + + return reinterpret_cast(c ? makeLocalReference(t, getJClass(t, c)) + : 0); +} + +extern "C" AVIAN_EXPORT jclass JNICALL + EXPORT(JVM_FindLoadedClass)(Thread* t, jobject loader, jstring name) +{ + uintptr_t arguments[] = {reinterpret_cast(loader), + reinterpret_cast(name)}; + + return reinterpret_cast(run(t, jvmFindLoadedClass, arguments)); +} + +uint64_t jvmDefineClass(Thread* t, uintptr_t* arguments) +{ + jobject loader = reinterpret_cast(arguments[0]); + const uint8_t* data = reinterpret_cast(arguments[1]); + jsize length = arguments[2]; + + return reinterpret_cast(makeLocalReference( + t, + getJClass( + t, + cast( + t, + defineClass(t, cast(t, *loader), data, length))))); +} + +extern "C" AVIAN_EXPORT jclass JNICALL + EXPORT(JVM_DefineClass)(Thread* t, + const char*, + jobject loader, + const uint8_t* data, + jsize length, + jobject) +{ + uintptr_t arguments[] = {reinterpret_cast(loader), + reinterpret_cast(data), + static_cast(length)}; + + return reinterpret_cast(run(t, jvmDefineClass, arguments)); +} + +extern "C" AVIAN_EXPORT jclass JNICALL + EXPORT(JVM_DefineClassWithSource)(Thread* t, + const char*, + jobject loader, + const uint8_t* data, + jsize length, + jobject, + const char*) +{ + return EXPORT(JVM_DefineClass)(t, 0, loader, data, length, 0); +} + +extern "C" AVIAN_EXPORT jclass JNICALL + EXPORT(JVM_DefineClassWithSourceCond)(Thread* t, + const char*, + jobject loader, + const uint8_t* data, + jsize length, + jobject, + const char*, + jboolean) +{ + return EXPORT(JVM_DefineClass)(t, 0, loader, data, length, 0); +} + +extern "C" AVIAN_EXPORT jstring JNICALL + EXPORT(JVM_GetClassName)(Thread* t, jclass c) +{ + ENTER(t, Thread::ActiveState); + + return reinterpret_cast(makeLocalReference(t, (*c)->name())); +} + +uint64_t jvmGetClassInterfaces(Thread* t, uintptr_t* arguments) +{ + jclass c = reinterpret_cast(arguments[0]); + + GcClassAddendum* addendum = (*c)->vmClass()->addendum(); + if (addendum) { + GcArray* table = cast(t, addendum->interfaceTable()); + if (table) { + PROTECT(t, table); + + object array + = makeObjectArray(t, type(t, GcJclass::Type), table->length()); + PROTECT(t, array); + + for (unsigned i = 0; i < table->length(); ++i) { + object c = getJClass(t, cast(t, table->body()[i])); + setField(t, array, ArrayBody + (i * BytesPerWord), c); + } + + return reinterpret_cast(makeLocalReference(t, array)); + } + } + + return reinterpret_cast( + makeLocalReference(t, makeObjectArray(t, type(t, GcJclass::Type), 0))); +} + +extern "C" AVIAN_EXPORT jobjectArray JNICALL + EXPORT(JVM_GetClassInterfaces)(Thread* t, jclass c) +{ + uintptr_t arguments[] = {reinterpret_cast(c)}; + + return reinterpret_cast( + run(t, jvmGetClassInterfaces, arguments)); +} + +extern "C" AVIAN_EXPORT jobject JNICALL + EXPORT(JVM_GetClassLoader)(Thread* t, jclass c) +{ + ENTER(t, Thread::ActiveState); + + GcClassLoader* loader = (*c)->vmClass()->loader(); + + if (loader == roots(t)->bootLoader()) { + // sun.misc.Unsafe.getUnsafe expects a null result if the class + // loader is the boot classloader and will throw a + // SecurityException otherwise. + GcMethod* caller = getCaller(t, 2); + if (caller + and strcmp(reinterpret_cast( + caller->class_()->name()->body().begin()), + "sun/misc/Unsafe") == 0) { + return 0; + } else { + return makeLocalReference(t, roots(t)->bootLoader()); + } + } else { + return makeLocalReference(t, loader); + } +} + +extern "C" AVIAN_EXPORT jboolean JNICALL + EXPORT(JVM_IsInterface)(Thread* t, jclass c) +{ + ENTER(t, Thread::ActiveState); + + return ((*c)->vmClass()->flags() & ACC_INTERFACE) != 0; +} + +extern "C" AVIAN_EXPORT jobjectArray JNICALL + EXPORT(JVM_GetClassSigners)(Thread* t, jclass c) +{ + ENTER(t, Thread::ActiveState); + + GcClassRuntimeData* runtimeData + = getClassRuntimeDataIfExists(t, (*c)->vmClass()); + + return runtimeData ? reinterpret_cast( + makeLocalReference(t, runtimeData->signers())) + : 0; +} + +extern "C" AVIAN_EXPORT jbyteArray JNICALL + EXPORT(JVM_GetClassTypeAnnotations)(Thread*, jclass) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jbyteArray JNICALL + EXPORT(JVM_GetFieldTypeAnnotations)(Thread*, jobject) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jbyteArray JNICALL + EXPORT(JVM_GetMethodTypeAnnotations)(Thread*, jobject) +{ + abort(); +} + +extern "C" AVIAN_EXPORT void JNICALL + EXPORT(JVM_SetClassSigners)(Thread* t, jclass c, jobjectArray signers) +{ + ENTER(t, Thread::ActiveState); + + GcClassRuntimeData* runtimeData = getClassRuntimeData(t, (*c)->vmClass()); + + runtimeData->setSigners(t, reinterpret_cast(*signers)); +} + +uint64_t jvmGetProtectionDomain(Thread* t, uintptr_t* arguments) +{ + jclass c = reinterpret_cast(arguments[0]); + + GcMethod* method + = resolveMethod(t, + roots(t)->bootLoader(), + "avian/Classes", + "getProtectionDomain", + "(Lavian/VMClass;)Ljava/security/ProtectionDomain;"); + + return reinterpret_cast(makeLocalReference( + t, t->m->processor->invoke(t, method, 0, (*c)->vmClass()))); +} + +extern "C" AVIAN_EXPORT jobject JNICALL + EXPORT(JVM_GetProtectionDomain)(Thread* t, jclass c) +{ + uintptr_t arguments[] = {reinterpret_cast(c)}; + + return reinterpret_cast(run(t, jvmGetProtectionDomain, arguments)); +} + +extern "C" AVIAN_EXPORT jobject JNICALL + EXPORT(JVM_GetResourceLookupCacheURLs)(Thread*, jobject) +{ + return 0; +} + +extern "C" AVIAN_EXPORT void JNICALL + EXPORT(JVM_SetProtectionDomain)(Thread*, jclass, jobject) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jboolean JNICALL + EXPORT(JVM_IsArrayClass)(Thread* t, jclass c) +{ + ENTER(t, Thread::ActiveState); + + return (*c)->vmClass()->arrayDimensions() != 0; +} + +extern "C" AVIAN_EXPORT jboolean JNICALL + EXPORT(JVM_IsPrimitiveClass)(Thread* t, jclass c) +{ + ENTER(t, Thread::ActiveState); + + return ((*c)->vmClass()->vmFlags() & PrimitiveFlag) != 0; +} + +uint64_t jvmGetComponentType(Thread* t, uintptr_t* arguments) +{ + jclass c = reinterpret_cast(arguments[0]); + + if ((*c)->vmClass()->arrayDimensions()) { + uint8_t n = (*c)->vmClass()->name()->body()[1]; + if (n != 'L' and n != '[') { + return reinterpret_cast( + makeLocalReference(t, getJClass(t, primitiveClass(t, n)))); + } else { + return reinterpret_cast(makeLocalReference( + t, getJClass(t, (*c)->vmClass()->arrayElementClass()))); + } + } else { + return 0; + } +} + +extern "C" AVIAN_EXPORT jclass JNICALL + EXPORT(JVM_GetComponentType)(Thread* t, jclass c) +{ + uintptr_t arguments[] = {reinterpret_cast(c)}; + + return reinterpret_cast(run(t, jvmGetComponentType, arguments)); +} + +uint64_t jvmGetClassModifiers(Thread* t, uintptr_t* arguments) +{ + return classModifiers( + t, + cast(t, *reinterpret_cast(arguments[0]))->vmClass()); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_GetClassModifiers)(Thread* t, jclass c) +{ + uintptr_t arguments[] = {reinterpret_cast(c)}; + + return run(t, jvmGetClassModifiers, arguments); +} + +uint64_t jvmGetDeclaredClasses(Thread* t, uintptr_t* arguments) +{ + return reinterpret_cast(makeLocalReference( + t, + getDeclaredClasses( + t, + cast(t, *reinterpret_cast(arguments[0])) + ->vmClass(), + false))); +} + +extern "C" AVIAN_EXPORT jobjectArray JNICALL + EXPORT(JVM_GetDeclaredClasses)(Thread* t, jclass c) +{ + uintptr_t arguments[] = {reinterpret_cast(c)}; + + return reinterpret_cast( + run(t, jvmGetDeclaredClasses, arguments)); +} + +uint64_t jvmGetDeclaringClass(Thread* t, uintptr_t* arguments) +{ + return reinterpret_cast(makeLocalReference( + t, + getDeclaringClass( + t, + cast(t, *reinterpret_cast(arguments[0])) + ->vmClass()))); +} + +extern "C" AVIAN_EXPORT jclass JNICALL + EXPORT(JVM_GetDeclaringClass)(Thread* t, jclass c) +{ + uintptr_t arguments[] = {reinterpret_cast(c)}; + + return reinterpret_cast(run(t, jvmGetDeclaringClass, arguments)); +} + +uint64_t jvmGetClassSignature(Thread* t, uintptr_t* arguments) +{ + jclass c = reinterpret_cast(arguments[0]); + + GcClassAddendum* addendum = (*c)->vmClass()->addendum(); + if (addendum) { + GcByteArray* signature = cast(t, addendum->signature()); + if (signature) { + return reinterpret_cast( + makeLocalReference(t, + t->m->classpath->makeString( + t, signature, 0, signature->length() - 1))); + } + } + return 0; +} + +extern "C" AVIAN_EXPORT jstring JNICALL + EXPORT(JVM_GetClassSignature)(Thread* t, jclass c) +{ + uintptr_t arguments[] = {reinterpret_cast(c)}; + + return reinterpret_cast(run(t, jvmGetClassSignature, arguments)); +} + +extern "C" AVIAN_EXPORT jbyteArray JNICALL + EXPORT(JVM_GetClassAnnotations)(Thread* t, jclass c) +{ + ENTER(t, Thread::ActiveState); + + GcClassAddendum* addendum = (*c)->vmClass()->addendum(); + return addendum ? reinterpret_cast( + makeLocalReference(t, addendum->annotationTable())) + : 0; +} + +uint64_t jvmGetClassDeclaredMethods(Thread* t, uintptr_t* arguments) +{ + jclass c = reinterpret_cast(arguments[0]); + jboolean publicOnly = arguments[1]; + + GcArray* table = cast(t, (*c)->vmClass()->methodTable()); + if (table) { + PROTECT(t, table); + + object array + = makeObjectArray(t, + type(t, GcJmethod::Type), + local::countMethods(t, (*c)->vmClass(), publicOnly)); + PROTECT(t, array); + + unsigned ai = 0; + for (unsigned i = 0, j = classDeclaredMethodCount(t, (*c)->vmClass()); + i < j; + ++i) { + GcMethod* vmMethod = cast(t, table->body()[i]); + PROTECT(t, vmMethod); + + if (((not publicOnly) or (vmMethod->flags() & ACC_PUBLIC)) + and vmMethod->name()->body()[0] != '<') { + object method = makeJmethod(t, vmMethod, i); + + assertT(t, ai < objectArrayLength(t, array)); + + setField(t, array, ArrayBody + ((ai++) * BytesPerWord), method); + } + } + + return reinterpret_cast(makeLocalReference(t, array)); + } else { + return reinterpret_cast( + makeLocalReference(t, makeObjectArray(t, type(t, GcJmethod::Type), 0))); + } +} + +extern "C" AVIAN_EXPORT jobjectArray JNICALL + EXPORT(JVM_GetClassDeclaredMethods)(Thread* t, + jclass c, + jboolean publicOnly) +{ + uintptr_t arguments[] = {reinterpret_cast(c), publicOnly}; + + return reinterpret_cast( + run(t, jvmGetClassDeclaredMethods, arguments)); +} + +uint64_t jvmGetClassDeclaredFields(Thread* t, uintptr_t* arguments) +{ + jclass c = reinterpret_cast(arguments[0]); + jboolean publicOnly = arguments[1]; + GcArray* table = cast(t, (*c)->vmClass()->fieldTable()); + if (table) { + PROTECT(t, table); + + object array + = makeObjectArray(t, + type(t, GcJfield::Type), + local::countFields(t, (*c)->vmClass(), publicOnly)); + PROTECT(t, array); + + unsigned ai = 0; + for (unsigned i = 0; i < table->length(); ++i) { + GcField* vmField = cast(t, table->body()[i]); + PROTECT(t, vmField); + + if ((not publicOnly) or (vmField->flags() & ACC_PUBLIC)) { + object field = makeJfield(t, vmField, i); + + assertT(t, ai < objectArrayLength(t, array)); + + setField(t, array, ArrayBody + ((ai++) * BytesPerWord), field); + } + } + assertT(t, ai == objectArrayLength(t, array)); + + return reinterpret_cast(makeLocalReference(t, array)); + } else { + return reinterpret_cast( + makeLocalReference(t, makeObjectArray(t, type(t, GcJfield::Type), 0))); + } +} + +extern "C" AVIAN_EXPORT jobjectArray JNICALL + EXPORT(JVM_GetClassDeclaredFields)(Thread* t, jclass c, jboolean publicOnly) +{ + uintptr_t arguments[] = {reinterpret_cast(c), publicOnly}; + + return reinterpret_cast( + run(t, jvmGetClassDeclaredFields, arguments)); +} + +uint64_t jvmGetClassDeclaredConstructors(Thread* t, uintptr_t* arguments) +{ + jclass c = reinterpret_cast(arguments[0]); + jboolean publicOnly = arguments[1]; + + GcArray* table = cast(t, (*c)->vmClass()->methodTable()); + if (table) { + PROTECT(t, table); + + object array = makeObjectArray( + t, + type(t, GcJconstructor::Type), + local::countConstructors(t, (*c)->vmClass(), publicOnly)); + PROTECT(t, array); + + unsigned ai = 0; + for (unsigned i = 0, j = classDeclaredMethodCount(t, (*c)->vmClass()); + i < j; + ++i) { + GcMethod* vmMethod = cast(t, table->body()[i]); + PROTECT(t, vmMethod); + + bool isCtor = strcmp(reinterpret_cast(vmMethod->name()->body().begin()), "") == 0; + if (((not publicOnly) or (vmMethod->flags() & ACC_PUBLIC)) and isCtor) { + object method = makeJconstructor(t, vmMethod, i); + + assertT(t, ai < objectArrayLength(t, array)); + + setField(t, array, ArrayBody + ((ai++) * BytesPerWord), method); + } + } + + return reinterpret_cast(makeLocalReference(t, array)); + } else { + return reinterpret_cast(makeLocalReference( + t, makeObjectArray(t, type(t, GcJconstructor::Type), 0))); + } +} + +extern "C" AVIAN_EXPORT jobjectArray JNICALL + EXPORT(JVM_GetClassDeclaredConstructors)(Thread* t, + jclass c, + jboolean publicOnly) +{ + uintptr_t arguments[] = {reinterpret_cast(c), publicOnly}; + + return reinterpret_cast( + run(t, jvmGetClassDeclaredConstructors, arguments)); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_GetClassAccessFlags)(Thread* t, jclass c) +{ + ENTER(t, Thread::ActiveState); + + return (*c)->vmClass()->flags(); +} + +uint64_t jvmInvokeMethod(Thread* t, uintptr_t* arguments) +{ + jobject method = reinterpret_cast(arguments[0]); + jobject instance = reinterpret_cast(arguments[1]); + jobjectArray args = reinterpret_cast(arguments[2]); + + GcMethod* vmMethod = cast( + t, + cast( + t, cast(t, *method)->clazz()->vmClass()->methodTable()) + ->body()[cast(t, *method)->slot()]); + + if (vmMethod->flags() & ACC_STATIC) { + instance = 0; + } + + if (instance and not instanceOf(t, vmMethod->class_(), *instance)) { + throwNew(t, GcIllegalArgumentException::Type); + } + + return reinterpret_cast( + makeLocalReference(t, + invoke(t, + vmMethod, + instance ? *instance : 0, + args ? reinterpret_cast(*args) : 0))); +} + +extern "C" AVIAN_EXPORT jobject JNICALL + EXPORT(JVM_InvokeMethod)(Thread* t, + jobject method, + jobject instance, + jobjectArray args) +{ + uintptr_t arguments[] = {reinterpret_cast(method), + reinterpret_cast(instance), + reinterpret_cast(args)}; + + return reinterpret_cast(run(t, jvmInvokeMethod, arguments)); +} + +uint64_t jvmNewInstanceFromConstructor(Thread* t, uintptr_t* arguments) +{ + jobject constructor = reinterpret_cast(arguments[0]); + jobjectArray args = reinterpret_cast(arguments[1]); + + object instance + = make(t, cast(t, *constructor)->clazz()->vmClass()); + PROTECT(t, instance); + + GcMethod* method = cast( + t, + cast(t, + cast(t, *constructor) + ->clazz() + ->vmClass() + ->methodTable()) + ->body()[cast(t, *constructor)->slot()]); + + invoke(t, method, instance, args ? reinterpret_cast(*args) : 0); + + return reinterpret_cast(makeLocalReference(t, instance)); +} + +extern "C" AVIAN_EXPORT jobject JNICALL + EXPORT(JVM_NewInstanceFromConstructor)(Thread* t, + jobject constructor, + jobjectArray args) +{ + uintptr_t arguments[] = {reinterpret_cast(constructor), + reinterpret_cast(args)}; + + return reinterpret_cast( + run(t, jvmNewInstanceFromConstructor, arguments)); +} + +extern "C" AVIAN_EXPORT jobject JNICALL + EXPORT(JVM_GetClassConstantPool)(Thread* t, jclass c) +{ + ENTER(t, Thread::ActiveState); + + GcClass* vmClass = (*c)->vmClass(); + GcClassAddendum* addendum = vmClass->addendum(); + object pool; + if (addendum) { + pool = addendum->pool(); + } else { + pool = 0; + } + + if (pool == 0) { + pool = getClassRuntimeData(t, vmClass)->pool(); + } + + return makeLocalReference(t, makeConstantPool(t, pool)); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_ConstantPoolGetSize)(Thread* t, jobject, jobject pool) +{ + if (pool == 0) + return 0; + + ENTER(t, Thread::ActiveState); + + return singletonCount(t, cast(t, *pool)); +} + +extern "C" AVIAN_EXPORT jclass JNICALL + EXPORT(JVM_ConstantPoolGetClassAt)(Thread*, jobject, jobject, jint) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jclass JNICALL + EXPORT(JVM_ConstantPoolGetClassAtIfLoaded)(Thread*, jobject, jobject, jint) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jobject JNICALL + EXPORT(JVM_ConstantPoolGetMethodAt)(Thread*, jobject, jobject, jint) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jobject JNICALL + EXPORT(JVM_ConstantPoolGetMethodAtIfLoaded)(Thread*, jobject, jobject, jint) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jobject JNICALL + EXPORT(JVM_ConstantPoolGetFieldAt)(Thread*, jobject, jobject, jint) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jobject JNICALL + EXPORT(JVM_ConstantPoolGetFieldAtIfLoaded)(Thread*, jobject, jobject, jint) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jobjectArray JNICALL + EXPORT(JVM_ConstantPoolGetMemberRefInfoAt)(Thread*, jobject, jobject, jint) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jint JNICALL EXPORT( + JVM_ConstantPoolGetIntAt)(Thread* t, jobject, jobject pool, jint index) +{ + ENTER(t, Thread::ActiveState); + + return singletonValue(t, cast(t, *pool), index - 1); +} + +extern "C" AVIAN_EXPORT jlong JNICALL EXPORT( + JVM_ConstantPoolGetLongAt)(Thread* t, jobject, jobject pool, jint index) +{ + ENTER(t, Thread::ActiveState); + + uint64_t v; + memcpy(&v, &singletonValue(t, cast(t, *pool), index - 1), 8); + + return v; +} + +extern "C" AVIAN_EXPORT jfloat JNICALL EXPORT( + JVM_ConstantPoolGetFloatAt)(Thread* t, jobject, jobject pool, jint index) +{ + ENTER(t, Thread::ActiveState); + + return bitsToFloat(singletonValue(t, cast(t, *pool), index - 1)); +} + +extern "C" AVIAN_EXPORT jdouble JNICALL EXPORT( + JVM_ConstantPoolGetDoubleAt)(Thread* t, jobject, jobject pool, jint index) +{ + ENTER(t, Thread::ActiveState); + + double v; + memcpy(&v, &singletonValue(t, cast(t, *pool), index - 1), 8); + + return v; +} + +extern "C" AVIAN_EXPORT jstring JNICALL + EXPORT(JVM_ConstantPoolGetStringAt)(Thread*, jobject, jobject, jint) +{ + abort(); +} + +uint64_t jvmConstantPoolGetUTF8At(Thread* t, uintptr_t* arguments) +{ + jobject pool = reinterpret_cast(arguments[0]); + jint index = arguments[1]; + + object array = parseUtf8( + t, + cast( + t, singletonObject(t, cast(t, *pool), index - 1))); + + return reinterpret_cast(makeLocalReference( + t, + t->m->classpath->makeString( + t, array, 0, fieldAtOffset(array, BytesPerWord) - 1))); +} + +extern "C" AVIAN_EXPORT jstring JNICALL EXPORT( + JVM_ConstantPoolGetUTF8At)(Thread* t, jobject, jobject pool, jint index) +{ + uintptr_t arguments[] + = {reinterpret_cast(pool), static_cast(index)}; + + return reinterpret_cast(run(t, jvmConstantPoolGetUTF8At, arguments)); +} + +void maybeWrap(Thread* t, bool wrapException) +{ + if (t->exception and wrapException + and not(instanceOf(t, type(t, GcError::Type), t->exception) + or instanceOf( + t, type(t, GcRuntimeException::Type), t->exception))) { + GcThrowable* exception = t->exception; + t->exception = 0; + + PROTECT(t, exception); + + GcClass* paeClass = resolveClass( + t, roots(t)->bootLoader(), "java/security/PrivilegedActionException"); + PROTECT(t, paeClass); + + GcMethod* paeConstructor + = resolveMethod(t, paeClass, "", "(Ljava/lang/Exception;)V"); + PROTECT(t, paeConstructor); + + GcThrowable* result = cast(t, make(t, paeClass)); + PROTECT(t, result); + + t->m->processor->invoke(t, paeConstructor, result, exception); + + t->exception = result; + } +} + +uint64_t jvmDoPrivileged(Thread* t, uintptr_t* arguments) +{ + jobject action = reinterpret_cast(arguments[0]); + jboolean wrapException = arguments[1]; + + // todo: cache these class and method lookups in the t->m->classpath + // object: + + GcClass* privilegedAction = resolveClass( + t, roots(t)->bootLoader(), "java/security/PrivilegedAction"); + + GcMethod* method; + if (instanceOf(t, privilegedAction, *action)) { + method = resolveMethod(t, privilegedAction, "run", "()Ljava/lang/Object;"); + } else { + GcClass* privilegedExceptionAction = resolveClass( + t, roots(t)->bootLoader(), "java/security/PrivilegedExceptionAction"); + + method = resolveMethod( + t, privilegedExceptionAction, "run", "()Ljava/lang/Object;"); + } + + THREAD_RESOURCE(t, jboolean, wrapException, maybeWrap(t, wrapException)); + + return reinterpret_cast( + makeLocalReference(t, t->m->processor->invoke(t, method, *action))); +} + +extern "C" AVIAN_EXPORT jobject JNICALL + EXPORT(JVM_DoPrivileged)(Thread* t, + jclass, + jobject action, + jobject, + jboolean wrapException) +{ + uintptr_t arguments[] = {reinterpret_cast(action), wrapException}; + + return reinterpret_cast(run(t, jvmDoPrivileged, arguments)); +} + +extern "C" AVIAN_EXPORT jobject JNICALL + EXPORT(JVM_GetInheritedAccessControlContext)(Thread*, jclass) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jobject JNICALL + EXPORT(JVM_GetStackAccessControlContext)(Thread*, jclass) +{ + return 0; +} + +extern "C" AVIAN_EXPORT void* JNICALL EXPORT(JVM_RegisterSignal)(jint, void*) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jboolean JNICALL EXPORT(JVM_RaiseSignal)(jint) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jint JNICALL EXPORT(JVM_FindSignal)(const char*) +{ + return -1; +} + +extern "C" AVIAN_EXPORT jboolean JNICALL + EXPORT(JVM_DesiredAssertionStatus)(Thread*, jclass, jclass) +{ + return false; +} + +extern "C" AVIAN_EXPORT jobject JNICALL + EXPORT(JVM_AssertionStatusDirectives)(Thread*, jclass) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jboolean JNICALL EXPORT(JVM_SupportsCX8)() +{ + return true; +} + +extern "C" AVIAN_EXPORT const char* JNICALL + EXPORT(JVM_GetClassNameUTF)(Thread*, jclass) +{ + abort(); +} + +extern "C" AVIAN_EXPORT void JNICALL + EXPORT(JVM_GetClassCPTypes)(Thread*, jclass, unsigned char*) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_GetClassCPEntriesCount)(Thread*, jclass) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_GetClassFieldsCount)(Thread*, jclass) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_GetClassMethodsCount)(Thread*, jclass) +{ + abort(); +} + +extern "C" AVIAN_EXPORT void JNICALL EXPORT( + JVM_GetMethodIxExceptionIndexes)(Thread*, jclass, jint, unsigned short*) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_GetMethodIxExceptionsCount)(Thread*, jclass, jint) +{ + abort(); +} + +extern "C" AVIAN_EXPORT void JNICALL + EXPORT(JVM_GetMethodIxByteCode)(Thread*, jclass, jint, unsigned char*) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_GetMethodIxByteCodeLength)(Thread*, jclass, jint) +{ + abort(); +} + +extern "C" AVIAN_EXPORT void JNICALL EXPORT(JVM_GetMethodIxExceptionTableEntry)( + Thread*, + jclass, + jint, + jint, + local::JVM_ExceptionTableEntryType*) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_GetMethodIxExceptionTableLength)(Thread*, jclass, int) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_GetFieldIxModifiers)(Thread*, jclass, int) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_GetMethodIxModifiers)(Thread*, jclass, int) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_GetMethodIxLocalsCount)(Thread*, jclass, int) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_GetMethodIxArgsSize)(Thread*, jclass, int) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_GetMethodIxMaxStack)(Thread*, jclass, int) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jboolean JNICALL + EXPORT(JVM_IsConstructorIx)(Thread*, jclass, int) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jboolean JNICALL + EXPORT(JVM_IsVMGeneratedMethodIx)(Thread*, jclass, int) +{ + abort(); +} + +extern "C" AVIAN_EXPORT const char* JNICALL + EXPORT(JVM_GetMethodIxNameUTF)(Thread*, jclass, jint) +{ + abort(); +} + +extern "C" AVIAN_EXPORT const char* JNICALL + EXPORT(JVM_GetMethodIxSignatureUTF)(Thread*, jclass, jint) +{ + abort(); +} + +extern "C" AVIAN_EXPORT const char* JNICALL + EXPORT(JVM_GetCPFieldNameUTF)(Thread*, jclass, jint) +{ + abort(); +} + +extern "C" AVIAN_EXPORT const char* JNICALL + EXPORT(JVM_GetCPMethodNameUTF)(Thread*, jclass, jint) +{ + abort(); +} + +extern "C" AVIAN_EXPORT const char* JNICALL + EXPORT(JVM_GetCPMethodSignatureUTF)(Thread*, jclass, jint) +{ + abort(); +} + +extern "C" AVIAN_EXPORT const char* JNICALL + EXPORT(JVM_GetCPFieldSignatureUTF)(Thread*, jclass, jint) +{ + abort(); +} + +extern "C" AVIAN_EXPORT const char* JNICALL + EXPORT(JVM_GetCPClassNameUTF)(Thread*, jclass, jint) +{ + abort(); +} + +extern "C" AVIAN_EXPORT const char* JNICALL + EXPORT(JVM_GetCPFieldClassNameUTF)(Thread*, jclass, jint) +{ + abort(); +} + +extern "C" AVIAN_EXPORT const char* JNICALL + EXPORT(JVM_GetCPMethodClassNameUTF)(Thread*, jclass, jint) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_GetCPFieldModifiers)(Thread*, jclass, int, jclass) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_GetCPMethodModifiers)(Thread*, jclass, int, jclass) +{ + abort(); +} + +extern "C" AVIAN_EXPORT void JNICALL EXPORT(JVM_ReleaseUTF)(const char*) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jboolean JNICALL + EXPORT(JVM_IsSameClassPackage)(Thread*, jclass, jclass) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_GetLastErrorString)(char* dst, int length) +{ + strncpy(dst, strerror(errno), length); + return strlen(dst); +} + +extern "C" AVIAN_EXPORT char* JNICALL EXPORT(JVM_NativePath)(char* path) +{ + return path; +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_Open)(const char* path, jint flags, jint mode) +{ + int r = OPEN(path, flags & 0xFFFF, mode); + if (r == -1) { + return errno == EEXIST ? JVM_EEXIST : -1; + } else { + return r; + } +} + +extern "C" AVIAN_EXPORT jint JNICALL EXPORT(JVM_Close)(jint fd) +{ + return CLOSE(fd); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_Read)(jint fd, char* dst, jint length) +{ + return READ(fd, dst, length); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_Write)(jint fd, char* src, jint length) +{ + return WRITE(fd, src, length); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_Available)(jint fd, jlong* result) +{ + struct STAT buffer; + int n; + if (FSTAT(fd, &buffer) >= 0 + and (S_ISCHR(buffer.st_mode) or S_ISFIFO(buffer.st_mode) + or S_ISSOCK(buffer.st_mode)) and local::pipeAvailable(fd, &n)) { + *result = n; + return 1; + } + + int current = LSEEK(fd, 0, SEEK_CUR); + if (current == -1) + return 0; + + int end = LSEEK(fd, 0, SEEK_END); + if (end == -1) + return 0; + + if (LSEEK(fd, current, SEEK_SET) == -1) + return 0; + + *result = end - current; + return 1; +} + +extern "C" AVIAN_EXPORT jlong JNICALL + EXPORT(JVM_Lseek)(jint fd, jlong offset, jint seek) +{ + return LSEEK(fd, offset, seek); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_SetLength)(jint fd, jlong length) +{ +#ifdef PLATFORM_WINDOWS + HANDLE h = reinterpret_cast(_get_osfhandle(fd)); + if (h == INVALID_HANDLE_VALUE) { + errno = EBADF; + return -1; + } + + long high = length >> 32; + DWORD r = SetFilePointer(h, static_cast(length), &high, FILE_BEGIN); + if (r == 0xFFFFFFFF and GetLastError() != NO_ERROR) { + errno = EIO; + return -1; + } + + if (SetEndOfFile(h)) { + return 0; + } else { + errno = EIO; + return -1; + } +#else + return ftruncate(fd, length); +#endif +} + +extern "C" AVIAN_EXPORT jint JNICALL EXPORT(JVM_Sync)(jint fd) +{ +#ifdef PLATFORM_WINDOWS + HANDLE h = reinterpret_cast(_get_osfhandle(fd)); + if (h == INVALID_HANDLE_VALUE) { + errno = EBADF; + return -1; + } + + if (FlushFileBuffers(h)) { + return 0; + } else { + errno = EIO; + return -1; + } +#else + return fsync(fd); +#endif +} + +extern "C" AVIAN_EXPORT jint JNICALL EXPORT(JVM_InitializeSocketLibrary)() +{ +#ifdef PLATFORM_WINDOWS + static bool wsaInitialized = false; + if (not wsaInitialized) { + WSADATA data; + int r = WSAStartup(MAKEWORD(2, 2), &data); + if (r or LOBYTE(data.wVersion) != 2 or HIBYTE(data.wVersion) != 2) { + return -1; + } else { + wsaInitialized = true; + } + } +#endif + return 0; +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_Socket)(jint domain, jint type, jint protocol) +{ + return socket(domain, type, protocol); +} + +extern "C" AVIAN_EXPORT jint JNICALL EXPORT(JVM_SocketClose)(jint socket) +{ +#ifdef PLATFORM_WINDOWS + return closesocket(socket); +#else + return close(socket); +#endif +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_SocketShutdown)(jint socket, jint how) +{ + return shutdown(socket, how); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_Recv)(jint socket, char* dst, jint count, jint flags) +{ + return recv(socket, dst, count, flags); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_Send)(jint socket, char* src, jint count, jint flags) +{ + return send(socket, src, count, flags); +} + +extern "C" AVIAN_EXPORT jint JNICALL EXPORT(JVM_Timeout)(int, long) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jint JNICALL EXPORT(JVM_Listen)(jint socket, jint count) +{ + return listen(socket, count); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_Connect)(jint socket, sockaddr* address, jint addressLength) +{ + return connect(socket, address, addressLength); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_Bind)(jint, struct sockaddr*, jint) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_Accept)(jint socket, + struct sockaddr* address, + jint* addressLength) +{ + socklen_t length = *addressLength; + int r = accept(socket, address, &length); + *addressLength = length; + return r; +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_RecvFrom)(jint, char*, int, int, struct sockaddr*, int*) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_SendTo)(jint, char*, int, int, struct sockaddr*, int) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_SocketAvailable)(jint socket, jint* count) +{ +#ifdef PLATFORM_WINDOWS + unsigned long c = *count; + int r = ioctlsocket(socket, FIONREAD, &c); + *count = c; + return r; +#else + return ioctl(socket, FIONREAD, count) < 0 ? 0 : 1; +#endif +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_GetSockName)(jint socket, + struct sockaddr* address, + int* addressLength) +{ + socklen_t length = *addressLength; + int r = getsockname(socket, address, &length); + *addressLength = length; + return r; +} + +extern "C" AVIAN_EXPORT jint JNICALL EXPORT(JVM_GetSockOpt)(jint socket, + int level, + int optionName, + char* optionValue, + int* optionLength) +{ + socklen_t length = *optionLength; + int rv = getsockopt(socket, level, optionName, optionValue, &length); + *optionLength = length; + return rv; +} + +extern "C" AVIAN_EXPORT jint JNICALL + EXPORT(JVM_SetSockOpt)(jint socket, + int level, + int optionName, + const char* optionValue, + int optionLength) +{ + return setsockopt(socket, level, optionName, optionValue, optionLength); +} + +extern "C" AVIAN_EXPORT struct protoent* JNICALL + EXPORT(JVM_GetProtoByName)(char*) +{ + abort(); +} + +extern "C" AVIAN_EXPORT struct hostent* JNICALL + EXPORT(JVM_GetHostByAddr)(const char*, int, int) +{ + abort(); +} + +extern "C" AVIAN_EXPORT struct hostent* JNICALL EXPORT(JVM_GetHostByName)(char*) +{ + abort(); +} + +extern "C" AVIAN_EXPORT int JNICALL + EXPORT(JVM_GetHostName)(char* name, int length) +{ + return gethostname(name, length); +} + +extern "C" AVIAN_EXPORT void* JNICALL EXPORT(JVM_RawMonitorCreate)(void) +{ + System* s = local::globalMachine->system; + System::Monitor* lock; + if (s->success(s->make(&lock))) { + return lock; + } else { + return 0; + } +} + +extern "C" AVIAN_EXPORT void JNICALL EXPORT(JVM_RawMonitorDestroy)(void* lock) +{ + static_cast(lock)->dispose(); +} + +extern "C" AVIAN_EXPORT jint JNICALL EXPORT(JVM_RawMonitorEnter)(void* lock) +{ + static_cast(lock) + ->acquire(static_cast(local::globalMachine->localThread->get()) + ->systemThread); + + return 0; +} + +extern "C" AVIAN_EXPORT void JNICALL EXPORT(JVM_RawMonitorExit)(void* lock) +{ + static_cast(lock) + ->release(static_cast(local::globalMachine->localThread->get()) + ->systemThread); +} + +int JNICALL GetVersion(Thread*) +{ + return JMM_VERSION_1_0; +} + +uint64_t getInputArgumentArray(Thread* t, uintptr_t*) +{ + object array + = makeObjectArray(t, type(t, GcString::Type), t->m->argumentCount); + PROTECT(t, array); + + for (unsigned i = 0; i < t->m->argumentCount; ++i) { + GcString* argument = makeString(t, t->m->arguments[i]); + setField(t, array, ArrayBody + (i * BytesPerWord), argument); + } + + return reinterpret_cast(makeLocalReference(t, array)); +} + +jobjectArray JNICALL GetInputArgumentArray(Thread* t) +{ + return reinterpret_cast(run(t, getInputArgumentArray, 0)); +} + +jint JNICALL GetOptionalSupport(Thread*, jmmOptionalSupport* support) +{ + memset(support, 0, sizeof(jmmOptionalSupport)); + return 0; +} + +jlong JNICALL GetLongAttribute(Thread* t, jobject, jmmLongAttribute attribute) +{ + const unsigned JMM_JVM_INIT_DONE_TIME_MS = 7; + + switch (attribute) { + case JMM_JVM_INIT_DONE_TIME_MS: + return 0; + + default: + abort(t); + } +} + +jboolean JNICALL GetBoolAttribute(Thread* t, jmmBoolAttribute attribute) +{ + const unsigned JMM_THREAD_CPU_TIME = 24; + const unsigned JMM_THREAD_ALLOCATED_MEMORY = 25; + + switch (attribute) { + case JMM_THREAD_CPU_TIME: + case JMM_THREAD_ALLOCATED_MEMORY: + return false; + + default: + abort(t); + } +} + +uint64_t getMemoryManagers(Thread* t, uintptr_t*) +{ + return reinterpret_cast(makeLocalReference( + t, + makeObjectArray(t, + resolveClass(t, + roots(t)->bootLoader(), + "java/lang/management/MemoryManagerMXBean"), + 0))); +} + +jobjectArray JNICALL GetMemoryManagers(Thread* t, jobject) +{ + return reinterpret_cast(run(t, getMemoryManagers, 0)); +} + +uint64_t getMemoryPools(Thread* t, uintptr_t*) +{ + return reinterpret_cast(makeLocalReference( + t, + makeObjectArray(t, + resolveClass(t, + roots(t)->bootLoader(), + "java/lang/management/MemoryPoolMXBean"), + 0))); +} + +jobjectArray JNICALL GetMemoryPools(Thread* t, jobject) +{ + return reinterpret_cast(run(t, getMemoryPools, 0)); +} + +extern "C" AVIAN_EXPORT void* JNICALL EXPORT(JVM_GetManagement)(jint version) +{ + if (version == JMM_VERSION_1_0) { + JmmInterface* interface = &(static_cast( + local::globalMachine->classpath) + ->jmmInterface); + + memset(interface, 0, sizeof(JmmInterface)); + + interface->GetVersion = GetVersion; + interface->GetOptionalSupport = GetOptionalSupport; + interface->GetLongAttribute = GetLongAttribute; + interface->GetBoolAttribute = GetBoolAttribute; + interface->GetMemoryManagers = GetMemoryManagers; + interface->GetMemoryPools = GetMemoryPools; + interface->GetInputArgumentArray = GetInputArgumentArray; + + return interface; + } else { + return 0; + } +} + +extern "C" AVIAN_EXPORT jobject JNICALL + EXPORT(JVM_InitAgentProperties)(Thread*, jobject) +{ + abort(); +} + +uint64_t getEnclosingMethodInfo(Thread* t, uintptr_t* arguments) +{ + jclass c = reinterpret_cast(arguments[0]); + GcClass* class_ = (*c)->vmClass(); + PROTECT(t, class_); + + GcClassAddendum* addendum = class_->addendum(); + if (addendum) { + object enclosingClass = addendum->enclosingClass(); + if (enclosingClass) { + PROTECT(t, enclosingClass); + + object array = makeObjectArray(t, type(t, GcJobject::Type), 3); + PROTECT(t, array); + + enclosingClass = getJClass( + t, + resolveClass( + t, class_->loader(), cast(t, enclosingClass))); + + setField(t, array, ArrayBody, enclosingClass); + + GcPair* enclosingMethod = cast(t, addendum->enclosingMethod()); + + if (enclosingMethod) { + PROTECT(t, enclosingMethod); + + GcString* name = t->m->classpath->makeString( + t, + enclosingMethod->first(), + 0, + cast(t, enclosingMethod->first())->length() - 1); + + setField(t, array, ArrayBody + BytesPerWord, name); + + GcString* spec = t->m->classpath->makeString( + t, + enclosingMethod->second(), + 0, + cast(t, enclosingMethod->second())->length() - 1); + + setField(t, array, ArrayBody + (2 * BytesPerWord), spec); + } + + return reinterpret_cast(makeLocalReference(t, array)); + } + } + return 0; +} + +extern "C" AVIAN_EXPORT jobjectArray JNICALL + EXPORT(JVM_GetEnclosingMethodInfo)(Thread* t, jclass c) +{ + uintptr_t arguments[] = {reinterpret_cast(c)}; + + return reinterpret_cast( + run(t, getEnclosingMethodInfo, arguments)); +} + +extern "C" AVIAN_EXPORT jintArray JNICALL + EXPORT(JVM_GetThreadStateValues)(JNIEnv*, jint) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jobjectArray JNICALL + EXPORT(JVM_GetThreadStateNames)(JNIEnv*, jint, jintArray) +{ + abort(); +} + +extern "C" AVIAN_EXPORT void JNICALL + EXPORT(JVM_GetVersionInfo)(JNIEnv*, + local::jvm_version_info* info, + size_t size) +{ + memset(info, 0, size); + info->jvm_version = 0x01070000; +} + +extern "C" AVIAN_EXPORT jboolean JNICALL + EXPORT(JVM_CX8Field)(JNIEnv*, jobject*, jfieldID*, jlong, jlong) +{ + abort(); +} + +extern "C" AVIAN_EXPORT void JNICALL + EXPORT(JVM_SetNativeThreadName)(JNIEnv*, jobject, jstring) +{ + abort(); +} + +} // namespace local + +} // namespace + +extern "C" AVIAN_EXPORT int jio_vsnprintf(char* dst, + size_t size, + const char* format, + va_list a) +{ + return vm::vsnprintf(dst, size, format, a); +} + +extern "C" AVIAN_EXPORT int jio_vfprintf(FILE* stream, + const char* format, + va_list a) +{ + return vfprintf(stream, format, a); +} + +#ifdef PLATFORM_WINDOWS +extern "C" AVIAN_EXPORT void* JNICALL EXPORT(JVM_GetThreadInterruptEvent)() +{ + // hack: We don't want to expose thread interruption implementation + // details, so we give the class library a fake event to play with. + // This means that threads won't be interruptable when blocked in + // Process.waitFor. + static HANDLE fake = 0; + if (fake == 0) { + fake = CreateEvent(0, true, false, 0); + } + return fake; +} + +namespace { +HMODULE jvmHandle = 0; +} + +extern "C" int JDK_InitJvmHandle() +{ + jvmHandle = GetModuleHandle(0); + return jvmHandle != 0; +} + +extern "C" void* JDK_FindJvmEntry(const char* name) +{ + return voidPointer(GetProcAddress(jvmHandle, name)); +} + +extern "C" HMODULE JDK_LoadSystemLibrary(const char* name) { + HMODULE handle; + char path[MAX_PATH]; + + if (GetSystemDirectory(path, sizeof(path)) != 0) { + strcat(path, "\\"); + strcat(path, name); + handle = LoadLibrary(path); + } else { + handle = nullptr; + } + + if (handle == nullptr) { + if (GetWindowsDirectory(path, sizeof(path)) != 0) { + strcat(path, "\\"); + strcat(path, name); + handle = LoadLibrary(path); + } + } + return handle; +} + +#ifdef AVIAN_OPENJDK_SRC + +extern "C" char* findJavaTZ_md(const char*, const char*); + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_util_TimeZone_getSystemTimeZoneID(Thread* t, + object, + uintptr_t* arguments) +{ + // On Windows, findJavaTZ_md loads tzmappings from the filesystem + // using fopen, so we have no opportunity to make it read straight + // from the embedded JAR file as with files read from Java code. + // Therefore, we must extract tzmappings to a temporary location + // before calling findJavaTZ_md. We could avoid this by + // implementing findJavaTZ_md ourselves from scratch, but that would + // be a lot of code to implement and maintain. + + GcString* country = cast(t, reinterpret_cast(arguments[1])); + + THREAD_RUNTIME_ARRAY(t, char, countryChars, country->length(t) + 1); + stringChars(t, country, RUNTIME_ARRAY_BODY(countryChars)); + + local::MyClasspath* cp = static_cast(t->m->classpath); + + local::EmbeddedFile ef(cp, cp->tzMappings, cp->tzMappingsLength); + if (ef.jar == 0 or ef.jarLength == 0 or ef.pathLength == 0) { + return 0; + } + + Finder* finder = getFinder(t, ef.jar, ef.jarLength); + if (finder == 0) { + return 0; + } + + System::Region* r = finder->find(ef.path); + if (r == 0) { + return 0; + } + + THREAD_RESOURCE(t, System::Region*, r, r->dispose()); + + char tmpPath[MAX_PATH + 1]; + GetTempPathA(MAX_PATH, tmpPath); + + char tmpDir[MAX_PATH + 1]; + vm::snprintf(tmpDir, MAX_PATH, "%s/avian-tmp", tmpPath); + if (_mkdir(tmpDir) != 0 and errno != EEXIST) { + return 0; + } + + THREAD_RESOURCE(t, char*, tmpDir, rmdir(tmpDir)); + + char libDir[MAX_PATH + 1]; + vm::snprintf(libDir, MAX_PATH, "%s/lib", tmpDir); + if (mkdir(libDir) != 0 and errno != EEXIST) { + return 0; + } + + THREAD_RESOURCE(t, char*, libDir, rmdir(libDir)); + + char file[MAX_PATH + 1]; + vm::snprintf(file, MAX_PATH, "%s/tzmappings", libDir); + FILE* out = vm::fopen(file, "wb"); + if (out == 0) { + return 0; + } + + THREAD_RESOURCE(t, char*, file, unlink(file)); + THREAD_RESOURCE(t, FILE*, out, fclose(out)); + + if (fwrite(r->start(), 1, r->length(), out) != r->length() + or fflush(out) != 0) { + return 0; + } + + char* javaTZ = findJavaTZ_md(tmpDir, RUNTIME_ARRAY_BODY(countryChars)); + if (javaTZ) { + THREAD_RESOURCE(t, char*, javaTZ, free(javaTZ)); + + return reinterpret_cast(makeString(t, "%s", javaTZ)); + } else { + return 0; + } +} +#else // not AVIAN_OPENJDK_SRC +extern "C" AVIAN_EXPORT int jio_snprintf(char* dst, + size_t size, + const char* format, + ...) +{ + va_list a; + va_start(a, format); + + int r = jio_vsnprintf(dst, size, format, a); + + va_end(a); + + return r; +} + +extern "C" AVIAN_EXPORT int jio_fprintf(FILE* stream, const char* format, ...) +{ + va_list a; + va_start(a, format); + + int r = jio_vfprintf(stream, format, a); + + va_end(a); + + return r; +} +#endif // not AVIAN_OPENJDK_SRC +#endif // PLATFORM_WINDOWS diff --git a/sgx-jvm/avian/src/codegen/CMakeLists.txt b/sgx-jvm/avian/src/codegen/CMakeLists.txt new file mode 100644 index 0000000000..001a8681d3 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/CMakeLists.txt @@ -0,0 +1,18 @@ +add_library (avian_codegen + compiler.cpp + runtime.cpp + targets.cpp + + compiler/context.cpp + compiler/event.cpp + compiler/frame.cpp + compiler/ir.cpp + compiler/promise.cpp + compiler/read.cpp + compiler/regalloc.cpp + compiler/resource.cpp + compiler/site.cpp + compiler/value.cpp +) + +add_subdirectory(target) diff --git a/sgx-jvm/avian/src/codegen/compiler.cpp b/sgx-jvm/avian/src/codegen/compiler.cpp new file mode 100644 index 0000000000..24ac8c9239 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/compiler.cpp @@ -0,0 +1,2964 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/target.h" + +#include + +#include +#include +#include +#include + +#include "codegen/compiler/regalloc.h" +#include "codegen/compiler/context.h" +#include "codegen/compiler/resource.h" +#include "codegen/compiler/value.h" +#include "codegen/compiler/site.h" +#include "codegen/compiler/read.h" +#include "codegen/compiler/event.h" +#include "codegen/compiler/promise.h" +#include "codegen/compiler/frame.h" +#include "codegen/compiler/ir.h" + +using namespace vm; + +namespace avian { +namespace codegen { +namespace compiler { + +const bool DebugAppend = false; +const bool DebugCompile = false; +const bool DebugResources = false; +const bool DebugFrame = false; +const bool DebugControl = false; +const bool DebugBuddies = false; + +const unsigned StealRegisterReserveCount = 2; + +// this should be equal to the largest number of registers used by a +// compare instruction: +const unsigned ResolveRegisterReserveCount = (TargetBytesPerWord == 8 ? 2 : 4); + +void apply(Context* c, + lir::UnaryOperation op, + unsigned s1Size, + Site* s1Low, + Site* s1High); + +void apply(Context* c, + lir::BinaryOperation op, + unsigned s1Size, + Site* s1Low, + Site* s1High, + unsigned s2Size, + Site* s2Low, + Site* s2High); + +void apply(Context* c, + lir::TernaryOperation op, + unsigned s1Size, + Site* s1Low, + Site* s1High, + unsigned s2Size, + Site* s2Low, + Site* s2High, + unsigned s3Size, + Site* s3Low, + Site* s3High); + +class ConstantPoolNode { + public: + ConstantPoolNode(Promise* promise) : promise(promise), next(0) + { + } + + Promise* promise; + ConstantPoolNode* next; +}; + +Read* live(Context* c UNUSED, Value* v) +{ + assertT(c, v->buddy->hasBuddy(c, v)); + + Value* p = v; + do { + if (valid(p->reads)) { + return p->reads; + } + p = p->buddy; + } while (p != v); + + return 0; +} + +unsigned sitesToString(Context* c, Value* v, char* buffer, unsigned size); + +void deadWord(Context* c, Value* v) +{ + Value* nextWord = v->nextWord; + assertT(c, nextWord != v); + + for (SiteIterator it(c, v, true, false); it.hasMore();) { + Site* s = it.next(); + + if (s->registerSize(c) > TargetBytesPerWord) { + it.remove(c); + nextWord->addSite(c, s); + } + } +} + +void deadBuddy(Context* c, Value* v, Read* r UNUSED) +{ + assertT(c, v->buddy != v); + assertT(c, r); + + if (DebugBuddies) { + fprintf(stderr, "remove dead buddy %p from", v); + for (Value* p = v->buddy; p != v; p = p->buddy) { + fprintf(stderr, " %p", p); + } + fprintf(stderr, "\n"); + } + + assertT(c, v->buddy); + + Value* next = v->buddy; + v->buddy = v; + Value* p = next; + while (p->buddy != v) + p = p->buddy; + p->buddy = next; + + assertT(c, p->buddy); + + for (SiteIterator it(c, v, false, false); it.hasMore();) { + Site* s = it.next(); + it.remove(c); + + next->addSite(c, s); + } +} + +void popRead(Context* c, Event* e UNUSED, Value* v) +{ + assertT(c, e == v->reads->event); + + if (DebugReads) { + fprintf(stderr, + "pop read %p from %p next %p event %p (%s)\n", + v->reads, + v, + v->reads->next(c), + e, + (e ? e->name() : 0)); + } + + v->reads = v->reads->next(c); + + if (not valid(v->reads)) { + Value* nextWord = v->nextWord; + if (nextWord != v) { + if (valid(nextWord->reads)) { + deadWord(c, v); + } else { + deadWord(c, nextWord); + } + } + + Read* r = live(c, v); + if (r) { + deadBuddy(c, v, r); + } else { + v->clearSites(c); + } + } +} + +void addBuddy(Value* original, Value* buddy) +{ + buddy->buddy = original; + Value* p = original; + while (p->buddy != original) + p = p->buddy; + p->buddy = buddy; + + if (DebugBuddies) { + fprintf(stderr, "add buddy %p to", buddy); + for (Value* p = buddy->buddy; p != buddy; p = p->buddy) { + fprintf(stderr, " %p", p); + } + fprintf(stderr, "\n"); + } +} + +void move(Context* c, Value* value, Site* src, Site* dst); + +unsigned sitesToString(Context* c, Site* sites, char* buffer, unsigned size) +{ + unsigned total = 0; + for (Site* s = sites; s; s = s->next) { + total += s->toString(c, buffer + total, size - total); + + if (s->next) { + assertT(c, size > total + 2); + memcpy(buffer + total, ", ", 2); + total += 2; + } + } + + assertT(c, size > total); + buffer[total] = 0; + + return total; +} + +unsigned sitesToString(Context* c, Value* v, char* buffer, unsigned size) +{ + unsigned total = 0; + Value* p = v; + do { + if (total) { + assertT(c, size > total + 2); + memcpy(buffer + total, "; ", 2); + total += 2; + } + + if (p->sites) { + total += vm::snprintf(buffer + total, size - total, "%p has ", p); + total += sitesToString(c, p->sites, buffer + total, size - total); + } else { + total += vm::snprintf(buffer + total, size - total, "%p has nothing", p); + } + + p = p->buddy; + } while (p != v); + + return total; +} + +Site* pickTargetSite(Context* c, + Read* read, + bool intersectRead = false, + unsigned registerReserveCount = 0, + CostCalculator* costCalculator = 0) +{ + Target target( + pickTarget(c, read, intersectRead, registerReserveCount, costCalculator)); + + expect(c, target.cost < Target::Impossible); + + if (target.type == lir::Operand::Type::Memory) { + return frameSite(c, target.index); + } else { + return registerSite(c, Register(target.index)); + } +} + +bool acceptMatch(Context* c, Site* s, Read*, const SiteMask& mask) +{ + return s->match(c, mask); +} + +Site* pickSourceSite(Context* c, + Read* read, + Site* target = 0, + unsigned* cost = 0, + SiteMask* extraMask = 0, + bool intersectRead = true, + bool includeBuddies = true, + bool includeNextWord = true, + bool (*accept)(Context*, Site*, Read*, const SiteMask&) + = acceptMatch) +{ + SiteMask mask; + + if (extraMask) { + mask = mask.intersectionWith(*extraMask); + } + + if (intersectRead) { + read->intersect(&mask); + } + + Site* site = 0; + unsigned copyCost = 0xFFFFFFFF; + for (SiteIterator it(c, read->value, includeBuddies, includeNextWord); + it.hasMore();) { + Site* s = it.next(); + if (accept(c, s, read, mask)) { + unsigned v = s->copyCost(c, target); + if (v < copyCost) { + site = s; + copyCost = v; + } + } + } + + if (DebugMoves and site and target) { + char srcb[256]; + site->toString(c, srcb, 256); + char dstb[256]; + target->toString(c, dstb, 256); + fprintf(stderr, + "pick source %s to %s for %p cost %d\n", + srcb, + dstb, + read->value, + copyCost); + } + + if (cost) + *cost = copyCost; + return site; +} + +Site* maybeMove(Context* c, + Read* read, + bool intersectRead, + bool includeNextWord, + unsigned registerReserveCount = 0) +{ + Value* value = read->value; + unsigned size = value == value->nextWord ? TargetBytesPerWord : 8; + + class MyCostCalculator : public CostCalculator { + public: + MyCostCalculator(Value* value, unsigned size, bool includeNextWord) + : value(value), size(size), includeNextWord(includeNextWord) + { + } + + virtual unsigned cost(Context* c, SiteMask dstMask) + { + OperandMask src; + OperandMask tmp; + c->arch->planMove( + size, src, tmp, OperandMask(dstMask.typeMask, dstMask.registerMask, 0)); + + SiteMask srcMask = SiteMask::lowPart(src); + for (SiteIterator it(c, value, true, includeNextWord); it.hasMore();) { + Site* s = it.next(); + if (s->match(c, srcMask) or s->match(c, dstMask)) { + return 0; + } + } + + return Target::IndirectMovePenalty; + } + + Value* value; + unsigned size; + bool includeNextWord; + } costCalculator(value, size, includeNextWord); + + Site* dstSite = pickTargetSite( + c, read, intersectRead, registerReserveCount, &costCalculator); + + OperandMask src; + OperandMask tmp; + c->arch->planMove( + size, + src, + tmp, + OperandMask(1 << (unsigned)dstSite->type(c), dstSite->registerMask(c), 0)); + + SiteMask srcMask = SiteMask::lowPart(src); + unsigned cost = 0xFFFFFFFF; + Site* srcSite = 0; + for (SiteIterator it(c, value, true, includeNextWord); it.hasMore();) { + Site* s = it.next(); + unsigned v = s->copyCost(c, dstSite); + if (v == 0) { + srcSite = s; + cost = 0; + break; + } + if (not s->match(c, srcMask)) { + v += CopyPenalty; + } + if (v < cost) { + srcSite = s; + cost = v; + } + } + + if (cost) { + if (DebugMoves) { + char srcb[256]; + srcSite->toString(c, srcb, 256); + char dstb[256]; + dstSite->toString(c, dstb, 256); + fprintf(stderr, + "maybe move %s to %s for %p to %p\n", + srcb, + dstb, + value, + value); + } + + srcSite->freeze(c, value); + + value->addSite(c, dstSite); + + srcSite->thaw(c, value); + + if (not srcSite->match(c, srcMask)) { + srcSite->freeze(c, value); + dstSite->freeze(c, value); + + SiteMask tmpMask = SiteMask::lowPart(tmp); + SingleRead tmpRead(tmpMask, 0); + tmpRead.value = value; + tmpRead.successor_ = value; + + Site* tmpSite = pickTargetSite(c, &tmpRead, true); + + value->addSite(c, tmpSite); + + move(c, value, srcSite, tmpSite); + + dstSite->thaw(c, value); + srcSite->thaw(c, value); + + srcSite = tmpSite; + } + + move(c, value, srcSite, dstSite); + } + + return dstSite; +} + +Site* maybeMove(Context* c, + Value* v, + const SiteMask& mask, + bool intersectMask, + bool includeNextWord, + unsigned registerReserveCount = 0) +{ + SingleRead read(mask, 0); + read.value = v; + read.successor_ = v; + + return maybeMove( + c, &read, intersectMask, includeNextWord, registerReserveCount); +} + +Site* pickSiteOrMove(Context* c, + Read* read, + bool intersectRead, + bool includeNextWord, + unsigned registerReserveCount = 0) +{ + Site* s + = pickSourceSite(c, read, 0, 0, 0, intersectRead, true, includeNextWord); + + if (s) { + return s; + } else { + return maybeMove( + c, read, intersectRead, includeNextWord, registerReserveCount); + } +} + +Site* pickSiteOrMove(Context* c, + Value* v, + const SiteMask& mask, + bool intersectMask, + bool includeNextWord, + unsigned registerReserveCount = 0) +{ + SingleRead read(mask, 0); + read.value = v; + read.successor_ = v; + + return pickSiteOrMove( + c, &read, intersectMask, includeNextWord, registerReserveCount); +} + +void steal(Context* c, Resource* r, Value* thief) +{ + if (DebugResources) { + char resourceBuffer[256]; + r->toString(c, resourceBuffer, 256); + char siteBuffer[1024]; + sitesToString(c, r->value, siteBuffer, 1024); + fprintf(stderr, + "%p steal %s from %p (%s)\n", + thief, + resourceBuffer, + r->value, + siteBuffer); + } + + if ((not(thief and thief->isBuddyOf(r->value)) + and r->value->uniqueSite(c, r->site))) { + r->site->freeze(c, r->value); + + maybeMove(c, live(c, r->value), false, true, StealRegisterReserveCount); + + r->site->thaw(c, r->value); + } + + r->value->removeSite(c, r->site); +} + +SiteMask generalRegisterMask(Context* c) +{ + return SiteMask(lir::Operand::RegisterPairMask, + c->regFile->generalRegisters, + NoFrameIndex); +} + +SiteMask generalRegisterOrConstantMask(Context* c) +{ + return SiteMask(lir::Operand::RegisterPairMask | lir::Operand::ConstantMask, + c->regFile->generalRegisters, + NoFrameIndex); +} + +MultiRead* multiRead(Context* c) +{ + return new (c->zone) MultiRead; +} + +StubRead* stubRead(Context* c) +{ + return new (c->zone) StubRead; +} + +Site* pickSite(Context* c, + Value* v, + Site* s, + unsigned index, + bool includeNextWord) +{ + for (SiteIterator it(c, v, true, includeNextWord); it.hasMore();) { + Site* candidate = it.next(); + if (s->matchNextWord(c, candidate, index)) { + return candidate; + } + } + + return 0; +} + +Site* pickSiteOrMove(Context* c, Value* v, Site* s, unsigned index) +{ + Site* n = pickSite(c, v, s, index, false); + if (n) { + return n; + } + + return maybeMove(c, v, s->nextWordMask(c, index), true, false); +} + +Site* pickSiteOrMove(Context* c, Value* v, Site* s, Site** low, Site** high) +{ + if (v->wordIndex == 0) { + *low = s; + *high = pickSiteOrMove(c, v->nextWord, s, 1); + return *high; + } else { + *low = pickSiteOrMove(c, v->nextWord, s, 0); + *high = s; + return *low; + } +} + +Site* pickSiteOrGrow(Context* c, Value* v, Site* s, unsigned index) +{ + Site* n = pickSite(c, v, s, index, false); + if (n) { + return n; + } + + n = s->makeNextWord(c, index); + v->addSite(c, n); + return n; +} + +Site* pickSiteOrGrow(Context* c, Value* v, Site* s, Site** low, Site** high) +{ + if (v->wordIndex == 0) { + *low = s; + *high = pickSiteOrGrow(c, v->nextWord, s, 1); + return *high; + } else { + *low = pickSiteOrGrow(c, v->nextWord, s, 0); + *high = s; + return *low; + } +} + +bool isHome(Value* v, int frameIndex) +{ + Value* p = v; + do { + if (p->home == frameIndex) { + return true; + } + p = p->buddy; + } while (p != v); + + return false; +} + +bool acceptForResolve(Context* c, Site* s, Read* read, const SiteMask& mask) +{ + if (acceptMatch(c, s, read, mask) and (not s->frozen(c))) { + if (s->type(c) == lir::Operand::Type::RegisterPair) { + return c->availableGeneralRegisterCount > ResolveRegisterReserveCount; + } else { + assertT(c, + s->match(c, SiteMask(lir::Operand::MemoryMask, 0, AnyFrameIndex))); + + return isHome(read->value, + offsetToFrameIndex(c, static_cast(s)->offset)); + } + } else { + return false; + } +} + +void move(Context* c, Value* value, Site* src, Site* dst) +{ + if (DebugMoves) { + char srcb[256]; + src->toString(c, srcb, 256); + char dstb[256]; + dst->toString(c, dstb, 256); + fprintf(stderr, "move %s to %s for %p to %p\n", srcb, dstb, value, value); + } + + assertT(c, value->findSite(dst)); + + src->freeze(c, value); + dst->freeze(c, value); + + unsigned srcSize; + unsigned dstSize; + if (value->nextWord == value) { + srcSize = TargetBytesPerWord; + dstSize = TargetBytesPerWord; + } else { + srcSize = src->registerSize(c); + dstSize = dst->registerSize(c); + } + + if (srcSize == dstSize) { + apply(c, lir::Move, srcSize, src, src, dstSize, dst, dst); + } else if (srcSize > TargetBytesPerWord) { + Site* low, *high, *other = pickSiteOrGrow(c, value, dst, &low, &high); + other->freeze(c, value->nextWord); + + apply(c, lir::Move, srcSize, src, src, srcSize, low, high); + + other->thaw(c, value->nextWord); + } else { + Site* low, *high, *other = pickSiteOrMove(c, value, src, &low, &high); + other->freeze(c, value->nextWord); + + apply(c, lir::Move, dstSize, low, high, dstSize, dst, dst); + + other->thaw(c, value->nextWord); + } + + dst->thaw(c, value); + src->thaw(c, value); +} + +void asAssemblerOperand(Context* c, Site* low, Site* high, lir::Operand* result) +{ + low->asAssemblerOperand(c, high, result); +} + +class OperandUnion : public lir::Operand { + public: + // must be large enough and aligned properly to hold any operand + // type (we'd use an actual union type here, except that classes + // with constructors cannot be used in a union): + uintptr_t padding[4]; +}; + +void apply(Context* c, + lir::UnaryOperation op, + unsigned s1Size, + Site* s1Low, + Site* s1High) +{ + assertT(c, s1Low->type(c) == s1High->type(c)); + + lir::Operand::Type s1Type = s1Low->type(c); + OperandUnion s1Union; + asAssemblerOperand(c, s1Low, s1High, &s1Union); + + c->assembler->apply(op, OperandInfo(s1Size, s1Type, &s1Union)); +} + +void apply(Context* c, + lir::BinaryOperation op, + unsigned s1Size, + Site* s1Low, + Site* s1High, + unsigned s2Size, + Site* s2Low, + Site* s2High) +{ + assertT(c, s1Low->type(c) == s1High->type(c)); + assertT(c, s2Low->type(c) == s2High->type(c)); + + lir::Operand::Type s1Type = s1Low->type(c); + OperandUnion s1Union; + asAssemblerOperand(c, s1Low, s1High, &s1Union); + + lir::Operand::Type s2Type = s2Low->type(c); + OperandUnion s2Union; + asAssemblerOperand(c, s2Low, s2High, &s2Union); + + c->assembler->apply(op, + OperandInfo(s1Size, s1Type, &s1Union), + OperandInfo(s2Size, s2Type, &s2Union)); +} + +void apply(Context* c, + lir::TernaryOperation op, + unsigned s1Size, + Site* s1Low, + Site* s1High, + unsigned s2Size, + Site* s2Low, + Site* s2High, + unsigned s3Size, + Site* s3Low, + Site* s3High) +{ + assertT(c, s1Low->type(c) == s1High->type(c)); + assertT(c, s2Low->type(c) == s2High->type(c)); + assertT(c, s3Low->type(c) == s3High->type(c)); + + lir::Operand::Type s1Type = s1Low->type(c); + OperandUnion s1Union; + asAssemblerOperand(c, s1Low, s1High, &s1Union); + + lir::Operand::Type s2Type = s2Low->type(c); + OperandUnion s2Union; + asAssemblerOperand(c, s2Low, s2High, &s2Union); + + lir::Operand::Type s3Type = s3Low->type(c); + OperandUnion s3Union; + asAssemblerOperand(c, s3Low, s3High, &s3Union); + + c->assembler->apply(op, + OperandInfo(s1Size, s1Type, &s1Union), + OperandInfo(s2Size, s2Type, &s2Union), + OperandInfo(s3Size, s3Type, &s3Union)); +} + +void append(Context* c, Event* e); + +void saveLocals(Context* c, Event* e) +{ + for (unsigned li = 0; li < c->localFootprint; ++li) { + Local* local = e->localsBefore + li; + if (local->value) { + if (DebugReads) { + fprintf(stderr, + "local save read %p at %d of %d\n", + local->value, + compiler::frameIndex(c, li), + totalFrameSize(c)); + } + + e->addRead( + c, + local->value, + SiteMask(lir::Operand::MemoryMask, 0, compiler::frameIndex(c, li))); + } + } +} + +void maybeMove(Context* c, + lir::BinaryOperation op, + unsigned srcSize, + unsigned srcSelectSize, + Value* srcValue, + unsigned dstSize, + Value* dstValue, + const SiteMask& dstMask) +{ + Read* read = live(c, dstValue); + bool isStore = read == 0; + + Site* target; + if (dstValue->target) { + target = dstValue->target; + } else if (isStore) { + return; + } else { + target = pickTargetSite(c, read); + } + + unsigned cost = srcValue->source->copyCost(c, target); + + if (srcSelectSize < dstSize) + cost = 1; + + if (cost) { + // todo: let c->arch->planMove decide this: + bool useTemporary = ((target->type(c) == lir::Operand::Type::Memory + and srcValue->source->type(c) == lir::Operand::Type::Memory) + or (srcSelectSize < dstSize + and target->type(c) != lir::Operand::Type::RegisterPair)); + + srcValue->source->freeze(c, srcValue); + + dstValue->addSite(c, target); + + srcValue->source->thaw(c, srcValue); + + bool addOffset = srcSize != srcSelectSize and c->arch->bigEndian() + and srcValue->source->type(c) == lir::Operand::Type::Memory; + + if (addOffset) { + static_cast(srcValue->source)->offset + += (srcSize - srcSelectSize); + } + + target->freeze(c, dstValue); + + if (target->match(c, dstMask) and not useTemporary) { + if (DebugMoves) { + char srcb[256]; + srcValue->source->toString(c, srcb, 256); + char dstb[256]; + target->toString(c, dstb, 256); + fprintf(stderr, + "move %s to %s for %p to %p\n", + srcb, + dstb, + srcValue, + dstValue); + } + + srcValue->source->freeze(c, srcValue); + + apply(c, + op, + min(srcSelectSize, dstSize), + srcValue->source, + srcValue->source, + dstSize, + target, + target); + + srcValue->source->thaw(c, srcValue); + } else { + // pick a temporary register which is valid as both a + // destination and a source for the moves we need to perform: + + dstValue->removeSite(c, target); + + bool thunk; + OperandMask src; + + c->arch->planSource(op, dstSize, src, dstSize, &thunk); + + if (isGeneralValue(srcValue)) { + src.lowRegisterMask &= c->regFile->generalRegisters; + } + + assertT(c, thunk == 0); + assertT(c, dstMask.typeMask & src.typeMask & lir::Operand::RegisterPairMask); + + Site* tmpTarget + = freeRegisterSite(c, dstMask.registerMask & src.lowRegisterMask); + + srcValue->source->freeze(c, srcValue); + + dstValue->addSite(c, tmpTarget); + + tmpTarget->freeze(c, dstValue); + + if (DebugMoves) { + char srcb[256]; + srcValue->source->toString(c, srcb, 256); + char dstb[256]; + tmpTarget->toString(c, dstb, 256); + fprintf(stderr, + "move %s to %s for %p to %p\n", + srcb, + dstb, + srcValue, + dstValue); + } + + apply(c, + op, + srcSelectSize, + srcValue->source, + srcValue->source, + dstSize, + tmpTarget, + tmpTarget); + + tmpTarget->thaw(c, dstValue); + + srcValue->source->thaw(c, srcValue); + + if (useTemporary or isStore) { + if (DebugMoves) { + char srcb[256]; + tmpTarget->toString(c, srcb, 256); + char dstb[256]; + target->toString(c, dstb, 256); + fprintf(stderr, + "move %s to %s for %p to %p\n", + srcb, + dstb, + srcValue, + dstValue); + } + + dstValue->addSite(c, target); + + tmpTarget->freeze(c, dstValue); + + apply(c, + lir::Move, + dstSize, + tmpTarget, + tmpTarget, + dstSize, + target, + target); + + tmpTarget->thaw(c, dstValue); + + if (isStore) { + dstValue->removeSite(c, tmpTarget); + } + } + } + + target->thaw(c, dstValue); + + if (addOffset) { + static_cast(srcValue->source)->offset + -= (srcSize - srcSelectSize); + } + } else { + target = srcValue->source; + + if (DebugMoves) { + char dstb[256]; + target->toString(c, dstb, 256); + fprintf( + stderr, "null move in %s for %p to %p\n", dstb, srcValue, dstValue); + } + } + + if (isStore) { + dstValue->removeSite(c, target); + } +} + +Site* pickMatchOrMove(Context* c, + Read* r, + Site* nextWord, + unsigned index, + bool intersectRead) +{ + Site* s = pickSite(c, r->value, nextWord, index, true); + SiteMask mask; + if (intersectRead) { + r->intersect(&mask); + } + if (s and s->match(c, mask)) { + return s; + } + + return pickSiteOrMove(c, + r->value, + mask.intersectionWith(nextWord->nextWordMask(c, index)), + true, + true); +} + +Site* pickSiteOrMove(Context* c, + Value* src, + Value* dst, + Site* nextWord, + unsigned index) +{ + if (live(c, dst)) { + Read* read = live(c, src); + Site* s; + if (nextWord) { + s = pickMatchOrMove(c, read, nextWord, index, false); + } else { + s = pickSourceSite(c, read, 0, 0, 0, false, true, true); + + if (s == 0 or s->isVolatile(c)) { + s = maybeMove(c, read, false, true); + } + } + assertT(c, s); + + addBuddy(src, dst); + + if (src->source->isVolatile(c)) { + src->removeSite(c, src->source); + } + + return s; + } else { + return 0; + } +} + +ConstantSite* findConstantSite(Context* c, Value* v); + +Site* getTarget(Context* c, + Value* value, + Value* result, + const SiteMask& resultMask); + +void freezeSource(Context* c, unsigned size, Value* v); + +void thawSource(Context* c, unsigned size, Value* v); + +void removeBuddy(Context* c, Value* v) +{ + if (v->buddy != v) { + if (DebugBuddies) { + fprintf(stderr, "remove buddy %p from", v); + for (Value* p = v->buddy; p != v; p = p->buddy) { + fprintf(stderr, " %p", p); + } + fprintf(stderr, "\n"); + } + + assertT(c, v->buddy); + + Value* next = v->buddy; + v->buddy = v; + Value* p = next; + while (p->buddy != v) + p = p->buddy; + p->buddy = next; + + assertT(c, p->buddy); + + if (not live(c, next)) { + next->clearSites(c); + } + + if (not live(c, v)) { + v->clearSites(c); + } + } +} + +Site* copy(Context* c, Site* s) +{ + Site* start = 0; + Site* end = 0; + for (; s; s = s->next) { + Site* n = s->copy(c); + if (end) { + end->next = n; + } else { + start = n; + } + end = n; + } + return start; +} + +class Snapshot { + public: + Snapshot(Context* c, Value* value, Snapshot* next) + : value(value), + buddy(value->buddy), + sites(copy(c, value->sites)), + next(next) + { + } + + Value* value; + Value* buddy; + Site* sites; + Snapshot* next; +}; + +Snapshot* snapshot(Context* c, Value* value, Snapshot* next) +{ + if (DebugControl) { + char buffer[256]; + sitesToString(c, value->sites, buffer, 256); + fprintf( + stderr, "snapshot %p buddy %p sites %s\n", value, value->buddy, buffer); + } + + return new (c->zone) Snapshot(c, value, next); +} + +Snapshot* makeSnapshots(Context* c, Value* value, Snapshot* next) +{ + next = snapshot(c, value, next); + for (Value* p = value->buddy; p != value; p = p->buddy) { + next = snapshot(c, p, next); + } + return next; +} + +Value* maybeBuddy(Context* c, Value* v); + +Value* pushWord(Context* c, Value* v) +{ + if (v) { + v = maybeBuddy(c, v); + } + + Stack* s = stack(c, v, c->stack); + + if (DebugFrame) { + fprintf(stderr, "push %p\n", v); + } + + if (v) { + v->home = frameIndex(c, s->index + c->localFootprint); + } + c->stack = s; + + return v; +} + +void push(Context* c, unsigned footprint, Value* v) +{ + assertT(c, footprint); + + bool bigEndian = c->arch->bigEndian(); + + Value* low = v; + + if (bigEndian) { + v = pushWord(c, v); + } + + Value* high; + if (footprint > 1) { + assertT(c, footprint == 2); + + if (TargetBytesPerWord == 4) { + low->maybeSplit(c); + high = pushWord(c, low->nextWord); + } else { + high = pushWord(c, 0); + } + } else { + high = 0; + } + + if (not bigEndian) { + v = pushWord(c, v); + } + + if (high) { + v->nextWord = high; + high->nextWord = v; + high->wordIndex = 1; + } +} + +void popWord(Context* c) +{ + Stack* s = c->stack; + assertT(c, s->value == 0 or s->value->home >= 0); + + if (DebugFrame) { + fprintf(stderr, "pop %p\n", s->value); + } + + c->stack = s->next; +} + +Value* pop(Context* c, unsigned footprint) +{ + assertT(c, footprint); + + Stack* s = 0; + + bool bigEndian = c->arch->bigEndian(); + + if (not bigEndian) { + s = c->stack; + } + + if (footprint > 1) { + assertT(c, footprint == 2); + +#ifndef NDEBUG + Stack* low; + Stack* high; + if (bigEndian) { + high = c->stack; + low = high->next; + } else { + low = c->stack; + high = low->next; + } + + assertT( + c, + (TargetBytesPerWord == 8 and low->value->nextWord == low->value + and high->value == 0) + or (TargetBytesPerWord == 4 and low->value->nextWord == high->value)); +#endif // not NDEBUG + + popWord(c); + } + + if (bigEndian) { + s = c->stack; + } + + popWord(c); + + return s->value; +} + +Value* storeLocal(Context* c, + unsigned footprint, + Value* v, + unsigned index, + bool copy) +{ + assertT(c, index + footprint <= c->localFootprint); + + if (copy) { + unsigned sizeInBytes = sizeof(Local) * c->localFootprint; + Local* newLocals = static_cast(c->zone->allocate(sizeInBytes)); + memcpy(newLocals, c->locals, sizeInBytes); + c->locals = newLocals; + } + + Value* high; + if (footprint > 1) { + assertT(c, footprint == 2); + + unsigned highIndex; + unsigned lowIndex; + if (c->arch->bigEndian()) { + highIndex = index + 1; + lowIndex = index; + } else { + lowIndex = index + 1; + highIndex = index; + } + + if (TargetBytesPerWord == 4) { + assertT(c, v->nextWord != v); + + high = storeLocal(c, 1, v->nextWord, highIndex, false); + } else { + high = 0; + } + + index = lowIndex; + } else { + high = 0; + } + + v = maybeBuddy(c, v); + + if (high != 0) { + v->nextWord = high; + high->nextWord = v; + high->wordIndex = 1; + } + + Local* local = c->locals + index; + local->value = v; + + if (DebugFrame) { + fprintf(stderr, "store local %p at %d\n", local->value, index); + } + + local->value->home = frameIndex(c, index); + + return v; +} + +unsigned typeFootprint(Context* c, ir::Type type) +{ + // TODO: this function is very Java-specific in nature. Generalize. + switch (type.flavor()) { + case ir::Type::Float: + case ir::Type::Integer: + return type.rawSize() / 4; + case ir::Type::Object: + case ir::Type::Address: + return 1; + case ir::Type::Void: + return 0; + default: + abort(c); + } +} + +Value* loadLocal(Context* c, ir::Type type, unsigned index) +{ + unsigned footprint = typeFootprint(c, type); + assertT(c, index + footprint <= c->localFootprint); + + if (footprint > 1) { + assertT(c, footprint == 2); + + if (not c->arch->bigEndian()) { + ++index; + } + } + + assertT(c, c->locals[index].value); + assertT(c, c->locals[index].value->home >= 0); + + if (DebugFrame) { + fprintf(stderr, "load local %p at %d\n", c->locals[index].value, index); + } + + return c->locals[index].value; +} + +Value* threadRegister(Context* c) +{ + Site* s = registerSite(c, c->arch->thread()); + return value(c, ir::Type::addr(), s, s); +} + +unsigned frameFootprint(Context* c, Stack* s) +{ + return c->localFootprint + (s ? (s->index + 1) : 0); +} + +void visit(Context* c, Link* link) +{ + if (false) { + fprintf(stderr, + "visit link from %d to %d fork %p junction %p\n", + link->predecessor->logicalInstruction->index, + link->successor->logicalInstruction->index, + link->forkState, + link->junctionState); + } + + ForkState* forkState = link->forkState; + if (forkState) { + for (unsigned i = 0; i < forkState->readCount; ++i) { + ForkElement* p = forkState->elements + i; + Value* v = p->value; + v->reads = p->read->nextTarget(); + if (false) { + fprintf(stderr, "next read %p for %p from %p\n", v->reads, v, p->read); + } + if (not live(c, v)) { + v->clearSites(c); + } + } + } + + JunctionState* junctionState = link->junctionState; + if (junctionState) { + for (unsigned i = 0; i < junctionState->frameFootprint; ++i) { + StubReadPair* p = junctionState->reads + i; + + if (p->value and p->value->reads) { + assertT(c, p->value->reads == p->read); + popRead(c, 0, p->value); + } + } + } +} + +class BuddyEvent : public Event { + public: + BuddyEvent(Context* c, Value* original, Value* buddy) + : Event(c), original(original), buddy(buddy) + { + this->addRead(c, original, SiteMask(~0, ~0, AnyFrameIndex), buddy); + } + + virtual const char* name() + { + return "BuddyEvent"; + } + + virtual void compile(Context* c) + { + if (DebugBuddies) { + fprintf(stderr, "original %p buddy %p\n", original, buddy); + } + + assertT(c, original->hasSite(c)); + + assertT(c, original); + assertT(c, buddy); + + addBuddy(original, buddy); + + popRead(c, this, original); + } + + Value* original; + Value* buddy; +}; + +void appendBuddy(Context* c, Value* original, Value* buddy) +{ + append(c, new (c->zone) BuddyEvent(c, original, buddy)); +} + +void append(Context* c, Event* e) +{ + LogicalInstruction* i = c->logicalCode[c->logicalIp]; + if (c->stack != i->stack or c->locals != i->locals) { + appendDummy(c); + } + + if (DebugAppend) { + fprintf(stderr, + " -- append %s at %d with %d stack before\n", + e->name(), + e->logicalInstruction->index, + c->stack ? c->stack->index + 1 : 0); + } + + if (c->lastEvent) { + c->lastEvent->next = e; + } else { + c->firstEvent = e; + } + c->lastEvent = e; + + Event* p = c->predecessor; + if (p) { + if (DebugAppend) { + fprintf(stderr, + "%d precedes %d\n", + p->logicalInstruction->index, + e->logicalInstruction->index); + } + + Link* link + = compiler::link(c, p, e->predecessors, e, p->successors, c->forkState); + e->predecessors = link; + p->successors = link; + } + c->forkState = 0; + + c->predecessor = e; + + if (e->logicalInstruction->firstEvent == 0) { + e->logicalInstruction->firstEvent = e; + } + e->logicalInstruction->lastEvent = e; +} + +Site* readSource(Context* c, Read* r) +{ + Value* v = r->value; + + if (DebugReads) { + char buffer[1024]; + sitesToString(c, v, buffer, 1024); + fprintf(stderr, "read source for %p from %s\n", v, buffer); + } + + if (not v->hasSite(c)) { + if (DebugReads) { + fprintf(stderr, "no sites found for %p\n", v); + } + return 0; + } + + Value* high = r->high(c); + if (high) { + return pickMatchOrMove(c, r, high->source, 0, true); + } else { + return pickSiteOrMove(c, r, true, true); + } +} + +void propagateJunctionSites(Context* c, Event* e, Site** sites) +{ + for (Link* pl = e->predecessors; pl; pl = pl->nextPredecessor) { + Event* p = pl->predecessor; + if (p->junctionSites == 0) { + p->junctionSites = sites; + for (Link* sl = p->successors; sl; sl = sl->nextSuccessor) { + Event* s = sl->successor; + propagateJunctionSites(c, s, sites); + } + } + } +} + +void propagateJunctionSites(Context* c, Event* e) +{ + for (Link* sl = e->successors; sl; sl = sl->nextSuccessor) { + Event* s = sl->successor; + if (s->predecessors->nextPredecessor) { + unsigned size = sizeof(Site*) * frameFootprint(c, e->stackAfter); + Site** junctionSites = static_cast(c->zone->allocate(size)); + memset(junctionSites, 0, size); + + propagateJunctionSites(c, s, junctionSites); + break; + } + } +} + +class SiteRecord { + public: + Site* site; + Value* value; +}; + +void init(SiteRecord* r, Site* s, Value* v) +{ + r->site = s; + r->value = v; +} + +class SiteRecordList { + public: + SiteRecordList(SiteRecord* records, unsigned capacity) + : records(records), index(0), capacity(capacity) + { + } + + SiteRecord* records; + unsigned index; + unsigned capacity; +}; + +void freeze(Context* c, SiteRecordList* frozen, Site* s, Value* v) +{ + assertT(c, frozen->index < frozen->capacity); + + s->freeze(c, v); + init(new (frozen->records + (frozen->index++)) SiteRecord, s, v); +} + +void thaw(Context* c, SiteRecordList* frozen) +{ + while (frozen->index) { + SiteRecord* sr = frozen->records + (--frozen->index); + sr->site->thaw(c, sr->value); + } +} + +bool resolveOriginalSites(Context* c, + Event* e, + SiteRecordList* frozen, + Site** sites) +{ + bool complete = true; + for (FrameIterator it(c, e->stackAfter, e->localsAfter, true); + it.hasMore();) { + FrameIterator::Element el = it.next(c); + Value* v = el.value; + Read* r = v ? live(c, v) : 0; + Site* s = sites[el.localIndex]; + + if (r) { + if (s) { + if (DebugControl) { + char buffer[256]; + s->toString(c, buffer, 256); + fprintf(stderr, + "resolve original %s for %p local %d frame %d\n", + buffer, + v, + el.localIndex, + el.frameIndex(c)); + } + + Site* target = pickSiteOrMove( + c, v, s->mask(c), true, true, ResolveRegisterReserveCount); + + freeze(c, frozen, target, v); + } else { + complete = false; + } + } else if (s) { + if (DebugControl) { + char buffer[256]; + s->toString(c, buffer, 256); + fprintf(stderr, + "freeze original %s for %p local %d frame %d\n", + buffer, + v, + el.localIndex, + el.frameIndex(c)); + } + + Value dummy(0, 0, ir::Type::addr()); + dummy.addSite(c, s); + dummy.removeSite(c, s); + freeze(c, frozen, s, 0); + } + } + + return complete; +} + +bool resolveSourceSites(Context* c, + Event* e, + SiteRecordList* frozen, + Site** sites) +{ + bool complete = true; + for (FrameIterator it(c, e->stackAfter, e->localsAfter); it.hasMore();) { + FrameIterator::Element el = it.next(c); + Value* v = el.value; + Read* r = live(c, v); + + if (r and sites[el.localIndex] == 0) { + SiteMask mask(lir::Operand::RegisterPairMask | lir::Operand::MemoryMask, + c->regFile->generalRegisters, + AnyFrameIndex); + + Site* s = pickSourceSite( + c, r, 0, 0, &mask, true, false, true, acceptForResolve); + + if (s) { + if (DebugControl) { + char buffer[256]; + s->toString(c, buffer, 256); + fprintf(stderr, + "resolve source %s from %p local %d frame %d\n", + buffer, + v, + el.localIndex, + el.frameIndex(c)); + } + + freeze(c, frozen, s, v); + + sites[el.localIndex] = s->copy(c); + } else { + complete = false; + } + } + } + + return complete; +} + +void resolveTargetSites(Context* c, + Event* e, + SiteRecordList* frozen, + Site** sites) +{ + for (FrameIterator it(c, e->stackAfter, e->localsAfter); it.hasMore();) { + FrameIterator::Element el = it.next(c); + Value* v = el.value; + Read* r = live(c, v); + + if (r and sites[el.localIndex] == 0) { + SiteMask mask(lir::Operand::RegisterPairMask | lir::Operand::MemoryMask, + c->regFile->generalRegisters, + AnyFrameIndex); + + Site* s = pickSourceSite( + c, r, 0, 0, &mask, false, true, true, acceptForResolve); + + if (s == 0) { + s = maybeMove(c, v, mask, false, true, ResolveRegisterReserveCount); + } + + freeze(c, frozen, s, v); + + sites[el.localIndex] = s->copy(c); + + if (DebugControl) { + char buffer[256]; + sites[el.localIndex]->toString(c, buffer, 256); + fprintf(stderr, + "resolve target %s for %p local %d frame %d\n", + buffer, + el.value, + el.localIndex, + el.frameIndex(c)); + } + } + } +} + +void resolveJunctionSites(Context* c, Event* e, SiteRecordList* frozen) +{ + bool complete; + if (e->junctionSites) { + complete = resolveOriginalSites(c, e, frozen, e->junctionSites); + } else { + propagateJunctionSites(c, e); + complete = false; + } + + if (e->junctionSites) { + if (not complete) { + complete = resolveSourceSites(c, e, frozen, e->junctionSites); + if (not complete) { + resolveTargetSites(c, e, frozen, e->junctionSites); + } + } + + if (DebugControl) { + fprintf(stderr, + "resolved junction sites %p at %d\n", + e->junctionSites, + e->logicalInstruction->index); + } + } +} + +void resolveBranchSites(Context* c, Event* e, SiteRecordList* frozen) +{ + if (e->successors->nextSuccessor and e->junctionSites == 0) { + unsigned footprint = frameFootprint(c, e->stackAfter); + RUNTIME_ARRAY(Site*, branchSites, footprint); + memset(RUNTIME_ARRAY_BODY(branchSites), 0, sizeof(Site*) * footprint); + + if (not resolveSourceSites(c, e, frozen, RUNTIME_ARRAY_BODY(branchSites))) { + resolveTargetSites(c, e, frozen, RUNTIME_ARRAY_BODY(branchSites)); + } + } +} + +void captureBranchSnapshots(Context* c, Event* e) +{ + if (e->successors->nextSuccessor) { + for (FrameIterator it(c, e->stackAfter, e->localsAfter); it.hasMore();) { + FrameIterator::Element el = it.next(c); + e->snapshots = makeSnapshots(c, el.value, e->snapshots); + } + + for (List* sv = e->successors->forkState->saved; sv; + sv = sv->next) { + e->snapshots = makeSnapshots(c, sv->item, e->snapshots); + } + + if (DebugControl) { + fprintf(stderr, + "captured snapshots %p at %d\n", + e->snapshots, + e->logicalInstruction->index); + } + } +} + +void populateSiteTables(Context* c, Event* e, SiteRecordList* frozen) +{ + resolveJunctionSites(c, e, frozen); + + resolveBranchSites(c, e, frozen); +} + +void setSites(Context* c, Value* v, Site* s) +{ + assertT(c, live(c, v)); + + for (; s; s = s->next) { + v->addSite(c, s->copy(c)); + } + + if (DebugControl) { + char buffer[256]; + sitesToString(c, v->sites, buffer, 256); + fprintf(stderr, "set sites %s for %p\n", buffer, v); + } +} + +void resetFrame(Context* c, Event* e) +{ + for (FrameIterator it(c, e->stackBefore, e->localsBefore); it.hasMore();) { + FrameIterator::Element el = it.next(c); + el.value->clearSites(c); + } + + while (c->acquiredResources) { + c->acquiredResources->value->clearSites(c); + } +} + +void setSites(Context* c, Event* e, Site** sites) +{ + resetFrame(c, e); + + for (FrameIterator it(c, e->stackBefore, e->localsBefore); it.hasMore();) { + FrameIterator::Element el = it.next(c); + if (sites[el.localIndex]) { + if (live(c, el.value)) { + setSites(c, el.value, sites[el.localIndex]); + } else if (DebugControl) { + char buffer[256]; + sitesToString(c, sites[el.localIndex], buffer, 256); + fprintf(stderr, + "skip sites %s for %p local %d frame %d\n", + buffer, + el.value, + el.localIndex, + el.frameIndex(c)); + } + } else if (DebugControl) { + fprintf(stderr, + "no sites for %p local %d frame %d\n", + el.value, + el.localIndex, + el.frameIndex(c)); + } + } +} + +void removeBuddies(Context* c) +{ + for (FrameIterator it(c, c->stack, c->locals); it.hasMore();) { + FrameIterator::Element el = it.next(c); + removeBuddy(c, el.value); + } +} + +void restore(Context* c, Event* e, Snapshot* snapshots) +{ + for (Snapshot* s = snapshots; s; s = s->next) { + Value* v = s->value; + Value* next = v->buddy; + if (v != next) { + v->buddy = v; + Value* p = next; + while (p->buddy != v) + p = p->buddy; + p->buddy = next; + } + } + + for (Snapshot* s = snapshots; s; s = s->next) { + assertT(c, s->buddy); + + s->value->buddy = s->buddy; + } + + resetFrame(c, e); + + for (Snapshot* s = snapshots; s; s = s->next) { + if (live(c, s->value)) { + if (live(c, s->value) and s->sites and s->value->sites == 0) { + setSites(c, s->value, s->sites); + } + } + + if (false) { + char buffer[256]; + sitesToString(c, s->sites, buffer, 256); + fprintf(stderr, + "restore %p buddy %p sites %s live %p\n", + s->value, + s->value->buddy, + buffer, + live(c, s->value)); + } + } +} + +void populateSources(Context* c, Event* e) +{ + RUNTIME_ARRAY(SiteRecord, frozenRecords, e->readCount); + SiteRecordList frozen(RUNTIME_ARRAY_BODY(frozenRecords), e->readCount); + + for (Read* r = e->reads; r; r = r->eventNext) { + r->value->source = readSource(c, r); + if (r->value->source) { + if (DebugReads) { + char buffer[256]; + r->value->source->toString(c, buffer, 256); + fprintf(stderr, "freeze source %s for %p\n", buffer, r->value); + } + + freeze(c, &frozen, r->value->source, r->value); + } + } + + thaw(c, &frozen); +} + +void setStubRead(Context* c, StubReadPair* p, Value* v) +{ + if (v) { + StubRead* r = stubRead(c); + if (DebugReads) { + fprintf(stderr, "add stub read %p to %p\n", r, v); + } + finishAddRead(c, v, r); + + p->value = v; + p->read = r; + } +} + +void populateJunctionReads(Context* c, Link* link) +{ + JunctionState* state = new ( + c->zone->allocate(sizeof(JunctionState) + + (sizeof(StubReadPair) * frameFootprint(c, c->stack)))) + JunctionState(frameFootprint(c, c->stack)); + + memset(state->reads, 0, sizeof(StubReadPair) * frameFootprint(c, c->stack)); + + link->junctionState = state; + + for (FrameIterator it(c, c->stack, c->locals); it.hasMore();) { + FrameIterator::Element e = it.next(c); + setStubRead(c, state->reads + e.localIndex, e.value); + } +} + +void updateJunctionReads(Context* c, JunctionState* state) +{ + for (FrameIterator it(c, c->stack, c->locals); it.hasMore();) { + FrameIterator::Element e = it.next(c); + StubReadPair* p = state->reads + e.localIndex; + if (p->value and p->read->read == 0) { + Read* r = live(c, e.value); + if (r) { + if (DebugReads) { + fprintf( + stderr, "stub read %p for %p valid: %p\n", p->read, p->value, r); + } + p->read->read = r; + } + } + } + + for (unsigned i = 0; i < frameFootprint(c, c->stack); ++i) { + StubReadPair* p = state->reads + i; + if (p->value and p->read->read == 0) { + if (DebugReads) { + fprintf(stderr, "stub read %p for %p invalid\n", p->read, p->value); + } + p->read->valid_ = false; + } + } +} + +void compile(Context* c, + uintptr_t stackOverflowHandler, + unsigned stackLimitOffset) +{ + if (c->logicalCode[c->logicalIp]->lastEvent == 0) { + appendDummy(c); + } + + Assembler* a = c->assembler; + + Block* firstBlock = block(c, c->firstEvent); + Block* block = firstBlock; + + if (stackOverflowHandler) { + a->checkStackOverflow(stackOverflowHandler, stackLimitOffset); + } + + a->allocateFrame(c->alignedFrameSize); + + for (Event* e = c->firstEvent; e; e = e->next) { + if (DebugCompile) { + fprintf(stderr, + " -- compile %s at %d with %d preds %d succs %d stack\n", + e->name(), + e->logicalInstruction->index, + e->predecessors->countPredecessors(), + e->successors->countSuccessors(), + e->stackBefore ? e->stackBefore->index + 1 : 0); + } + + e->block = block; + + c->stack = e->stackBefore; + c->locals = e->localsBefore; + + if (e->logicalInstruction->machineOffset == 0) { + e->logicalInstruction->machineOffset = a->offset(); + } + + if (e->predecessors) { + visit(c, e->predecessors->lastPredecessor()); + + Event* first = e->predecessors->predecessor; + if (e->predecessors->nextPredecessor) { + for (Link* pl = e->predecessors; pl->nextPredecessor; + pl = pl->nextPredecessor) { + updateJunctionReads(c, pl->junctionState); + } + + if (DebugControl) { + fprintf(stderr, + "set sites to junction sites %p at %d\n", + first->junctionSites, + first->logicalInstruction->index); + } + + setSites(c, e, first->junctionSites); + removeBuddies(c); + } else if (first->successors->nextSuccessor) { + if (DebugControl) { + fprintf(stderr, + "restore snapshots %p at %d\n", + first->snapshots, + first->logicalInstruction->index); + } + + restore(c, e, first->snapshots); + } + } + + unsigned footprint = frameFootprint(c, e->stackAfter); + RUNTIME_ARRAY(SiteRecord, frozenRecords, footprint); + SiteRecordList frozen(RUNTIME_ARRAY_BODY(frozenRecords), footprint); + + bool branch = e->isBranch(); + if (branch and e->successors) { + populateSiteTables(c, e, &frozen); + } + + populateSources(c, e); + + if (branch and e->successors) { + captureBranchSnapshots(c, e); + } + + thaw(c, &frozen); + + e->compile(c); + + if ((not branch) and e->successors) { + populateSiteTables(c, e, &frozen); + captureBranchSnapshots(c, e); + thaw(c, &frozen); + } + + if (e->visitLinks) { + for (List* cell = reverseDestroy(e->visitLinks); cell; + cell = cell->next) { + visit(c, cell->item); + } + e->visitLinks = 0; + } + + for (CodePromise* p = e->promises; p; p = p->next) { + p->offset = a->offset(); + } + + a->endEvent(); + + LogicalInstruction* nextInstruction = e->logicalInstruction->next(c); + if (e->next == 0 or (e->next->logicalInstruction != e->logicalInstruction + and (e->next->logicalInstruction != nextInstruction + or e != e->logicalInstruction->lastEvent))) { + Block* b = e->logicalInstruction->firstEvent->block; + + while (b->nextBlock) { + b = b->nextBlock; + } + + if (b != block) { + b->nextBlock = block; + } + + block->nextInstruction = nextInstruction; + block->assemblerBlock = a->endBlock(e->next != 0); + + if (e->next) { + block = compiler::block(c, e->next); + } + } + } + + c->firstBlock = firstBlock; +} + +void restore(Context* c, ForkState* state) +{ + for (unsigned i = 0; i < state->readCount; ++i) { + ForkElement* p = state->elements + i; + p->value->lastRead = p->read; + p->read->allocateTarget(c); + } +} + +void addForkElement(Context* c, Value* v, ForkState* state, unsigned index) +{ + MultiRead* r = multiRead(c); + if (DebugReads) { + fprintf(stderr, "add multi read %p to %p\n", r, v); + } + finishAddRead(c, v, r); + + ForkElement* p = state->elements + index; + p->value = v; + p->read = r; +} + +ForkState* saveState(Context* c) +{ + if (c->logicalCode[c->logicalIp]->lastEvent == 0) { + appendDummy(c); + } + + unsigned elementCount = frameFootprint(c, c->stack) + c->saved->count(); + + ForkState* state = new (c->zone->allocate( + sizeof(ForkState) + (sizeof(ForkElement) * elementCount))) + ForkState(c->stack, c->locals, c->saved, c->predecessor, c->logicalIp); + + if (c->predecessor) { + c->forkState = state; + + unsigned count = 0; + + for (FrameIterator it(c, c->stack, c->locals); it.hasMore();) { + FrameIterator::Element e = it.next(c); + addForkElement(c, e.value, state, count++); + } + + for (List* sv = c->saved; sv; sv = sv->next) { + addForkElement(c, sv->item, state, count++); + } + + state->readCount = count; + } + + c->saved = 0; + + return state; +} + +void restoreState(Context* c, ForkState* s) +{ + if (c->logicalCode[c->logicalIp]->lastEvent == 0) { + appendDummy(c); + } + + c->stack = s->stack; + c->locals = s->locals; + c->predecessor = s->predecessor; + c->logicalIp = s->logicalIp; + + if (c->predecessor) { + c->forkState = s; + restore(c, s); + } +} + +Value* maybeBuddy(Context* c, Value* v) +{ + if (v->home >= 0) { + Value* n = value(c, v->type); + appendBuddy(c, v, n); + return n; + } else { + return v; + } +} + +void linkLocals(Context* c, Local* oldLocals, Local* newLocals) +{ + for (int i = 0; i < static_cast(c->localFootprint); ++i) { + Local* local = oldLocals + i; + if (local->value) { + int highOffset = c->arch->bigEndian() ? 1 : -1; + + if (i + highOffset >= 0 + and i + highOffset < static_cast(c->localFootprint) + and local->value->nextWord == local[highOffset].value) { + Value* v = newLocals[i].value; + Value* next = newLocals[i + highOffset].value; + v->nextWord = next; + next->nextWord = v; + next->wordIndex = 1; + } + } + } +} + +class Client : public Assembler::Client { + public: + Client(Context* c) : c(c) + { + } + + virtual Register acquireTemporary(RegisterMask mask) + { + unsigned cost; + Register r = pickRegisterTarget(c, 0, mask, &cost); + expect(c, cost < Target::Impossible); + save(r); + c->registerResources[r.index()].increment(c); + return r; + } + + virtual void releaseTemporary(Register r) + { + c->registerResources[r.index()].decrement(c); + } + + virtual void save(Register r) + { + RegisterResource* reg = c->registerResources + r.index(); + + assertT(c, reg->referenceCount == 0); + assertT(c, reg->freezeCount == 0); + assertT(c, not reg->reserved); + + if (reg->value) { + steal(c, reg, 0); + } + } + + Context* c; +}; + +class MyCompiler : public Compiler { + public: + MyCompiler(System* s, + Assembler* assembler, + Zone* zone, + Compiler::Client* compilerClient) + : c(s, assembler, zone, compilerClient), client(&c) + { + assembler->setClient(&client); + } + + virtual State* saveState() + { + State* s = compiler::saveState(&c); + restoreState(s); + return s; + } + + virtual void restoreState(State* state) + { + compiler::restoreState(&c, static_cast(state)); + } + + virtual void init(unsigned logicalCodeLength, + unsigned parameterFootprint, + unsigned localFootprint, + unsigned alignedFrameSize) + { + c.parameterFootprint = parameterFootprint; + c.localFootprint = localFootprint; + c.alignedFrameSize = alignedFrameSize; + + unsigned frameResourceCount = totalFrameSize(&c); + + c.frameResources = static_cast( + c.zone->allocate(sizeof(FrameResource) * frameResourceCount)); + + for (unsigned i = 0; i < frameResourceCount; ++i) { + new (c.frameResources + i) FrameResource; + } + + unsigned base = frameBase(&c); + c.frameResources[base + c.arch->returnAddressOffset()].reserved = true; + c.frameResources[base + c.arch->framePointerOffset()].reserved + = UseFramePointer; + + c.logicalCode.init(c.zone, logicalCodeLength); + + c.logicalCode[-1] = new (c.zone) LogicalInstruction(-1, c.stack, c.locals); + + c.locals + = static_cast(c.zone->allocate(sizeof(Local) * localFootprint)); + + memset(c.locals, 0, sizeof(Local) * localFootprint); + } + + virtual void extendLogicalCode(unsigned more) + { + c.logicalCode.extend(c.zone, more); + } + + virtual void visitLogicalIp(unsigned logicalIp) + { + assertT(&c, logicalIp < c.logicalCode.count()); + + if (c.logicalCode[c.logicalIp]->lastEvent == 0) { + appendDummy(&c); + } + + Event* e = c.logicalCode[logicalIp]->firstEvent; + + Event* p = c.predecessor; + if (p) { + if (DebugAppend) { + fprintf(stderr, + "visit %d pred %d\n", + logicalIp, + p->logicalInstruction->index); + } + + p->stackAfter = c.stack; + p->localsAfter = c.locals; + + Link* link = compiler::link( + &c, p, e->predecessors, e, p->successors, c.forkState); + e->predecessors = link; + p->successors = link; + c.lastEvent->visitLinks = cons(&c, link, c.lastEvent->visitLinks); + + if (DebugAppend) { + fprintf(stderr, + "populate junction reads for %d to %d\n", + p->logicalInstruction->index, + logicalIp); + } + + populateJunctionReads(&c, link); + } + + c.forkState = 0; + } + + virtual void startLogicalIp(unsigned logicalIp) + { + assertT(&c, logicalIp < c.logicalCode.count()); + assertT(&c, c.logicalCode[logicalIp] == 0); + + if (c.logicalCode[c.logicalIp]->lastEvent == 0) { + appendDummy(&c); + } + + Event* p = c.predecessor; + if (p) { + p->stackAfter = c.stack; + p->localsAfter = c.locals; + } + + c.logicalCode[logicalIp] = new (c.zone) + LogicalInstruction(logicalIp, c.stack, c.locals); + + c.logicalIp = logicalIp; + } + + virtual Promise* machineIp(unsigned logicalIp) + { + return ipPromise(&c, logicalIp); + } + + virtual Promise* poolAppend(intptr_t value) + { + return poolAppendPromise(resolvedPromise(&c, value)); + } + + virtual Promise* poolAppendPromise(Promise* value) + { + Promise* p = poolPromise(&c, c.constantCount); + + ConstantPoolNode* constant = new (c.zone) ConstantPoolNode(value); + + if (c.firstConstant) { + c.lastConstant->next = constant; + } else { + c.firstConstant = constant; + } + c.lastConstant = constant; + ++c.constantCount; + + return p; + } + + virtual ir::Value* constant(int64_t value, ir::Type type) + { + return promiseConstant(resolvedPromise(&c, value), type); + } + + virtual ir::Value* promiseConstant(Promise* value, ir::Type type) + { + return compiler::value(&c, type, compiler::constantSite(&c, value)); + } + + virtual ir::Value* address(ir::Type type, Promise* address) + { + return value(&c, type, compiler::addressSite(&c, address)); + } + + virtual ir::Value* memory(ir::Value* base, + ir::Type type, + int displacement = 0, + ir::Value* index = 0) + { + Value* result = value(&c, type); + + appendMemory(&c, + static_cast(base), + displacement, + static_cast(index), + index == 0 ? 1 : type.size(c.targetInfo), + result); + + return result; + } + + virtual ir::Value* threadRegister() + { + return compiler::threadRegister(&c); + } + + Promise* machineIp() + { + return c.logicalCode[c.logicalIp]->lastEvent->makeCodePromise(&c); + } + + virtual void push(ir::Type type, ir::Value* value) + { + // TODO: once type information is flowed properly, enable this assertT. + // Some time later, we can remove the parameter. + // assertT(&c, value->type == type); + compiler::push(&c, typeFootprint(&c, type), static_cast(value)); + } + + virtual void save(ir::Type type, ir::Value* value) + { + // TODO: once type information is flowed properly, enable this assertT. + // Some time later, we can remove the parameter. + // assertT(&c, value->type == type); + unsigned footprint = typeFootprint(&c, type); + c.saved = cons(&c, static_cast(value), c.saved); + if (TargetBytesPerWord == 4 and footprint > 1) { + assertT(&c, footprint == 2); + assertT(&c, static_cast(value)->nextWord); + + save(ir::Type::i4(), static_cast(value)->nextWord); + } + } + + virtual ir::Value* pop(ir::Type type) + { + ir::Value* value = compiler::pop(&c, typeFootprint(&c, type)); + // TODO: once type information is flowed properly, enable this assertT. + // Some time later, we can remove the parameter. + // assertT(&c, static_cast(value)->type == type); + return value; + } + + virtual void pushed(ir::Type type) + { + Value* v = value(&c, type); + appendFrameSite( + &c, + v, + frameIndex(&c, (c.stack ? c.stack->index : 0) + c.localFootprint)); + + Stack* s = compiler::stack(&c, v, c.stack); + v->home = frameIndex(&c, s->index + c.localFootprint); + c.stack = s; + } + + virtual void popped(unsigned footprint) + { + for (; footprint; --footprint) { + assertT(&c, c.stack->value == 0 or c.stack->value->home >= 0); + + if (DebugFrame) { + fprintf(stderr, "popped %p\n", c.stack->value); + } + + c.stack = c.stack->next; + } + } + + virtual unsigned topOfStack() + { + return c.stack->index; + } + + virtual ir::Value* peek(unsigned footprint, unsigned index) + { + Stack* s = c.stack; + for (unsigned i = index; i > 0; --i) { + s = s->next; + } + + if (footprint > 1) { + assertT(&c, footprint == 2); + + bool bigEndian = c.arch->bigEndian(); + +#ifndef NDEBUG + Stack* low; + Stack* high; + if (bigEndian) { + high = s; + low = s->next; + } else { + low = s; + high = s->next; + } + + assertT( + &c, + (TargetBytesPerWord == 8 and low->value->nextWord == low->value + and high->value == 0) + or (TargetBytesPerWord == 4 and low->value->nextWord == high->value)); +#endif // not NDEBUG + + if (bigEndian) { + s = s->next; + } + } + + return s->value; + } + + virtual ir::Value* nativeCall(ir::Value* address, + unsigned flags, + TraceHandler* traceHandler, + ir::Type resultType, + util::Slice arguments) + { + bool bigEndian = c.arch->bigEndian(); + + unsigned footprint = 0; + unsigned size = TargetBytesPerWord; + RUNTIME_ARRAY(ir::Value*, args, arguments.count); + int index = 0; + for (unsigned i = 0; i < arguments.count; ++i) { + Value* o = static_cast(arguments[i]); + if (o) { + if (bigEndian and size > TargetBytesPerWord) { + RUNTIME_ARRAY_BODY(args)[index++] = o->nextWord; + } + RUNTIME_ARRAY_BODY(args)[index] = o; + if ((not bigEndian) and size > TargetBytesPerWord) { + RUNTIME_ARRAY_BODY(args)[++index] = o->nextWord; + } + size = TargetBytesPerWord; + ++index; + } else { + size = 8; + } + ++footprint; + } + + Value* result = value(&c, resultType); + appendCall(&c, + static_cast(address), + ir::CallingConvention::Native, + flags, + traceHandler, + result, + util::Slice(RUNTIME_ARRAY_BODY(args), index)); + + return result; + } + + virtual ir::Value* stackCall(ir::Value* address, + unsigned flags, + TraceHandler* traceHandler, + ir::Type resultType, + Slice arguments) + { + Value* result = value(&c, resultType); + Stack* b UNUSED = c.stack; + appendCall(&c, + static_cast(address), + ir::CallingConvention::Avian, + flags, + traceHandler, + result, + arguments); + assertT(&c, c.stack == b); + return result; + } + + virtual void return_(ir::Value* a) + { + assertT(&c, a); + appendReturn(&c, static_cast(a)); + } + + virtual void return_() + { + appendReturn(&c, 0); + } + + void initLocalPart(unsigned index, ir::Type type) + { + Value* v = value(&c, type); + + if (DebugFrame) { + fprintf(stderr, + "init local %p at %d (%d)\n", + v, + index, + frameIndex(&c, index)); + } + + appendFrameSite(&c, v, frameIndex(&c, index)); + + Local* local = c.locals + index; + local->value = v; + v->home = frameIndex(&c, index); + } + + virtual void initLocal(unsigned index, ir::Type type) + { + unsigned footprint = typeFootprint(&c, type); + + assertT(&c, index + footprint <= c.localFootprint); + + Value* v = value(&c, type); + + if (footprint > 1) { + assertT(&c, footprint == 2); + + unsigned highIndex; + unsigned lowIndex; + if (c.arch->bigEndian()) { + highIndex = index + 1; + lowIndex = index; + } else { + lowIndex = index + 1; + highIndex = index; + } + + if (TargetBytesPerWord == 4) { + initLocalPart(highIndex, type); + Value* next = c.locals[highIndex].value; + v->nextWord = next; + next->nextWord = v; + next->wordIndex = 1; + } + + index = lowIndex; + } + + if (DebugFrame) { + fprintf(stderr, + "init local %p at %d (%d)\n", + v, + index, + frameIndex(&c, index)); + } + + appendFrameSite(&c, v, frameIndex(&c, index)); + + Local* local = c.locals + index; + local->value = v; + v->home = frameIndex(&c, index); + } + + virtual void initLocalsFromLogicalIp(unsigned logicalIp) + { + assertT(&c, logicalIp < c.logicalCode.count()); + + unsigned footprint = sizeof(Local) * c.localFootprint; + Local* newLocals = static_cast(c.zone->allocate(footprint)); + memset(newLocals, 0, footprint); + c.locals = newLocals; + + Event* e = c.logicalCode[logicalIp]->firstEvent; + for (int i = 0; i < static_cast(c.localFootprint); ++i) { + Local* local = e->locals() + i; + if (local->value) { + initLocalPart(i, local->value->type); + } + } + + linkLocals(&c, e->locals(), newLocals); + } + + virtual void storeLocal(ir::Value* src, unsigned index) + { + compiler::storeLocal(&c, + typeFootprint(&c, src->type), + static_cast(src), + index, + true); + } + + virtual ir::Value* loadLocal(ir::Type type, unsigned index) + { + return compiler::loadLocal(&c, type, index); + } + + virtual void saveLocals() + { + int oldIp UNUSED = c.logicalIp; + appendSaveLocals(&c); + assertT(&c, oldIp == c.logicalIp); + } + + virtual void checkBounds(ir::Value* object, + unsigned lengthOffset, + ir::Value* index, + intptr_t handler) + { + appendBoundsCheck(&c, + static_cast(object), + lengthOffset, + static_cast(index), + handler); + } + + virtual ir::Value* truncate(ir::Type type, ir::Value* src) + { + assertT(&c, src->type.flavor() == type.flavor()); + assertT(&c, type.flavor() != ir::Type::Float); + assertT(&c, type.rawSize() < src->type.rawSize()); + Value* dst = value(&c, type); + appendMove(&c, + lir::Move, + src->type.size(c.targetInfo), + src->type.size(c.targetInfo), + static_cast(src), + type.size(c.targetInfo), + dst); + return dst; + } + + virtual ir::Value* truncateThenExtend(ir::ExtendMode extendMode, + ir::Type extendType, + ir::Type truncateType, + ir::Value* src) + { + Value* dst = value(&c, extendType); + appendMove(&c, + extendMode == ir::ExtendMode::Signed ? lir::Move : lir::MoveZ, + TargetBytesPerWord, + truncateType.size(c.targetInfo), + static_cast(src), + extendType.size(c.targetInfo) < TargetBytesPerWord + ? TargetBytesPerWord + : extendType.size(c.targetInfo), + dst); + return dst; + } + + virtual void store(ir::Value* src, ir::Value* dst) + { + assertT(&c, src->type.flavor() == dst->type.flavor()); + + appendMove(&c, + lir::Move, + src->type.size(c.targetInfo), + src->type.size(c.targetInfo), + static_cast(src), + dst->type.size(c.targetInfo), + static_cast(dst)); + } + + virtual ir::Value* load(ir::ExtendMode extendMode, + ir::Value* src, + ir::Type dstType) + { + assertT(&c, src->type.flavor() == dstType.flavor()); + + Value* dst = value(&c, dstType); + appendMove(&c, + extendMode == ir::ExtendMode::Signed ? lir::Move : lir::MoveZ, + src->type.size(c.targetInfo), + src->type.size(c.targetInfo), + static_cast(src), + dstType.size(c.targetInfo) < TargetBytesPerWord + ? TargetBytesPerWord + : dstType.size(c.targetInfo), + dst); + return dst; + } + + virtual void condJump(lir::TernaryOperation op, + ir::Value* a, + ir::Value* b, + ir::Value* addr) + { + assertT(&c, + (isGeneralBranch(op) and isGeneralValue(a) and isGeneralValue(b)) + or (isFloatBranch(op) and isFloatValue(a) and isFloatValue(b))); + + assertT(&c, a->type == b->type); + assertT(&c, addr->type == ir::Type::iptr()); + + appendBranch(&c, + op, + static_cast(a), + static_cast(b), + static_cast(addr)); + } + + virtual void jmp(ir::Value* addr) + { + appendJump(&c, lir::Jump, static_cast(addr)); + } + + virtual void exit(ir::Value* addr) + { + appendJump(&c, lir::Jump, static_cast(addr), true); + } + + virtual ir::Value* binaryOp(lir::TernaryOperation op, + ir::Type type, + ir::Value* a, + ir::Value* b) + { + assertT(&c, + (isGeneralBinaryOp(op) and isGeneralValue(a) and isGeneralValue(b)) + or (isFloatBinaryOp(op) and isFloatValue(a) and isFloatValue(b))); + + Value* result = value(&c, type); + + appendCombine( + &c, op, static_cast(a), static_cast(b), result); + return result; + } + + virtual ir::Value* unaryOp(lir::BinaryOperation op, ir::Value* a) + { + assertT(&c, + (isGeneralUnaryOp(op) and isGeneralValue(a)) + or (isFloatUnaryOp(op) and isFloatValue(a))); + Value* result = value(&c, a->type); + appendTranslate(&c, op, static_cast(a), result); + return result; + } + + virtual ir::Value* f2f(ir::Type resType, ir::Value* a) + { + assertT(&c, isFloatValue(a)); + assertT(&c, resType.flavor() == ir::Type::Float); + Value* result = value(&c, resType); + appendTranslate(&c, lir::Float2Float, static_cast(a), result); + return result; + } + + virtual ir::Value* f2i(ir::Type resType, ir::Value* a) + { + assertT(&c, isFloatValue(a)); + assertT(&c, resType.flavor() != ir::Type::Float); + Value* result = value(&c, resType); + appendTranslate(&c, lir::Float2Int, static_cast(a), result); + return result; + } + + virtual ir::Value* i2f(ir::Type resType, ir::Value* a) + { + assertT(&c, isGeneralValue(a)); + assertT(&c, resType.flavor() == ir::Type::Float); + Value* result = value(&c, resType); + appendTranslate(&c, lir::Int2Float, static_cast(a), result); + return result; + } + + virtual void nullaryOp(lir::Operation op) + { + appendOperation(&c, op); + } + + virtual void compile(uintptr_t stackOverflowHandler, + unsigned stackLimitOffset) + { + compiler::compile(&c, stackOverflowHandler, stackLimitOffset); + } + + virtual unsigned resolve(uint8_t* dst) + { + c.machineCode = dst; + c.assembler->setDestination(dst); + + Block* block = c.firstBlock; + while (block->nextBlock or block->nextInstruction) { + Block* next = block->nextBlock + ? block->nextBlock + : block->nextInstruction->firstEvent->block; + + next->start + = block->assemblerBlock->resolve(block->start, next->assemblerBlock); + + block = next; + } + + return c.machineCodeSize = block->assemblerBlock->resolve(block->start, 0) + + c.assembler->footerSize(); + } + + virtual unsigned poolSize() + { + return c.constantCount * TargetBytesPerWord; + } + + virtual void write() + { + c.assembler->write(); + + int i = 0; + for (ConstantPoolNode* n = c.firstConstant; n; n = n->next) { + target_intptr_t* target = reinterpret_cast( + c.machineCode + pad(c.machineCodeSize, TargetBytesPerWord) + i); + + if (n->promise->resolved()) { + *target = targetVW(n->promise->value()); + } else { + class Listener : public Promise::Listener { + public: + Listener(target_intptr_t* target) : target(target) + { + } + + virtual bool resolve(int64_t value, void** location) + { + *target = targetVW(value); + if (location) + *location = target; + return true; + } + + target_intptr_t* target; + }; + new (n->promise->listen(sizeof(Listener))) Listener(target); + } + + i += TargetBytesPerWord; + } + } + + virtual void dispose() + { + // ignore + } + + Context c; + compiler::Client client; +}; + +} // namespace compiler + +Compiler* makeCompiler(System* system, + Assembler* assembler, + Zone* zone, + Compiler::Client* client) +{ + return new (zone) compiler::MyCompiler(system, assembler, zone, client); +} + +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/compiler/context.cpp b/sgx-jvm/avian/src/codegen/compiler/context.cpp new file mode 100644 index 0000000000..3027d72568 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/compiler/context.cpp @@ -0,0 +1,70 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "codegen/compiler/context.h" +#include "codegen/compiler/resource.h" + +#include + +namespace avian { +namespace codegen { +namespace compiler { + +Context::Context(vm::System* system, + Assembler* assembler, + vm::Zone* zone, + Compiler::Client* client) + : system(system), + assembler(assembler), + arch(assembler->arch()), + zone(zone), + client(client), + stack(0), + locals(0), + saved(0), + predecessor(0), + regFile(arch->registerFile()), + regAlloc(system, arch->registerFile()), + registerResources(static_cast(zone->allocate( + sizeof(RegisterResource) * regFile->allRegisters.limit))), + frameResources(0), + acquiredResources(0), + firstConstant(0), + lastConstant(0), + machineCode(0), + firstEvent(0), + lastEvent(0), + forkState(0), + firstBlock(0), + logicalIp(-1), + constantCount(0), + parameterFootprint(0), + localFootprint(0), + machineCodeSize(0), + alignedFrameSize(0), + availableGeneralRegisterCount(regFile->generalRegisters.limit + - regFile->generalRegisters.start), + targetInfo(arch->targetInfo()) +{ + for (Register i : regFile->generalRegisters) { + new (registerResources + i.index()) RegisterResource(arch->reserved(i)); + + if (registerResources[i.index()].reserved) { + --availableGeneralRegisterCount; + } + } + for (Register i : regFile->floatRegisters) { + new (registerResources + i.index()) RegisterResource(arch->reserved(i)); + } +} + +} // namespace compiler +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/compiler/context.h b/sgx-jvm/avian/src/codegen/compiler/context.h new file mode 100644 index 0000000000..c6ca6ece73 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/compiler/context.h @@ -0,0 +1,149 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_COMPILER_CONTEXT_H +#define AVIAN_CODEGEN_COMPILER_CONTEXT_H + +#include +#include +#include + +#include "regalloc.h" + +using namespace avian::util; + +namespace avian { +namespace codegen { +namespace compiler { + +class Stack; +class Local; +class Event; +class LogicalInstruction; + +class Resource; +class RegisterResource; +class FrameResource; + +class ConstantPoolNode; + +class ForkState; +class Block; + +template +List* reverseDestroy(List* cell) +{ + List* previous = 0; + while (cell) { + List* next = cell->next; + cell->next = previous; + previous = cell; + cell = next; + } + return previous; +} + +class LogicalCode { + private: + util::Slice logicalCode; + + public: + LogicalCode() : logicalCode(0, 0) + { + } + + void init(vm::Zone* zone, size_t count) + { + // leave room for logical instruction -1 + size_t realCount = count + 1; + + logicalCode + = util::Slice::allocAndSet(zone, realCount, 0); + } + + void extend(vm::Zone* zone, size_t more) + { + util::Slice newCode + = logicalCode.cloneAndSet(zone, logicalCode.count + more, 0); + + for (size_t i = 0; i < logicalCode.count; i++) { + assertT((vm::System*)0, logicalCode[i] == newCode[i]); + } + + logicalCode = newCode; + } + + size_t count() + { + return logicalCode.count - 1; + } + + LogicalInstruction*& operator[](int index) + { + // leave room for logical instruction -1 + return logicalCode[index + 1]; + } +}; + +class Context { + public: + Context(vm::System* system, + Assembler* assembler, + vm::Zone* zone, + Compiler::Client* client); + + vm::System* system; + Assembler* assembler; + Architecture* arch; + vm::Zone* zone; + Compiler::Client* client; + Stack* stack; + Local* locals; + List* saved; + Event* predecessor; + LogicalCode logicalCode; + const RegisterFile* regFile; + RegisterAllocator regAlloc; + RegisterResource* registerResources; + FrameResource* frameResources; + Resource* acquiredResources; + ConstantPoolNode* firstConstant; + ConstantPoolNode* lastConstant; + uint8_t* machineCode; + Event* firstEvent; + Event* lastEvent; + ForkState* forkState; + Block* firstBlock; + int logicalIp; + unsigned constantCount; + unsigned parameterFootprint; + unsigned localFootprint; + unsigned machineCodeSize; + unsigned alignedFrameSize; + unsigned availableGeneralRegisterCount; + ir::TargetInfo targetInfo; +}; + +inline Aborter* getAborter(Context* c) +{ + return c->system; +} + +template +List* cons(Context* c, const T& value, List* next) +{ + return new (c->zone) List(value, next); +} + +} // namespace compiler +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_COMPILER_CONTEXT_H diff --git a/sgx-jvm/avian/src/codegen/compiler/event.cpp b/sgx-jvm/avian/src/codegen/compiler/event.cpp new file mode 100644 index 0000000000..575bede6ac --- /dev/null +++ b/sgx-jvm/avian/src/codegen/compiler/event.cpp @@ -0,0 +1,2193 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/target.h" +#include +#include + +#include "codegen/compiler/context.h" +#include "codegen/compiler/event.h" +#include "codegen/compiler/site.h" +#include "codegen/compiler/read.h" +#include "codegen/compiler/value.h" +#include "codegen/compiler/promise.h" +#include "codegen/compiler/frame.h" +#include "codegen/compiler/ir.h" + +using namespace avian::util; + +namespace avian { +namespace codegen { +namespace compiler { + +SiteMask generalRegisterMask(Context* c); +SiteMask generalRegisterOrConstantMask(Context* c); + +CodePromise* codePromise(Context* c, Promise* offset); + +void saveLocals(Context* c, Event* e); + +void apply(Context* c, + lir::UnaryOperation op, + unsigned s1Size, + Site* s1Low, + Site* s1High); + +void apply(Context* c, + lir::BinaryOperation op, + unsigned s1Size, + Site* s1Low, + Site* s1High, + unsigned s2Size, + Site* s2Low, + Site* s2High); + +void apply(Context* c, + lir::TernaryOperation op, + unsigned s1Size, + Site* s1Low, + Site* s1High, + unsigned s2Size, + Site* s2Low, + Site* s2High, + unsigned s3Size, + Site* s3Low, + Site* s3High); + +void append(Context* c, Event* e); + +void clean(Context* c, + Event* e, + Stack* stack, + Local* locals, + Read* reads, + unsigned popIndex); + +Read* live(Context* c UNUSED, Value* v); + +void popRead(Context* c, Event* e UNUSED, Value* v); + +void maybeMove(Context* c, + lir::BinaryOperation op, + unsigned srcSize, + unsigned srcSelectSize, + Value* src, + unsigned dstSize, + Value* dst, + const SiteMask& dstMask); + +Site* maybeMove(Context* c, + Value* v, + const SiteMask& mask, + bool intersectMask, + bool includeNextWord, + unsigned registerReserveCount = 0); + +Site* maybeMove(Context* c, + Read* read, + bool intersectRead, + bool includeNextWord, + unsigned registerReserveCount = 0); +Site* pickSiteOrMove(Context* c, + Value* src, + Value* dst, + Site* nextWord, + unsigned index); + +Site* pickTargetSite(Context* c, + Read* read, + bool intersectRead = false, + unsigned registerReserveCount = 0, + CostCalculator* costCalculator = 0); +Value* threadRegister(Context* c); + +Event::Event(Context* c) + : next(0), + stackBefore(c->stack), + localsBefore(c->locals), + stackAfter(0), + localsAfter(0), + promises(0), + reads(0), + junctionSites(0), + snapshots(0), + predecessors(0), + successors(0), + visitLinks(0), + block(0), + logicalInstruction(c->logicalCode[c->logicalIp]), + readCount(0) +{ +} + +void Event::addRead(Context* c, Value* v, Read* r) +{ + if (DebugReads) { + fprintf(stderr, + "add read %p to %p last %p event %p (%s)\n", + r, + v, + v->lastRead, + this, + this->name()); + } + + r->event = this; + r->eventNext = this->reads; + this->reads = r; + ++this->readCount; + + finishAddRead(c, v, r); +} + +void finishAddRead(Context* c, Value* v, Read* r) +{ + r->value = v; + + if (v->lastRead) { + if (DebugReads) { + fprintf(stderr, "append %p to %p for %p\n", r, v->lastRead, v); + } + + v->lastRead->append(c, r); + } else { + v->reads = r; + } + v->lastRead = r; +} + +void Event::addRead(Context* c, + Value* v, + const SiteMask& mask, + Value* successor) +{ + this->addRead(c, v, read(c, mask, successor)); +} + +void Event::addReads(Context* c, + Value* v, + unsigned size, + const SiteMask& lowMask, + Value* lowSuccessor, + const SiteMask& highMask, + Value* highSuccessor) +{ + SingleRead* r = read(c, lowMask, lowSuccessor); + this->addRead(c, v, r); + if (size > c->targetInfo.pointerSize) { + r->high_ = v->nextWord; + this->addRead(c, v->nextWord, highMask, highSuccessor); + } +} + +void Event::addReads(Context* c, + Value* v, + unsigned size, + const SiteMask& lowMask, + const SiteMask& highMask) +{ + this->addReads(c, v, size, lowMask, 0, highMask, 0); +} + +CodePromise* Event::makeCodePromise(Context* c) +{ + return this->promises = new (c->zone) CodePromise(c, this->promises); +} + +bool Event::isUnreachable() +{ + for (Link* p = this->predecessors; p; p = p->nextPredecessor) { + if (not p->predecessor->allExits()) + return false; + } + return this->predecessors != 0; +} + +unsigned Link::countPredecessors() +{ + Link* link = this; + unsigned c = 0; + for (; link; link = link->nextPredecessor) { + ++c; + } + return c; +} + +Link* Link::lastPredecessor() +{ + Link* link = this; + while (link->nextPredecessor) { + link = link->nextPredecessor; + } + return link; +} + +unsigned Link::countSuccessors() +{ + Link* link = this; + unsigned c = 0; + for (; link; link = link->nextSuccessor) { + ++c; + } + return c; +} + +Link* link(Context* c, + Event* predecessor, + Link* nextPredecessor, + Event* successor, + Link* nextSuccessor, + ForkState* forkState) +{ + return new (c->zone) + Link(predecessor, nextPredecessor, successor, nextSuccessor, forkState); +} + +Value* maybeBuddySlice(Context* c, Value* v) +{ + if (v->home >= 0) { + Value* n = value(c, v->type); + appendBuddy(c, v, n); + return n; + } else { + return v; + } +} + +template +struct SliceStack : public Slice { + size_t capacity; + + SliceStack(T* items, size_t capacity) + : Slice(items + capacity, 0), capacity(capacity) + { + } + + void push(const T& item) + { + ASSERT(Slice::count < capacity); + --Slice::items; + ++Slice::count; + *Slice::items = item; + } +}; + +template +struct FixedSliceStack : public SliceStack { + T itemArray[Capacity]; + + FixedSliceStack() : SliceStack(&itemArray[0], Capacity) + { + } +}; + +Value* slicePushWord(Context* c, + Value* v, + size_t stackBase UNUSED, + SliceStack& slice) +{ + if (v) { + v = maybeBuddySlice(c, v); + } + + size_t index UNUSED = slice.count; + + assertT(c, slice.count < slice.capacity); + slice.push(v); + + if (false) { + fprintf(stderr, "push %p\n", v); + } + + if (v) { + v->home = frameIndex(c, index + stackBase + c->localFootprint); + } + + return v; +} + +void slicePush(Context* c, + unsigned footprint, + Value* v, + size_t stackBase, + SliceStack& slice) +{ + assertT(c, footprint); + + bool bigEndian = c->arch->bigEndian(); + + Value* low = v; + + if (bigEndian) { + v = slicePushWord(c, v, stackBase, slice); + } + + Value* high; + if (footprint > 1) { + assertT(c, footprint == 2); + + if (c->targetInfo.pointerSize == 4) { + low->maybeSplit(c); + high = slicePushWord(c, low->nextWord, stackBase, slice); + } else { + high = slicePushWord(c, 0, stackBase, slice); + } + } else { + high = 0; + } + + if (not bigEndian) { + v = slicePushWord(c, v, stackBase, slice); + } + + if (high) { + v->nextWord = high; + high->nextWord = v; + high->wordIndex = 1; + } +} + +class CallEvent : public Event { + public: + CallEvent(Context* c, + Value* address, + ir::CallingConvention callingConvention, + unsigned flags, + TraceHandler* traceHandler, + Value* resultValue, + util::Slice arguments) + : Event(c), + address(address), + traceHandler(traceHandler), + resultValue(resultValue), + returnAddressSurrogate(0), + framePointerSurrogate(0), + popIndex(0), + stackArgumentIndex(0), + flags(flags), + stackArgumentFootprint(callingConvention == ir::CallingConvention::Avian + ? arguments.count + : 0) + { + RegisterMask registerMask = c->regFile->generalRegisters; + + if (callingConvention == ir::CallingConvention::Native) { + assertT(c, (flags & Compiler::TailJump) == 0); + assertT(c, stackArgumentFootprint == 0); + + unsigned index = 0; + unsigned argumentIndex = 0; + + while (true) { + Value* v = static_cast(arguments[argumentIndex]); + + unsigned footprint = (argumentIndex + 1 < arguments.count + and v->nextWord == arguments[argumentIndex + 1]) + ? 2 + : 1; + + if (index % (c->arch->argumentAlignment() ? footprint : 1)) { + ++index; + } + + SiteMask targetMask; + if (index + (c->arch->argumentRegisterAlignment() ? footprint : 1) + <= c->arch->argumentRegisterCount()) { + Register number = c->arch->argumentRegister(index); + + if (DebugReads) { + fprintf(stderr, "reg %d arg read %p\n", number.index(), v); + } + + targetMask = SiteMask::fixedRegisterMask(number); + registerMask = registerMask.excluding(number); + } else { + if (index < c->arch->argumentRegisterCount()) { + index = c->arch->argumentRegisterCount(); + } + + unsigned frameIndex = index - c->arch->argumentRegisterCount(); + + if (DebugReads) { + fprintf(stderr, "stack %d arg read %p\n", frameIndex, v); + } + + targetMask = SiteMask(lir::Operand::MemoryMask, 0, frameIndex); + } + + this->addRead(c, v, targetMask); + + ++index; + + if ((++argumentIndex) >= arguments.count) { + break; + } + } + } + + if (DebugReads) { + fprintf(stderr, "address read %p\n", address); + } + + { + bool thunk; + OperandMask op; + c->arch->plan((flags & Compiler::Aligned) ? lir::AlignedCall : lir::Call, + c->targetInfo.pointerSize, + op, + &thunk); + + assertT(c, not thunk); + + this->addRead( + c, + address, + SiteMask(op.typeMask, registerMask & op.lowRegisterMask, AnyFrameIndex)); + } + + Stack* stack = stackBefore; + + if (callingConvention == ir::CallingConvention::Avian) { + for (size_t i = 0; i < arguments.count; i++) { + stack = stack->next; + } + for (int i = stackArgumentFootprint - 1; i >= 0; --i) { + Value* v = static_cast(arguments[i]); + + if ((c->targetInfo.pointerSize == 8 + && (v == 0 || (i >= 1 && arguments[i - 1] == 0))) + || (c->targetInfo.pointerSize == 4 && v->nextWord != v)) { + assertT(c, + c->targetInfo.pointerSize == 8 + or v->nextWord == arguments[i - 1]); + + arguments[i] = arguments[i - 1]; + --i; + } + arguments[i] = v; + } + + int returnAddressIndex; + int framePointerIndex; + int frameOffset; + + if (TailCalls and (flags & Compiler::TailJump)) { + int base = frameBase(c); + returnAddressIndex = base + c->arch->returnAddressOffset(); + if (UseFramePointer) { + framePointerIndex = base + c->arch->framePointerOffset(); + } else { + framePointerIndex = -1; + } + + frameOffset = totalFrameSize(c) + - c->arch->argumentFootprint(stackArgumentFootprint); + } else { + returnAddressIndex = -1; + framePointerIndex = -1; + frameOffset = 0; + } + + for (unsigned i = 0; i < stackArgumentFootprint; ++i) { + Value* v = static_cast(arguments[i]); + if (v) { + int frameIndex = i + frameOffset; + + if (DebugReads) { + fprintf(stderr, + "stack arg read %p at %d of %d\n", + v, + frameIndex, + totalFrameSize(c)); + } + + if (static_cast(frameIndex) == returnAddressIndex) { + returnAddressSurrogate = v; + this->addRead(c, v, generalRegisterMask(c)); + } else if (static_cast(frameIndex) == framePointerIndex) { + framePointerSurrogate = v; + this->addRead(c, v, generalRegisterMask(c)); + } else { + this->addRead( + c, v, SiteMask(lir::Operand::MemoryMask, 0, frameIndex)); + } + } + } + } + + if ((not TailCalls) or (flags & Compiler::TailJump) == 0) { + stackArgumentIndex = c->localFootprint; + if (stackBefore) { + stackArgumentIndex += stackBefore->index + 1 - stackArgumentFootprint; + } + + popIndex = c->alignedFrameSize + c->parameterFootprint + - c->arch->frameFooterSize() - stackArgumentIndex; + + assertT(c, static_cast(popIndex) >= 0); + + while (stack) { + if (stack->value) { + unsigned logicalIndex + = compiler::frameIndex(c, stack->index + c->localFootprint); + + if (DebugReads) { + fprintf(stderr, + "stack save read %p at %d of %d\n", + stack->value, + logicalIndex, + totalFrameSize(c)); + } + + this->addRead(c, + stack->value, + SiteMask(lir::Operand::MemoryMask, 0, logicalIndex)); + } + + stack = stack->next; + } + + saveLocals(c, this); + } + } + + virtual const char* name() + { + return "CallEvent"; + } + + virtual void compile(Context* c) + { + lir::UnaryOperation op; + + unsigned footprint = c->arch->argumentFootprint(stackArgumentFootprint); + + if (TailCalls and (flags & Compiler::TailJump)) { + if (flags & Compiler::LongJumpOrCall) { + if (flags & Compiler::Aligned) { + op = lir::AlignedLongJump; + } else { + op = lir::LongJump; + } + } else if (flags & Compiler::Aligned) { + op = lir::AlignedJump; + } else { + op = lir::Jump; + } + + assertT( + c, + returnAddressSurrogate == 0 + or returnAddressSurrogate->source->type(c) == lir::Operand::Type::RegisterPair); + assertT( + c, + framePointerSurrogate == 0 + or framePointerSurrogate->source->type(c) == lir::Operand::Type::RegisterPair); + + Register ras; + if (returnAddressSurrogate) { + returnAddressSurrogate->source->freeze(c, returnAddressSurrogate); + + ras = static_cast(returnAddressSurrogate->source) + ->number; + } else { + ras = NoRegister; + } + + Register fps; + if (framePointerSurrogate) { + framePointerSurrogate->source->freeze(c, framePointerSurrogate); + + fps = static_cast(framePointerSurrogate->source)->number; + } else { + fps = NoRegister; + } + + int offset = static_cast(footprint) + - static_cast( + c->arch->argumentFootprint(c->parameterFootprint)); + + c->assembler->popFrameForTailCall(c->alignedFrameSize, offset, ras, fps); + } else if (flags & Compiler::LongJumpOrCall) { + if (flags & Compiler::Aligned) { + op = lir::AlignedLongCall; + } else { + op = lir::LongCall; + } + } else if (flags & Compiler::Aligned) { + op = lir::AlignedCall; + } else { + op = lir::Call; + } + + apply(c, op, c->targetInfo.pointerSize, address->source, address->source); + + if (traceHandler) { + traceHandler->handleTrace(codePromise(c, c->assembler->offset(true)), + stackArgumentIndex); + } + + if (TailCalls) { + if (flags & Compiler::TailJump) { + if (returnAddressSurrogate) { + returnAddressSurrogate->source->thaw(c, returnAddressSurrogate); + } + + if (framePointerSurrogate) { + framePointerSurrogate->source->thaw(c, framePointerSurrogate); + } + } else if (footprint > c->arch->stackAlignmentInWords()) { + c->assembler->adjustFrame(footprint - c->arch->stackAlignmentInWords()); + } + } + + clean(c, this, stackBefore, localsBefore, reads, popIndex); + + if (resultValue->type.size(c->targetInfo) and live(c, resultValue)) { + resultValue->addSite(c, registerSite(c, c->arch->returnLow())); + if (resultValue->type.size(c->targetInfo) > c->targetInfo.pointerSize + and live(c, resultValue->nextWord)) { + resultValue->nextWord->addSite(c, + registerSite(c, c->arch->returnHigh())); + } + } + } + + virtual bool allExits() + { + return (flags & Compiler::TailJump) != 0; + } + + Value* address; + TraceHandler* traceHandler; + Value* resultValue; + Value* returnAddressSurrogate; + Value* framePointerSurrogate; + unsigned popIndex; + unsigned stackArgumentIndex; + unsigned flags; + unsigned stackArgumentFootprint; +}; + +void appendCall(Context* c, + Value* address, + ir::CallingConvention callingConvention, + unsigned flags, + TraceHandler* traceHandler, + Value* result, + util::Slice arguments) +{ + append(c, + new (c->zone) CallEvent(c, + address, + callingConvention, + flags, + traceHandler, + result, + arguments)); +} + +class ReturnEvent : public Event { + public: + ReturnEvent(Context* c, Value* value) : Event(c), value(value) + { + if (value) { + this->addReads(c, + value, + value->type.size(c->targetInfo), + SiteMask::fixedRegisterMask(c->arch->returnLow()), + SiteMask::fixedRegisterMask(c->arch->returnHigh())); + } + } + + virtual const char* name() + { + return "ReturnEvent"; + } + + virtual void compile(Context* c) + { + for (Read* r = reads; r; r = r->eventNext) { + popRead(c, this, r->value); + } + + if (not this->isUnreachable()) { + c->assembler->popFrameAndPopArgumentsAndReturn( + c->alignedFrameSize, + c->arch->argumentFootprint(c->parameterFootprint)); + } + } + + Value* value; +}; + +void appendReturn(Context* c, Value* value) +{ + append(c, new (c->zone) ReturnEvent(c, value)); +} + +class MoveEvent : public Event { + public: + MoveEvent(Context* c, + lir::BinaryOperation op, + unsigned srcSize, + unsigned srcSelectSize, + Value* srcValue, + unsigned dstSize, + Value* dstValue, + const SiteMask& srcLowMask, + const SiteMask& srcHighMask) + : Event(c), + op(op), + srcSize(srcSize), + srcSelectSize(srcSelectSize), + srcValue(srcValue), + dstSize(dstSize), + dstValue(dstValue) + { + assertT(c, srcSelectSize <= srcSize); + + bool noop = srcSelectSize >= dstSize; + + if (dstSize > c->targetInfo.pointerSize) { + dstValue->grow(c); + } + + if (srcSelectSize > c->targetInfo.pointerSize) { + srcValue->maybeSplit(c); + } + + this->addReads( + c, + srcValue, + srcSelectSize, + srcLowMask, + noop ? dstValue : 0, + srcHighMask, + noop and dstSize > c->targetInfo.pointerSize ? dstValue->nextWord : 0); + } + + virtual const char* name() + { + return "MoveEvent"; + } + + virtual void compile(Context* c) + { + OperandMask dst; + + c->arch->planDestination( + op, + srcSelectSize, + OperandMask( + 1 << (unsigned)srcValue->source->type(c), + srcValue->source->registerMask(c), + srcValue->nextWord->source->registerMask(c)), + dstSize, + dst); + + SiteMask dstLowMask = SiteMask::lowPart(dst); + SiteMask dstHighMask = SiteMask::highPart(dst); + + if (srcSelectSize >= c->targetInfo.pointerSize + and dstSize >= c->targetInfo.pointerSize and srcSelectSize >= dstSize) { + if (dstValue->target) { + if (dstSize > c->targetInfo.pointerSize) { + if (srcValue->source->registerSize(c) > c->targetInfo.pointerSize) { + apply(c, + lir::Move, + srcSelectSize, + srcValue->source, + srcValue->source, + dstSize, + dstValue->target, + dstValue->target); + + if (live(c, dstValue) == 0) { + dstValue->removeSite(c, dstValue->target); + if (dstSize > c->targetInfo.pointerSize) { + dstValue->nextWord->removeSite(c, dstValue->nextWord->target); + } + } + } else { + srcValue->nextWord->source->freeze(c, srcValue->nextWord); + + maybeMove(c, + lir::Move, + c->targetInfo.pointerSize, + c->targetInfo.pointerSize, + srcValue, + c->targetInfo.pointerSize, + dstValue, + dstLowMask); + + srcValue->nextWord->source->thaw(c, srcValue->nextWord); + + maybeMove(c, + lir::Move, + c->targetInfo.pointerSize, + c->targetInfo.pointerSize, + srcValue->nextWord, + c->targetInfo.pointerSize, + dstValue->nextWord, + dstHighMask); + } + } else { + maybeMove(c, + lir::Move, + c->targetInfo.pointerSize, + c->targetInfo.pointerSize, + srcValue, + c->targetInfo.pointerSize, + dstValue, + dstLowMask); + } + } else { + Site* low = pickSiteOrMove(c, srcValue, dstValue, 0, 0); + if (dstSize > c->targetInfo.pointerSize) { + pickSiteOrMove(c, srcValue->nextWord, dstValue->nextWord, low, 1); + } + } + } else if (srcSelectSize <= c->targetInfo.pointerSize + and dstSize <= c->targetInfo.pointerSize) { + maybeMove(c, + op, + srcSize, + srcSelectSize, + srcValue, + dstSize, + dstValue, + dstLowMask); + } else { + assertT(c, srcSize == c->targetInfo.pointerSize); + assertT(c, srcSelectSize == c->targetInfo.pointerSize); + + if (dstValue->nextWord->target or live(c, dstValue->nextWord)) { + assertT(c, dstLowMask.typeMask & lir::Operand::RegisterPairMask); + + Site* low = freeRegisterSite(c, dstLowMask.registerMask); + + srcValue->source->freeze(c, srcValue); + + dstValue->addSite(c, low); + + low->freeze(c, dstValue); + + if (DebugMoves) { + char srcb[256]; + srcValue->source->toString(c, srcb, 256); + char dstb[256]; + low->toString(c, dstb, 256); + fprintf(stderr, "move %s to %s for %p\n", srcb, dstb, srcValue); + } + + apply(c, + lir::Move, + c->targetInfo.pointerSize, + srcValue->source, + srcValue->source, + c->targetInfo.pointerSize, + low, + low); + + low->thaw(c, dstValue); + + srcValue->source->thaw(c, srcValue); + + assertT(c, dstHighMask.typeMask & lir::Operand::RegisterPairMask); + + Site* high = freeRegisterSite(c, dstHighMask.registerMask); + + low->freeze(c, dstValue); + + dstValue->nextWord->addSite(c, high); + + high->freeze(c, dstValue->nextWord); + + if (DebugMoves) { + char srcb[256]; + low->toString(c, srcb, 256); + char dstb[256]; + high->toString(c, dstb, 256); + fprintf(stderr, + "extend %s to %s for %p %p\n", + srcb, + dstb, + dstValue, + dstValue->nextWord); + } + + apply(c, + lir::Move, + c->targetInfo.pointerSize, + low, + low, + dstSize, + low, + high); + + high->thaw(c, dstValue->nextWord); + + low->thaw(c, dstValue); + } else { + pickSiteOrMove(c, srcValue, dstValue, 0, 0); + } + } + + for (Read* r = reads; r; r = r->eventNext) { + popRead(c, this, r->value); + } + } + + lir::BinaryOperation op; + unsigned srcSize; + unsigned srcSelectSize; + Value* srcValue; + unsigned dstSize; + Value* dstValue; +}; + +void appendMove(Context* c, + lir::BinaryOperation op, + unsigned srcSize, + unsigned srcSelectSize, + Value* srcValue, + unsigned dstSize, + Value* dstValue) +{ + bool thunk; + OperandMask src; + + c->arch->planSource(op, srcSelectSize, src, dstSize, &thunk); + + assertT(c, not thunk); + + append(c, + new (c->zone) MoveEvent(c, + op, + srcSize, + srcSelectSize, + srcValue, + dstSize, + dstValue, + SiteMask::lowPart(src), + SiteMask::highPart(src))); +} + +void freezeSource(Context* c, unsigned size, Value* v) +{ + v->source->freeze(c, v); + if (size > c->targetInfo.pointerSize) { + v->nextWord->source->freeze(c, v->nextWord); + } +} + +void thawSource(Context* c, unsigned size, Value* v) +{ + v->source->thaw(c, v); + if (size > c->targetInfo.pointerSize) { + v->nextWord->source->thaw(c, v->nextWord); + } +} + +Read* liveNext(Context* c, Value* v) +{ + assertT(c, v->buddy->hasBuddy(c, v)); + + Read* r = v->reads->next(c); + if (valid(r)) + return r; + + for (Value* p = v->buddy; p != v; p = p->buddy) { + if (valid(p->reads)) + return p->reads; + } + + return 0; +} + +void preserve(Context* c, Value* v, Read* r, Site* s) +{ + s->freeze(c, v); + + maybeMove(c, r, false, true, 0); + + s->thaw(c, v); +} + +Site* getTarget(Context* c, + Value* value, + Value* result, + const SiteMask& resultMask) +{ + Site* s; + Value* v; + Read* r = liveNext(c, value); + if (value->source->match(c, static_cast(resultMask)) + and (r == 0 + or value->source->loneMatch( + c, static_cast(resultMask)))) { + s = value->source; + v = value; + if (r and v->uniqueSite(c, s)) { + preserve(c, v, r, s); + } + } else { + SingleRead r(resultMask, 0); + r.value = result; + r.successor_ = result; + s = pickTargetSite(c, &r, true); + v = result; + result->addSite(c, s); + } + + v->removeSite(c, s); + + s->freeze(c, v); + + return s; +} + +class CombineEvent : public Event { + public: + CombineEvent(Context* c, + lir::TernaryOperation op, + Value* firstValue, + Value* secondValue, + Value* resultValue, + const SiteMask& firstLowMask, + const SiteMask& firstHighMask, + const SiteMask& secondLowMask, + const SiteMask& secondHighMask) + : Event(c), + op(op), + firstValue(firstValue), + secondValue(secondValue), + resultValue(resultValue) + { + this->addReads(c, + firstValue, + firstValue->type.size(c->targetInfo), + firstLowMask, + firstHighMask); + + if (resultValue->type.size(c->targetInfo) > c->targetInfo.pointerSize) { + resultValue->grow(c); + } + + bool condensed = c->arch->alwaysCondensed(op); + + this->addReads(c, + secondValue, + secondValue->type.size(c->targetInfo), + secondLowMask, + condensed ? resultValue : 0, + secondHighMask, + condensed ? resultValue->nextWord : 0); + } + + virtual const char* name() + { + return "CombineEvent"; + } + + virtual void compile(Context* c) + { + assertT( + c, + firstValue->source->type(c) == firstValue->nextWord->source->type(c)); + + if (false) { + if (secondValue->source->type(c) + != secondValue->nextWord->source->type(c)) { + fprintf(stderr, + "%p %p %d : %p %p %d\n", + secondValue, + secondValue->source, + static_cast(secondValue->source->type(c)), + secondValue->nextWord, + secondValue->nextWord->source, + static_cast(secondValue->nextWord->source->type(c))); + } + } + + assertT( + c, + secondValue->source->type(c) == secondValue->nextWord->source->type(c)); + + freezeSource(c, firstValue->type.size(c->targetInfo), firstValue); + + OperandMask cMask; + + c->arch->planDestination( + op, + firstValue->type.size(c->targetInfo), + OperandMask( + 1 << (unsigned)firstValue->source->type(c), + firstValue->source->registerMask(c), + firstValue->nextWord->source->registerMask(c)), + secondValue->type.size(c->targetInfo), + OperandMask( + 1 << (unsigned)secondValue->source->type(c), + secondValue->source->registerMask(c), + secondValue->nextWord->source->registerMask(c)), + resultValue->type.size(c->targetInfo), + cMask); + + SiteMask resultLowMask = SiteMask::lowPart(cMask); + SiteMask resultHighMask = SiteMask::highPart(cMask); + + Site* low = getTarget(c, secondValue, resultValue, resultLowMask); + unsigned lowSize = low->registerSize(c); + Site* high = (resultValue->type.size(c->targetInfo) > lowSize + ? getTarget(c, + secondValue->nextWord, + resultValue->nextWord, + resultHighMask) + : low); + + if (false) { + fprintf(stderr, + "combine %p:%p and %p:%p into %p:%p\n", + firstValue, + firstValue->nextWord, + secondValue, + secondValue->nextWord, + resultValue, + resultValue->nextWord); + } + + apply(c, + op, + firstValue->type.size(c->targetInfo), + firstValue->source, + firstValue->nextWord->source, + secondValue->type.size(c->targetInfo), + secondValue->source, + secondValue->nextWord->source, + resultValue->type.size(c->targetInfo), + low, + high); + + thawSource(c, firstValue->type.size(c->targetInfo), firstValue); + + for (Read* r = reads; r; r = r->eventNext) { + popRead(c, this, r->value); + } + + low->thaw(c, secondValue); + if (resultValue->type.size(c->targetInfo) > lowSize) { + high->thaw(c, secondValue->nextWord); + } + + if (live(c, resultValue)) { + resultValue->addSite(c, low); + if (resultValue->type.size(c->targetInfo) > lowSize + and live(c, resultValue->nextWord)) { + resultValue->nextWord->addSite(c, high); + } + } + } + + lir::TernaryOperation op; + Value* firstValue; + Value* secondValue; + Value* resultValue; +}; + +void appendCombine(Context* c, + lir::TernaryOperation op, + Value* firstValue, + Value* secondValue, + Value* resultValue) +{ + bool thunk; + OperandMask firstMask; + OperandMask secondMask; + c->arch->planSource(op, + firstValue->type.size(c->targetInfo), + firstMask, + secondValue->type.size(c->targetInfo), + secondMask, + resultValue->type.size(c->targetInfo), + &thunk); + + if (thunk) { + const size_t MaxValueCount = 6; + FixedSliceStack slice; + size_t stackBase = c->stack ? c->stack->index + 1 : 0; + + bool threadParameter; + intptr_t handler + = c->client->getThunk(op, + firstValue->type.size(c->targetInfo), + resultValue->type.size(c->targetInfo), + &threadParameter); + + unsigned stackSize = ceilingDivide(secondValue->type.size(c->targetInfo), + c->targetInfo.pointerSize) + + ceilingDivide(firstValue->type.size(c->targetInfo), + c->targetInfo.pointerSize); + + slicePush(c, + ceilingDivide(secondValue->type.size(c->targetInfo), + c->targetInfo.pointerSize), + secondValue, + stackBase, + slice); + slicePush(c, + ceilingDivide(firstValue->type.size(c->targetInfo), + c->targetInfo.pointerSize), + firstValue, + stackBase, + slice); + + if (threadParameter) { + ++stackSize; + + slicePush(c, 1, threadRegister(c), stackBase, slice); + } + + appendCall(c, + value(c, ir::Type::addr(), constantSite(c, handler)), + ir::CallingConvention::Native, + 0, + 0, + resultValue, + slice); + } else { + append(c, + new (c->zone) CombineEvent(c, + op, + firstValue, + secondValue, + resultValue, + SiteMask::lowPart(firstMask), + SiteMask::highPart(firstMask), + SiteMask::lowPart(secondMask), + SiteMask::highPart(secondMask))); + } +} + +class TranslateEvent : public Event { + public: + TranslateEvent(Context* c, + lir::BinaryOperation op, + Value* firstValue, + Value* resultValue, + const SiteMask& valueLowMask, + const SiteMask& valueHighMask) + : Event(c), op(op), firstValue(firstValue), resultValue(resultValue) + { + bool condensed = c->arch->alwaysCondensed(op); + + if (resultValue->type.size(c->targetInfo) > c->targetInfo.pointerSize) { + resultValue->grow(c); + } + + this->addReads(c, + firstValue, + firstValue->type.size(c->targetInfo), + valueLowMask, + condensed ? resultValue : 0, + valueHighMask, + condensed ? resultValue->nextWord : 0); + } + + virtual const char* name() + { + return "TranslateEvent"; + } + + virtual void compile(Context* c) + { + assertT( + c, + firstValue->source->type(c) == firstValue->nextWord->source->type(c)); + + OperandMask bMask; + + c->arch->planDestination( + op, + firstValue->type.size(c->targetInfo), + OperandMask( + 1 << (unsigned)firstValue->source->type(c), + firstValue->source->registerMask(c), + firstValue->nextWord->source->registerMask(c)), + resultValue->type.size(c->targetInfo), + bMask); + + SiteMask resultLowMask = SiteMask::lowPart(bMask); + SiteMask resultHighMask = SiteMask::highPart(bMask); + + Site* low = getTarget(c, firstValue, resultValue, resultLowMask); + unsigned lowSize = low->registerSize(c); + Site* high = (resultValue->type.size(c->targetInfo) > lowSize + ? getTarget(c, + firstValue->nextWord, + resultValue->nextWord, + resultHighMask) + : low); + + apply(c, + op, + firstValue->type.size(c->targetInfo), + firstValue->source, + firstValue->nextWord->source, + resultValue->type.size(c->targetInfo), + low, + high); + + for (Read* r = reads; r; r = r->eventNext) { + popRead(c, this, r->value); + } + + low->thaw(c, firstValue); + if (resultValue->type.size(c->targetInfo) > lowSize) { + high->thaw(c, firstValue->nextWord); + } + + if (live(c, resultValue)) { + resultValue->addSite(c, low); + if (resultValue->type.size(c->targetInfo) > lowSize + and live(c, resultValue->nextWord)) { + resultValue->nextWord->addSite(c, high); + } + } + } + + lir::BinaryOperation op; + Value* firstValue; + Value* resultValue; + Read* resultRead; + SiteMask resultLowMask; + SiteMask resultHighMask; +}; + +void appendTranslate(Context* c, + lir::BinaryOperation op, + Value* firstValue, + Value* resultValue) +{ + assertT(c, + firstValue->type.size(c->targetInfo) + == firstValue->type.size(c->targetInfo)); + assertT(c, + resultValue->type.size(c->targetInfo) + == resultValue->type.size(c->targetInfo)); + + bool thunk; + OperandMask first; + + c->arch->planSource(op, + firstValue->type.size(c->targetInfo), + first, + resultValue->type.size(c->targetInfo), + &thunk); + + if (thunk) { + size_t stackBase = c->stack ? c->stack->index + 1 : 0; + FixedSliceStack slice; + + slicePush(c, + ceilingDivide(firstValue->type.size(c->targetInfo), + c->targetInfo.pointerSize), + firstValue, + stackBase, + slice); + + appendCall(c, + value(c, + ir::Type::addr(), + constantSite(c, + c->client->getThunk( + op, + firstValue->type.size(c->targetInfo), + resultValue->type.size(c->targetInfo)))), + ir::CallingConvention::Native, + 0, + 0, + resultValue, + slice); + } else { + append(c, + new (c->zone) TranslateEvent(c, + op, + firstValue, + resultValue, + SiteMask::lowPart(first), + SiteMask::highPart(first))); + } +} + +class OperationEvent : public Event { + public: + OperationEvent(Context* c, lir::Operation op) : Event(c), op(op) + { + } + + virtual const char* name() + { + return "OperationEvent"; + } + + virtual void compile(Context* c) + { + c->assembler->apply(op); + } + + lir::Operation op; +}; + +void appendOperation(Context* c, lir::Operation op) +{ + append(c, new (c->zone) OperationEvent(c, op)); +} + +ConstantSite* findConstantSite(Context* c, Value* v) +{ + for (SiteIterator it(c, v); it.hasMore();) { + Site* s = it.next(); + if (s->type(c) == lir::Operand::Type::Constant) { + return static_cast(s); + } + } + return 0; +} + +void moveIfConflict(Context* c, Value* v, MemorySite* s) +{ + if (v->reads) { + SiteMask mask(lir::Operand::RegisterPairMask, ~0, AnyFrameIndex); + v->reads->intersect(&mask); + if (s->conflicts(mask)) { + maybeMove(c, v->reads, true, false); + v->removeSite(c, s); + } + } +} + +class MemoryEvent : public Event { + public: + MemoryEvent(Context* c, + Value* base, + int displacement, + Value* index, + unsigned scale, + Value* result) + : Event(c), + base(base), + displacement(displacement), + index(index), + scale(scale), + result(result) + { + this->addRead(c, base, generalRegisterMask(c)); + if (index) { + this->addRead(c, index, generalRegisterOrConstantMask(c)); + } + } + + virtual const char* name() + { + return "MemoryEvent"; + } + + virtual void compile(Context* c) + { + Register indexRegister; + int displacement = this->displacement; + unsigned scale = this->scale; + if (index) { + ConstantSite* constant = findConstantSite(c, index); + + if (constant) { + indexRegister = NoRegister; + displacement += (constant->value->value() * scale); + scale = 1; + } else { + assertT(c, index->source->type(c) == lir::Operand::Type::RegisterPair); + indexRegister = static_cast(index->source)->number; + } + } else { + indexRegister = NoRegister; + } + assertT(c, base->source->type(c) == lir::Operand::Type::RegisterPair); + Register baseRegister = static_cast(base->source)->number; + + popRead(c, this, base); + if (index) { + if (c->targetInfo.pointerSize == 8 and indexRegister != NoRegister) { + apply(c, + lir::Move, + 4, + index->source, + index->source, + 8, + index->source, + index->source); + } + + popRead(c, this, index); + } + + MemorySite* site + = memorySite(c, baseRegister, displacement, indexRegister, scale); + + MemorySite* low; + if (result->nextWord != result) { + MemorySite* high = static_cast(site->copyHigh(c)); + low = static_cast(site->copyLow(c)); + + result->nextWord->target = high; + result->nextWord->addSite(c, high); + moveIfConflict(c, result->nextWord, high); + } else { + low = site; + } + + result->target = low; + result->addSite(c, low); + moveIfConflict(c, result, low); + } + + Value* base; + int displacement; + Value* index; + unsigned scale; + Value* result; +}; + +void appendMemory(Context* c, + Value* base, + int displacement, + Value* index, + unsigned scale, + Value* result) +{ + append(c, + new (c->zone) + MemoryEvent(c, base, displacement, index, scale, result)); +} + +double asFloat(unsigned size, int64_t v) +{ + if (size == 4) { + return vm::bitsToFloat(v); + } else { + return vm::bitsToDouble(v); + } +} + +bool unordered(double a, double b) +{ + return not(a >= b or a < b); +} + +bool shouldJump(Context* c, + lir::TernaryOperation op, + unsigned size, + int64_t b, + int64_t a) +{ + switch (op) { + case lir::JumpIfEqual: + return a == b; + + case lir::JumpIfNotEqual: + return a != b; + + case lir::JumpIfLess: + return a < b; + + case lir::JumpIfGreater: + return a > b; + + case lir::JumpIfLessOrEqual: + return a <= b; + + case lir::JumpIfGreaterOrEqual: + return a >= b; + + case lir::JumpIfFloatEqual: + return asFloat(size, a) == asFloat(size, b); + + case lir::JumpIfFloatNotEqual: + return asFloat(size, a) != asFloat(size, b); + + case lir::JumpIfFloatLess: + return asFloat(size, a) < asFloat(size, b); + + case lir::JumpIfFloatGreater: + return asFloat(size, a) > asFloat(size, b); + + case lir::JumpIfFloatLessOrEqual: + return asFloat(size, a) <= asFloat(size, b); + + case lir::JumpIfFloatGreaterOrEqual: + return asFloat(size, a) >= asFloat(size, b); + + case lir::JumpIfFloatLessOrUnordered: + return asFloat(size, a) < asFloat(size, b) + or unordered(asFloat(size, a), asFloat(size, b)); + + case lir::JumpIfFloatGreaterOrUnordered: + return asFloat(size, a) > asFloat(size, b) + or unordered(asFloat(size, a), asFloat(size, b)); + + case lir::JumpIfFloatLessOrEqualOrUnordered: + return asFloat(size, a) <= asFloat(size, b) + or unordered(asFloat(size, a), asFloat(size, b)); + + case lir::JumpIfFloatGreaterOrEqualOrUnordered: + return asFloat(size, a) >= asFloat(size, b) + or unordered(asFloat(size, a), asFloat(size, b)); + + default: + abort(c); + } +} + +lir::TernaryOperation thunkBranch(Context* c, lir::TernaryOperation op) +{ + switch (op) { + case lir::JumpIfFloatEqual: + return lir::JumpIfEqual; + + case lir::JumpIfFloatNotEqual: + return lir::JumpIfNotEqual; + + case lir::JumpIfFloatLess: + case lir::JumpIfFloatLessOrUnordered: + return lir::JumpIfLess; + + case lir::JumpIfFloatGreater: + case lir::JumpIfFloatGreaterOrUnordered: + return lir::JumpIfGreater; + + case lir::JumpIfFloatLessOrEqual: + case lir::JumpIfFloatLessOrEqualOrUnordered: + return lir::JumpIfLessOrEqual; + + case lir::JumpIfFloatGreaterOrEqual: + case lir::JumpIfFloatGreaterOrEqualOrUnordered: + return lir::JumpIfGreaterOrEqual; + + default: + abort(c); + } +} + +class BranchEvent : public Event { + public: + BranchEvent(Context* c, + lir::TernaryOperation op, + Value* firstValue, + Value* secondValue, + Value* addressValue, + const SiteMask& firstLowMask, + const SiteMask& firstHighMask, + const SiteMask& secondLowMask, + const SiteMask& secondHighMask) + : Event(c), + op(op), + firstValue(firstValue), + secondValue(secondValue), + addressValue(addressValue) + { + this->addReads(c, + firstValue, + firstValue->type.size(c->targetInfo), + firstLowMask, + firstHighMask); + this->addReads(c, + secondValue, + firstValue->type.size(c->targetInfo), + secondLowMask, + secondHighMask); + + OperandMask dstMask; + c->arch->planDestination(op, + firstValue->type.size(c->targetInfo), + OperandMask(0, 0, 0), + firstValue->type.size(c->targetInfo), + OperandMask(0, 0, 0), + c->targetInfo.pointerSize, + dstMask); + + this->addRead(c, addressValue, SiteMask::lowPart(dstMask)); + } + + virtual const char* name() + { + return "BranchEvent"; + } + + virtual void compile(Context* c) + { + ConstantSite* firstConstant = findConstantSite(c, firstValue); + ConstantSite* secondConstant = findConstantSite(c, secondValue); + + if (not this->isUnreachable()) { + if (firstConstant and secondConstant and firstConstant->value->resolved() + and secondConstant->value->resolved()) { + int64_t firstConstVal = firstConstant->value->value(); + int64_t secondConstVal = secondConstant->value->value(); + + if (firstValue->type.size(c->targetInfo) > c->targetInfo.pointerSize) { + firstConstVal + |= findConstantSite(c, firstValue->nextWord)->value->value() + << 32; + secondConstVal + |= findConstantSite(c, secondValue->nextWord)->value->value() + << 32; + } + + if (shouldJump(c, + op, + firstValue->type.size(c->targetInfo), + firstConstVal, + secondConstVal)) { + apply(c, + lir::Jump, + c->targetInfo.pointerSize, + addressValue->source, + addressValue->source); + } + } else { + freezeSource(c, firstValue->type.size(c->targetInfo), firstValue); + freezeSource(c, firstValue->type.size(c->targetInfo), secondValue); + freezeSource(c, c->targetInfo.pointerSize, addressValue); + + apply(c, + op, + firstValue->type.size(c->targetInfo), + firstValue->source, + firstValue->nextWord->source, + firstValue->type.size(c->targetInfo), + secondValue->source, + secondValue->nextWord->source, + c->targetInfo.pointerSize, + addressValue->source, + addressValue->source); + + thawSource(c, c->targetInfo.pointerSize, addressValue); + thawSource(c, firstValue->type.size(c->targetInfo), secondValue); + thawSource(c, firstValue->type.size(c->targetInfo), firstValue); + } + } + + for (Read* r = reads; r; r = r->eventNext) { + popRead(c, this, r->value); + } + } + + virtual bool isBranch() + { + return true; + } + + lir::TernaryOperation op; + Value* firstValue; + Value* secondValue; + Value* addressValue; +}; + +void appendBranch(Context* c, + lir::TernaryOperation op, + Value* firstValue, + Value* secondValue, + Value* addressValue) +{ + bool thunk; + OperandMask firstMask; + OperandMask secondMask; + + c->arch->planSource(op, + firstValue->type.size(c->targetInfo), + firstMask, + firstValue->type.size(c->targetInfo), + secondMask, + c->targetInfo.pointerSize, + &thunk); + + if (thunk) { + const size_t MaxValueCount = 4; + FixedSliceStack slice; + size_t stackBase = c->stack ? c->stack->index + 1 : 0; + + bool threadParameter; + intptr_t handler = c->client->getThunk(op, + firstValue->type.size(c->targetInfo), + firstValue->type.size(c->targetInfo), + &threadParameter); + + assertT(c, not threadParameter); + + slicePush(c, + ceilingDivide(firstValue->type.size(c->targetInfo), + c->targetInfo.pointerSize), + secondValue, + stackBase, + slice); + slicePush(c, + ceilingDivide(firstValue->type.size(c->targetInfo), + c->targetInfo.pointerSize), + firstValue, + stackBase, + slice); + + Value* result = value(c, ir::Type::addr()); + appendCall(c, + value(c, ir::Type::addr(), constantSite(c, handler)), + ir::CallingConvention::Native, + 0, + 0, + result, + slice); + + appendBranch( + c, + thunkBranch(c, op), + value(c, ir::Type::addr(), constantSite(c, static_cast(0))), + result, + addressValue); + } else { + append(c, + new (c->zone) BranchEvent(c, + op, + firstValue, + secondValue, + addressValue, + SiteMask::lowPart(firstMask), + SiteMask::highPart(firstMask), + SiteMask::lowPart(secondMask), + SiteMask::highPart(secondMask))); + } +} + +void clean(Context* c, Value* v, unsigned popIndex) +{ + for (SiteIterator it(c, v); it.hasMore();) { + Site* s = it.next(); + if (not(s->match(c, SiteMask(lir::Operand::MemoryMask, 0, AnyFrameIndex)) + and offsetToFrameIndex(c, static_cast(s)->offset) + >= popIndex)) { + if (false + and s->match(c, + SiteMask(lir::Operand::MemoryMask, 0, AnyFrameIndex))) { + char buffer[256]; + s->toString(c, buffer, 256); + fprintf(stderr, + "remove %s from %p at %d pop offset 0x%x\n", + buffer, + v, + offsetToFrameIndex(c, static_cast(s)->offset), + frameIndexToOffset(c, popIndex)); + } + it.remove(c); + } + } +} + +void clean(Context* c, + Event* e, + Stack* stack, + Local* locals, + Read* reads, + unsigned popIndex) +{ + for (FrameIterator it(c, stack, locals); it.hasMore();) { + FrameIterator::Element e = it.next(c); + clean(c, e.value, popIndex); + } + + for (Read* r = reads; r; r = r->eventNext) { + popRead(c, e, r->value); + } +} + +class JumpEvent : public Event { + public: + JumpEvent(Context* c, + lir::UnaryOperation op, + Value* address, + bool exit, + bool cleanLocals) + : Event(c), op(op), address(address), exit(exit), cleanLocals(cleanLocals) + { + bool thunk; + OperandMask mask; + c->arch->plan(op, c->targetInfo.pointerSize, mask, &thunk); + + assertT(c, not thunk); + + this->addRead(c, address, SiteMask::lowPart(mask)); + } + + virtual const char* name() + { + return "JumpEvent"; + } + + virtual void compile(Context* c) + { + if (not this->isUnreachable()) { + apply(c, op, c->targetInfo.pointerSize, address->source, address->source); + } + + for (Read* r = reads; r; r = r->eventNext) { + popRead(c, this, r->value); + } + + if (cleanLocals) { + for (FrameIterator it(c, 0, c->locals); it.hasMore();) { + FrameIterator::Element e = it.next(c); + clean(c, e.value, 0); + } + } + } + + virtual bool isBranch() + { + return true; + } + + virtual bool allExits() + { + return exit or this->isUnreachable(); + } + + lir::UnaryOperation op; + Value* address; + bool exit; + bool cleanLocals; +}; + +void appendJump(Context* c, + lir::UnaryOperation op, + Value* address, + bool exit, + bool cleanLocals) +{ + append(c, new (c->zone) JumpEvent(c, op, address, exit, cleanLocals)); +} + +class BoundsCheckEvent : public Event { + public: + BoundsCheckEvent(Context* c, + Value* object, + unsigned lengthOffset, + Value* index, + intptr_t handler) + : Event(c), + object(object), + lengthOffset(lengthOffset), + index(index), + handler(handler) + { + this->addRead(c, object, generalRegisterMask(c)); + this->addRead(c, index, generalRegisterOrConstantMask(c)); + } + + virtual const char* name() + { + return "BoundsCheckEvent"; + } + + virtual void compile(Context* c) + { + Assembler* a = c->assembler; + + ConstantSite* constant = findConstantSite(c, index); + CodePromise* outOfBoundsPromise = 0; + + if (constant) { + if (constant->value->value() < 0) { + lir::Constant handlerConstant(resolvedPromise(c, handler)); + a->apply(lir::Call, + OperandInfo(c->targetInfo.pointerSize, + lir::Operand::Type::Constant, + &handlerConstant)); + } + } else { + outOfBoundsPromise = compiler::codePromise(c, static_cast(0)); + + ConstantSite zero(resolvedPromise(c, 0)); + ConstantSite oob(outOfBoundsPromise); + apply(c, + lir::JumpIfLess, + 4, + &zero, + &zero, + 4, + index->source, + index->source, + c->targetInfo.pointerSize, + &oob, + &oob); + } + + if (constant == 0 or constant->value->value() >= 0) { + assertT(c, object->source->type(c) == lir::Operand::Type::RegisterPair); + MemorySite length(static_cast(object->source)->number, + lengthOffset, + NoRegister, + 1); + length.acquired = true; + + CodePromise* nextPromise + = compiler::codePromise(c, static_cast(0)); + + freezeSource(c, c->targetInfo.pointerSize, index); + + ConstantSite next(nextPromise); + apply(c, + lir::JumpIfGreater, + 4, + index->source, + index->source, + 4, + &length, + &length, + c->targetInfo.pointerSize, + &next, + &next); + + thawSource(c, c->targetInfo.pointerSize, index); + + if (constant == 0) { + outOfBoundsPromise->offset = a->offset(); + } + + lir::Constant handlerConstant(resolvedPromise(c, handler)); + a->apply(lir::Call, + OperandInfo(c->targetInfo.pointerSize, + lir::Operand::Type::Constant, + &handlerConstant)); + + nextPromise->offset = a->offset(); + } + + popRead(c, this, object); + popRead(c, this, index); + } + + Value* object; + unsigned lengthOffset; + Value* index; + intptr_t handler; +}; + +void appendBoundsCheck(Context* c, + Value* object, + unsigned lengthOffset, + Value* index, + intptr_t handler) +{ + append(c, + new (c->zone) + BoundsCheckEvent(c, object, lengthOffset, index, handler)); +} + +class FrameSiteEvent : public Event { + public: + FrameSiteEvent(Context* c, Value* value, int index) + : Event(c), value(value), index(index) + { + } + + virtual const char* name() + { + return "FrameSiteEvent"; + } + + virtual void compile(Context* c) + { + if (live(c, value)) { + value->addSite(c, frameSite(c, index)); + } + } + + Value* value; + int index; +}; + +void appendFrameSite(Context* c, Value* value, int index) +{ + append(c, new (c->zone) FrameSiteEvent(c, value, index)); +} + +class SaveLocalsEvent : public Event { + public: + SaveLocalsEvent(Context* c) : Event(c) + { + saveLocals(c, this); + } + + virtual const char* name() + { + return "SaveLocalsEvent"; + } + + virtual void compile(Context* c) + { + for (Read* r = reads; r; r = r->eventNext) { + popRead(c, this, r->value); + } + } +}; + +void appendSaveLocals(Context* c) +{ + append(c, new (c->zone) SaveLocalsEvent(c)); +} + +class DummyEvent : public Event { + public: + DummyEvent(Context* c, Local* locals) : Event(c), locals_(locals) + { + } + + virtual const char* name() + { + return "DummyEvent"; + } + + virtual void compile(Context*) + { + } + + virtual Local* locals() + { + return locals_; + } + + Local* locals_; +}; + +void appendDummy(Context* c) +{ + Stack* stack = c->stack; + Local* locals = c->locals; + LogicalInstruction* i = c->logicalCode[c->logicalIp]; + + c->stack = i->stack; + c->locals = i->locals; + + append(c, new (c->zone) DummyEvent(c, locals)); + + c->stack = stack; + c->locals = locals; +} + +} // namespace compiler +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/compiler/event.h b/sgx-jvm/avian/src/codegen/compiler/event.h new file mode 100644 index 0000000000..8aa68bec11 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/compiler/event.h @@ -0,0 +1,214 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_COMPILER_EVENT_H +#define AVIAN_CODEGEN_COMPILER_EVENT_H + +namespace avian { +namespace codegen { +namespace compiler { + +class Context; +class CodePromise; +class Snapshot; +class Link; +class Site; +class StubRead; + +const bool DebugReads = false; +const bool DebugMoves = false; + +class Event { + public: + Event(Context* c); + + virtual const char* name() = 0; + + virtual void compile(Context* c) = 0; + + virtual bool isBranch() + { + return false; + } + + virtual bool allExits() + { + return false; + } + + virtual Local* locals() + { + return localsBefore; + } + + void addRead(Context* c, Value* v, Read* r); + + void addRead(Context* c, + Value* v, + const SiteMask& mask, + Value* successor = 0); + + void addReads(Context* c, + Value* v, + unsigned size, + const SiteMask& lowMask, + Value* lowSuccessor, + const SiteMask& highMask, + Value* highSuccessor); + + void addReads(Context* c, + Value* v, + unsigned size, + const SiteMask& lowMask, + const SiteMask& highMask); + + CodePromise* makeCodePromise(Context* c); + + bool isUnreachable(); + + Event* next; + Stack* stackBefore; + Local* localsBefore; + Stack* stackAfter; + Local* localsAfter; + CodePromise* promises; + Read* reads; + Site** junctionSites; + Snapshot* snapshots; + Link* predecessors; + Link* successors; + List* visitLinks; + Block* block; + LogicalInstruction* logicalInstruction; + unsigned readCount; +}; + +void finishAddRead(Context* c, Value* v, Read* r); + +class StubReadPair { + public: + Value* value; + StubRead* read; +}; + +class JunctionState { + public: + JunctionState(unsigned frameFootprint) : frameFootprint(frameFootprint) + { + } + + unsigned frameFootprint; + StubReadPair reads[0]; +}; + +class Link { + public: + Link(Event* predecessor, + Link* nextPredecessor, + Event* successor, + Link* nextSuccessor, + ForkState* forkState) + : predecessor(predecessor), + nextPredecessor(nextPredecessor), + successor(successor), + nextSuccessor(nextSuccessor), + forkState(forkState), + junctionState(0) + { + } + + unsigned countPredecessors(); + Link* lastPredecessor(); + unsigned countSuccessors(); + + Event* predecessor; + Link* nextPredecessor; + Event* successor; + Link* nextSuccessor; + ForkState* forkState; + JunctionState* junctionState; +}; + +Link* link(Context* c, + Event* predecessor, + Link* nextPredecessor, + Event* successor, + Link* nextSuccessor, + ForkState* forkState); + +void appendCall(Context* c, + Value* address, + ir::CallingConvention callingConvention, + unsigned flags, + TraceHandler* traceHandler, + Value* result, + util::Slice arguments); + +void appendReturn(Context* c, Value* value); + +void appendMove(Context* c, + lir::BinaryOperation op, + unsigned srcSize, + unsigned srcSelectSize, + Value* src, + unsigned dstSize, + Value* dst); + +void appendCombine(Context* c, + lir::TernaryOperation op, + Value* first, + Value* second, + Value* result); + +void appendTranslate(Context* c, + lir::BinaryOperation op, + Value* first, + Value* result); + +void appendOperation(Context* c, lir::Operation op); + +void appendMemory(Context* c, + Value* base, + int displacement, + Value* index, + unsigned scale, + Value* result); + +void appendBranch(Context* c, + lir::TernaryOperation op, + Value* first, + Value* second, + Value* address); + +void appendJump(Context* c, + lir::UnaryOperation op, + Value* address, + bool exit = false, + bool cleanLocals = false); + +void appendBoundsCheck(Context* c, + Value* object, + unsigned lengthOffset, + Value* index, + intptr_t handler); + +void appendFrameSite(Context* c, Value* value, int index); + +void appendSaveLocals(Context* c); + +void appendDummy(Context* c); + +void appendBuddy(Context* c, Value* original, Value* buddy); + +} // namespace compiler +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_COMPILER_EVENT_H diff --git a/sgx-jvm/avian/src/codegen/compiler/frame.cpp b/sgx-jvm/avian/src/codegen/compiler/frame.cpp new file mode 100644 index 0000000000..e6b6832f9c --- /dev/null +++ b/sgx-jvm/avian/src/codegen/compiler/frame.cpp @@ -0,0 +1,132 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/target.h" + +#include "codegen/compiler/context.h" +#include "codegen/compiler/frame.h" + +#include + +namespace avian { +namespace codegen { +namespace compiler { + +unsigned totalFrameSize(Context* c) +{ + return c->alignedFrameSize + c->arch->frameHeaderSize() + + c->arch->argumentFootprint(c->parameterFootprint); +} + +int frameIndex(Context* c, int localIndex) +{ + assertT(c, localIndex >= 0); + + int index = c->alignedFrameSize + c->parameterFootprint - localIndex - 1; + + if (localIndex < static_cast(c->parameterFootprint)) { + index += c->arch->frameHeaderSize(); + } else { + index -= c->arch->frameFooterSize(); + } + + assertT(c, index >= 0); + assertT(c, static_cast(index) < totalFrameSize(c)); + + return index; +} + +unsigned frameIndexToOffset(Context* c, unsigned frameIndex) +{ + assertT(c, frameIndex < totalFrameSize(c)); + + return (frameIndex + c->arch->frameFooterSize()) * c->targetInfo.pointerSize; +} + +unsigned offsetToFrameIndex(Context* c, unsigned offset) +{ + assertT(c, + static_cast((offset / c->targetInfo.pointerSize) + - c->arch->frameFooterSize()) >= 0); + assertT(c, + ((offset / c->targetInfo.pointerSize) - c->arch->frameFooterSize()) + < totalFrameSize(c)); + + return (offset / c->targetInfo.pointerSize) - c->arch->frameFooterSize(); +} + +unsigned frameBase(Context* c) +{ + return c->alignedFrameSize - c->arch->frameReturnAddressSize() + - c->arch->frameFooterSize() + c->arch->frameHeaderSize(); +} + +FrameIterator::Element::Element(Value* value, unsigned localIndex) + : value(value), localIndex(localIndex) +{ +} + +int FrameIterator::Element::frameIndex(Context* c) +{ + return compiler::frameIndex(c, this->localIndex); +} + +FrameIterator::FrameIterator(Context* c, + Stack* stack, + Local* locals, + bool includeEmpty) + : stack(stack), + locals(locals), + localIndex(c->localFootprint - 1), + includeEmpty(includeEmpty) +{ +} + +bool FrameIterator::hasMore() +{ + if (not includeEmpty) { + while (stack and stack->value == 0) { + stack = stack->next; + } + + while (localIndex >= 0 and locals[localIndex].value == 0) { + --localIndex; + } + } + + return stack != 0 or localIndex >= 0; +} + +FrameIterator::Element FrameIterator::next(Context* c) +{ + Value* v; + unsigned li; + if (stack) { + Stack* s = stack; + v = s->value; + li = s->index + c->localFootprint; + stack = stack->next; + } else { + Local* l = locals + localIndex; + v = l->value; + li = localIndex; + --localIndex; + } + return Element(v, li); +} + +Stack* stack(Context* c, Value* value, Stack* next) +{ + return new (c->zone) Stack(next ? next->index + 1 : 0, value, next); +} + +} // namespace compiler +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/compiler/frame.h b/sgx-jvm/avian/src/codegen/compiler/frame.h new file mode 100644 index 0000000000..2f4bdb58a5 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/compiler/frame.h @@ -0,0 +1,78 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_COMPILER_FRAME_H +#define AVIAN_CODEGEN_COMPILER_FRAME_H + +namespace avian { +namespace codegen { +namespace compiler { + +unsigned totalFrameSize(Context* c); + +int frameIndex(Context* c, int localIndex); + +unsigned frameIndexToOffset(Context* c, unsigned frameIndex); + +unsigned offsetToFrameIndex(Context* c, unsigned offset); + +unsigned frameBase(Context* c); + +class FrameIterator { + public: + class Element { + public: + Element(Value* value, unsigned localIndex); + + int frameIndex(Context* c); + + Value* const value; + const unsigned localIndex; + }; + + FrameIterator(Context* c, + Stack* stack, + Local* locals, + bool includeEmpty = false); + + bool hasMore(); + + Element next(Context* c); + + Stack* stack; + Local* locals; + int localIndex; + bool includeEmpty; +}; + +class Local { + public: + Value* value; +}; + +class Stack { + public: + Stack(unsigned index, Value* value, Stack* next) + : index(index), value(value), next(next) + { + } + + unsigned index; + Value* value; + Stack* next; +}; + +Stack* stack(Context* c, Value* value, Stack* next); + +} // namespace compiler +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_COMPILER_FRAME_H diff --git a/sgx-jvm/avian/src/codegen/compiler/ir.cpp b/sgx-jvm/avian/src/codegen/compiler/ir.cpp new file mode 100644 index 0000000000..fe7b58a5de --- /dev/null +++ b/sgx-jvm/avian/src/codegen/compiler/ir.cpp @@ -0,0 +1,57 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "codegen/compiler/context.h" +#include "codegen/compiler/ir.h" + +namespace avian { +namespace codegen { +namespace compiler { + +LogicalInstruction::LogicalInstruction(int index, Stack* stack, Local* locals) + : firstEvent(0), + lastEvent(0), + immediatePredecessor(0), + stack(stack), + locals(locals), + machineOffset(0), + /*subroutine(0), */ index(index) +{ +} + +LogicalInstruction* LogicalInstruction::next(Context* c) +{ + LogicalInstruction* i = this; + for (size_t n = i->index + 1; n < c->logicalCode.count(); ++n) { + i = c->logicalCode[n]; + if (i) + return i; + } + return 0; +} + +unsigned machineOffset(Context* c, int logicalIp) +{ + return c->logicalCode[logicalIp]->machineOffset->value(); +} + +Block::Block(Event* head) + : head(head), nextBlock(0), nextInstruction(0), assemblerBlock(0), start(0) +{ +} + +Block* block(Context* c, Event* head) +{ + return new (c->zone) Block(head); +} + +} // namespace compiler +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/compiler/ir.h b/sgx-jvm/avian/src/codegen/compiler/ir.h new file mode 100644 index 0000000000..cd52fe063e --- /dev/null +++ b/sgx-jvm/avian/src/codegen/compiler/ir.h @@ -0,0 +1,86 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_COMPILER_IR_H +#define AVIAN_CODEGEN_COMPILER_IR_H + +namespace avian { +namespace codegen { +namespace compiler { + +class MultiRead; + +class ForkElement { + public: + Value* value; + MultiRead* read; + bool local; +}; + +class ForkState : public Compiler::State { + public: + ForkState(Stack* stack, + Local* locals, + List* saved, + Event* predecessor, + unsigned logicalIp) + : stack(stack), + locals(locals), + saved(saved), + predecessor(predecessor), + logicalIp(logicalIp), + readCount(0) + { + } + + Stack* stack; + Local* locals; + List* saved; + Event* predecessor; + unsigned logicalIp; + unsigned readCount; + ForkElement elements[0]; +}; + +class LogicalInstruction { + public: + LogicalInstruction(int index, Stack* stack, Local* locals); + + LogicalInstruction* next(Context* c); + + Event* firstEvent; + Event* lastEvent; + LogicalInstruction* immediatePredecessor; + Stack* stack; + Local* locals; + Promise* machineOffset; + int index; +}; + +class Block { + public: + Block(Event* head); + + Event* head; + Block* nextBlock; + LogicalInstruction* nextInstruction; + Assembler::Block* assemblerBlock; + unsigned start; +}; + +Block* block(Context* c, Event* head); + +unsigned machineOffset(Context* c, int logicalIp); + +} // namespace compiler +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_COMPILER_IR_H diff --git a/sgx-jvm/avian/src/codegen/compiler/promise.cpp b/sgx-jvm/avian/src/codegen/compiler/promise.cpp new file mode 100644 index 0000000000..3fff9d6afa --- /dev/null +++ b/sgx-jvm/avian/src/codegen/compiler/promise.cpp @@ -0,0 +1,133 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/target.h" + +#include "codegen/compiler/context.h" +#include "codegen/compiler/promise.h" +#include "codegen/compiler/ir.h" + +namespace avian { +namespace codegen { +namespace compiler { + +CodePromise::CodePromise(Context* c, CodePromise* next) + : c(c), offset(0), next(next) +{ +} + +CodePromise::CodePromise(Context* c, Promise* offset) + : c(c), offset(offset), next(0) +{ +} + +int64_t CodePromise::value() +{ + if (resolved()) { + return reinterpret_cast(c->machineCode + offset->value()); + } + + abort(c); +} + +bool CodePromise::resolved() +{ + return c->machineCode != 0 and offset and offset->resolved(); +} + +CodePromise* codePromise(Context* c, Promise* offset) +{ + return new (c->zone) CodePromise(c, offset); +} + +Promise* shiftMaskPromise(Context* c, + Promise* base, + unsigned shift, + int64_t mask) +{ + return new (c->zone) ShiftMaskPromise(base, shift, mask); +} + +Promise* combinedPromise(Context* c, Promise* low, Promise* high) +{ + return new (c->zone) CombinedPromise(low, high); +} + +Promise* resolvedPromise(Context* c, int64_t value) +{ + return new (c->zone) ResolvedPromise(value); +} + +class IpPromise : public Promise { + public: + IpPromise(Context* c, int logicalIp) : c(c), logicalIp(logicalIp) + { + } + + virtual int64_t value() + { + if (resolved()) { + return reinterpret_cast(c->machineCode + + machineOffset(c, logicalIp)); + } + + abort(c); + } + + virtual bool resolved() + { + return c->machineCode != 0 + and c->logicalCode[logicalIp]->machineOffset->resolved(); + } + + Context* c; + int logicalIp; +}; + +Promise* ipPromise(Context* c, int logicalIp) +{ + return new (c->zone) IpPromise(c, logicalIp); +} + +class PoolPromise : public Promise { + public: + PoolPromise(Context* c, int key) : c(c), key(key) + { + } + + virtual int64_t value() + { + if (resolved()) { + return reinterpret_cast( + c->machineCode + + vm::pad(c->machineCodeSize, c->targetInfo.pointerSize) + + (key * c->targetInfo.pointerSize)); + } + + abort(c); + } + + virtual bool resolved() + { + return c->machineCode != 0; + } + + Context* c; + int key; +}; + +Promise* poolPromise(Context* c, int key) +{ + return new (c->zone) PoolPromise(c, key); +} + +} // namespace compiler +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/compiler/promise.h b/sgx-jvm/avian/src/codegen/compiler/promise.h new file mode 100644 index 0000000000..928b7cd11e --- /dev/null +++ b/sgx-jvm/avian/src/codegen/compiler/promise.h @@ -0,0 +1,52 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_COMPILER_PROMISE_H +#define AVIAN_CODEGEN_COMPILER_PROMISE_H + +namespace avian { +namespace codegen { +namespace compiler { + +class CodePromise : public Promise { + public: + CodePromise(Context* c, CodePromise* next); + + CodePromise(Context* c, Promise* offset); + + virtual int64_t value(); + + virtual bool resolved(); + + Context* c; + Promise* offset; + CodePromise* next; +}; + +CodePromise* codePromise(Context* c, Promise* offset); + +Promise* shiftMaskPromise(Context* c, + Promise* base, + unsigned shift, + int64_t mask); + +Promise* combinedPromise(Context* c, Promise* low, Promise* high); + +Promise* resolvedPromise(Context* c, int64_t value); + +Promise* ipPromise(Context* c, int logicalIp); + +Promise* poolPromise(Context* c, int key); + +} // namespace compiler +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_COMPILER_PROMISE_H diff --git a/sgx-jvm/avian/src/codegen/compiler/read.cpp b/sgx-jvm/avian/src/codegen/compiler/read.cpp new file mode 100644 index 0000000000..5dfaba2777 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/compiler/read.cpp @@ -0,0 +1,215 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/target.h" + +#include "codegen/compiler/context.h" +#include "codegen/compiler/value.h" +#include "codegen/compiler/site.h" +#include "codegen/compiler/resource.h" +#include "codegen/compiler/read.h" + +namespace avian { +namespace codegen { +namespace compiler { + +SingleRead::SingleRead(const SiteMask& mask, Value* successor) + : next_(0), mask(mask), high_(0), successor_(successor) +{ +} + +bool SingleRead::intersect(SiteMask* mask, unsigned) +{ + *mask = mask->intersectionWith(this->mask); + + return true; +} + +Value* SingleRead::high(Context*) +{ + return high_; +} + +Value* SingleRead::successor() +{ + return successor_; +} + +bool SingleRead::valid() +{ + return true; +} + +void SingleRead::append(Context* c UNUSED, Read* r) +{ + assertT(c, next_ == 0); + next_ = r; +} + +Read* SingleRead::next(Context*) +{ + return next_; +} + +MultiRead::MultiRead() + : reads(0), lastRead(0), firstTarget(0), lastTarget(0), visited(false) +{ +} + +bool MultiRead::intersect(SiteMask* mask, unsigned depth) +{ + if (depth > 0) { + // short-circuit recursion to avoid poor performance in + // deeply-nested branches + return reads != 0; + } + + bool result = false; + if (not visited) { + visited = true; + for (List** cell = &reads; *cell;) { + Read* r = (*cell)->item; + bool valid = r->intersect(mask, depth + 1); + if (valid) { + result = true; + cell = &((*cell)->next); + } else { + *cell = (*cell)->next; + } + } + visited = false; + } + return result; +} + +Value* MultiRead::successor() +{ + return 0; +} + +bool MultiRead::valid() +{ + bool result = false; + if (not visited) { + visited = true; + for (List** cell = &reads; *cell;) { + Read* r = (*cell)->item; + if (r->valid()) { + result = true; + cell = &((*cell)->next); + } else { + *cell = (*cell)->next; + } + } + visited = false; + } + return result; +} + +void MultiRead::append(Context* c, Read* r) +{ + List* cell = cons(c, r, 0); + if (lastRead == 0) { + reads = cell; + } else { + lastRead->next = cell; + } + lastRead = cell; + + if (false) { + fprintf(stderr, "append %p to %p for %p\n", r, lastTarget, this); + } + + lastTarget->item = r; +} + +Read* MultiRead::next(Context* c) +{ + abort(c); +} + +void MultiRead::allocateTarget(Context* c) +{ + List* cell = cons(c, 0, 0); + + if (false) { + fprintf(stderr, "allocate target for %p: %p\n", this, cell); + } + + if (lastTarget) { + lastTarget->next = cell; + } else { + firstTarget = cell; + } + lastTarget = cell; +} + +Read* MultiRead::nextTarget() +{ + if (false) { + fprintf(stderr, "next target for %p: %p\n", this, firstTarget); + } + + Read* r = firstTarget->item; + firstTarget = firstTarget->next; + return r; +} + +StubRead::StubRead() : next_(0), read(0), visited(false), valid_(true) +{ +} + +bool StubRead::intersect(SiteMask* mask, unsigned depth) +{ + if (not visited) { + visited = true; + if (read) { + bool valid = read->intersect(mask, depth); + if (not valid) { + read = 0; + } + } + visited = false; + } + return valid_; +} + +Value* StubRead::successor() +{ + return 0; +} + +bool StubRead::valid() +{ + return valid_; +} + +void StubRead::append(Context* c UNUSED, Read* r) +{ + assertT(c, next_ == 0); + next_ = r; +} + +Read* StubRead::next(Context*) +{ + return next_; +} + +SingleRead* read(Context* c, const SiteMask& mask, Value* successor) +{ + assertT(c, + (mask.typeMask != lir::Operand::MemoryMask) or mask.frameIndex >= 0); + + return new (c->zone) SingleRead(mask, successor); +} + +} // namespace compiler +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/compiler/read.h b/sgx-jvm/avian/src/codegen/compiler/read.h new file mode 100644 index 0000000000..80b8983a2f --- /dev/null +++ b/sgx-jvm/avian/src/codegen/compiler/read.h @@ -0,0 +1,127 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_COMPILER_READ_H +#define AVIAN_CODEGEN_COMPILER_READ_H + +namespace avian { +namespace codegen { +namespace compiler { + +class Context; +class SiteMask; +class Value; +class Event; + +class Read { + public: + Read() : value(0), event(0), eventNext(0) + { + } + + virtual bool intersect(SiteMask* mask, unsigned depth = 0) = 0; + + virtual Value* high(Context* c) + { + abort(c); + } + + virtual Value* successor() = 0; + + virtual bool valid() = 0; + + virtual void append(Context* c, Read* r) = 0; + + virtual Read* next(Context* c) = 0; + + Value* value; + Event* event; + Read* eventNext; +}; + +inline bool valid(Read* r) +{ + return r and r->valid(); +} + +class SingleRead : public Read { + public: + SingleRead(const SiteMask& mask, Value* successor); + + virtual bool intersect(SiteMask* mask, unsigned); + + virtual Value* high(Context*); + + virtual Value* successor(); + + virtual bool valid(); + + virtual void append(Context* c UNUSED, Read* r); + + virtual Read* next(Context*); + + Read* next_; + SiteMask mask; + Value* high_; + Value* successor_; +}; + +class MultiRead : public Read { + public: + MultiRead(); + + virtual bool intersect(SiteMask* mask, unsigned depth); + + virtual Value* successor(); + + virtual bool valid(); + + virtual void append(Context* c, Read* r); + + virtual Read* next(Context* c); + + void allocateTarget(Context* c); + + Read* nextTarget(); + + List* reads; + List* lastRead; + List* firstTarget; + List* lastTarget; + bool visited; +}; + +class StubRead : public Read { + public: + StubRead(); + + virtual bool intersect(SiteMask* mask, unsigned depth); + + virtual Value* successor(); + + virtual bool valid(); + + virtual void append(Context* c UNUSED, Read* r); + + virtual Read* next(Context*); + + Read* next_; + Read* read; + bool visited; + bool valid_; +}; + +SingleRead* read(Context* c, const SiteMask& mask, Value* successor = 0); + +} // namespace compiler +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_COMPILER_READ_H diff --git a/sgx-jvm/avian/src/codegen/compiler/regalloc.cpp b/sgx-jvm/avian/src/codegen/compiler/regalloc.cpp new file mode 100644 index 0000000000..788cab3dd3 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/compiler/regalloc.cpp @@ -0,0 +1,320 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/target.h" + +#include "codegen/compiler/regalloc.h" +#include "codegen/compiler/context.h" +#include "codegen/compiler/site.h" +#include "codegen/compiler/resource.h" +#include "codegen/compiler/read.h" + +namespace avian { +namespace codegen { +namespace compiler { + +RegisterAllocator::RegisterAllocator(Aborter* a, + const RegisterFile* registerFile) + : a(a), registerFile(registerFile) +{ +} + +unsigned totalFrameSize(Context* c); +Read* live(Context* c UNUSED, Value* v); + +unsigned resourceCost(Context* c, + Value* v, + Resource* r, + SiteMask mask, + CostCalculator* costCalculator) +{ + if (r->reserved or r->freezeCount or r->referenceCount) { + return Target::Impossible; + } else { + unsigned baseCost = costCalculator ? costCalculator->cost(c, mask) : 0; + + if (r->value) { + assertT(c, r->value->findSite(r->site)); + + if (v and r->value->isBuddyOf(v)) { + return baseCost; + } else if (r->value->uniqueSite(c, r->site)) { + return baseCost + Target::StealUniquePenalty; + } else { + return baseCost = Target::StealPenalty; + } + } else { + return baseCost; + } + } +} + +bool pickRegisterTarget(Context* c, + Register i, + Value* v, + RegisterMask mask, + Register* target, + unsigned* cost, + CostCalculator* costCalculator) +{ + if (mask.contains(i)) { + RegisterResource* r = c->registerResources + i.index(); + unsigned myCost + = resourceCost( + c, + v, + r, + SiteMask(lir::Operand::RegisterPairMask, RegisterMask(i), NoFrameIndex), + costCalculator) + Target::MinimumRegisterCost; + + if (mask.containsExactly(i)) { + *cost = myCost; + return true; + } else if (myCost < *cost) { + *cost = myCost; + *target = i; + } + } + return false; +} + +Register pickRegisterTarget(Context* c, + Value* v, + RegisterMask mask, + unsigned* cost, + CostCalculator* costCalculator) +{ + Register target = NoRegister; + *cost = Target::Impossible; + + if (mask & c->regFile->generalRegisters) { + for (Register i : c->regFile->generalRegisters) { + if (pickRegisterTarget(c, i, v, mask, &target, cost, costCalculator)) { + return i; + } + } + } + + if (mask & c->regFile->floatRegisters) { + for (Register i : c->regFile->floatRegisters) { + if (pickRegisterTarget(c, i, v, mask, &target, cost, costCalculator)) { + return i; + } + } + } + + return target; +} + +Target pickRegisterTarget(Context* c, + Value* v, + RegisterMask mask, + CostCalculator* costCalculator) +{ + unsigned cost; + Register number = pickRegisterTarget(c, v, mask, &cost, costCalculator); + return Target(number, cost); +} + +unsigned frameCost(Context* c, + Value* v, + int frameIndex, + CostCalculator* costCalculator) +{ + return resourceCost(c, + v, + c->frameResources + frameIndex, + SiteMask(lir::Operand::MemoryMask, 0, frameIndex), + costCalculator) + Target::MinimumFrameCost; +} + +Target pickFrameTarget(Context* c, Value* v, CostCalculator* costCalculator) +{ + Target best; + + Value* p = v; + do { + if (p->home >= 0) { + Target mine(p->home, + lir::Operand::Type::Memory, + frameCost(c, v, p->home, costCalculator)); + + if (mine.cost == Target::MinimumFrameCost) { + return mine; + } else if (mine.cost < best.cost) { + best = mine; + } + } + p = p->buddy; + } while (p != v); + + return best; +} + +Target pickAnyFrameTarget(Context* c, Value* v, CostCalculator* costCalculator) +{ + Target best; + + unsigned count = totalFrameSize(c); + for (unsigned i = 0; i < count; ++i) { + Target mine(i, lir::Operand::Type::Memory, frameCost(c, v, i, costCalculator)); + if (mine.cost == Target::MinimumFrameCost) { + return mine; + } else if (mine.cost < best.cost) { + best = mine; + } + } + + return best; +} + +Target pickTarget(Context* c, + Value* value, + const SiteMask& mask, + unsigned registerPenalty, + Target best, + CostCalculator* costCalculator) +{ + if (mask.typeMask & lir::Operand::RegisterPairMask) { + Target mine + = pickRegisterTarget(c, value, mask.registerMask, costCalculator); + + mine.cost += registerPenalty; + if (mine.cost == Target::MinimumRegisterCost) { + return mine; + } else if (mine.cost < best.cost) { + best = mine; + } + } + + if (mask.typeMask & lir::Operand::MemoryMask) { + if (mask.frameIndex >= 0) { + Target mine(mask.frameIndex, + lir::Operand::Type::Memory, + frameCost(c, value, mask.frameIndex, costCalculator)); + if (mine.cost == Target::MinimumFrameCost) { + return mine; + } else if (mine.cost < best.cost) { + best = mine; + } + } else if (mask.frameIndex == AnyFrameIndex) { + Target mine = pickFrameTarget(c, value, costCalculator); + if (mine.cost == Target::MinimumFrameCost) { + return mine; + } else if (mine.cost < best.cost) { + best = mine; + } + } + } + + return best; +} + +Target pickTarget(Context* c, + Read* read, + bool intersectRead, + unsigned registerReserveCount, + CostCalculator* costCalculator) +{ + unsigned registerPenalty + = (c->availableGeneralRegisterCount > registerReserveCount + ? 0 + : Target::LowRegisterPenalty); + + Value* value = read->value; + + RegisterMask registerMask + = (isFloatValue(value) ? AnyRegisterMask : (RegisterMask)c->regFile->generalRegisters); + + SiteMask mask(~0, registerMask, AnyFrameIndex); + read->intersect(&mask); + + if (isFloatValue(value)) { + RegisterMask floatMask = mask.registerMask & c->regFile->floatRegisters; + if (floatMask) { + mask.registerMask = floatMask; + } + } + + Target best; + + Value* successor = read->successor(); + if (successor) { + Read* r = live(c, successor); + if (r) { + SiteMask intersection = mask; + if (r->intersect(&intersection)) { + best = pickTarget( + c, value, intersection, registerPenalty, best, costCalculator); + + if (best.cost <= Target::MinimumFrameCost) { + return best; + } + } + } + } + + best = pickTarget(c, value, mask, registerPenalty, best, costCalculator); + if (best.cost <= Target::MinimumFrameCost) { + return best; + } + + if (intersectRead) { + if (best.cost == Target::Impossible) { + fprintf(stderr, + "mask type %d reg %" LLD " frame %d\n", + mask.typeMask, + (uint64_t)mask.registerMask, + mask.frameIndex); + abort(c); + } + return best; + } + + { + Target mine = pickRegisterTarget(c, value, registerMask, costCalculator); + + mine.cost += registerPenalty; + + if (mine.cost == Target::MinimumRegisterCost) { + return mine; + } else if (mine.cost < best.cost) { + best = mine; + } + } + + { + Target mine = pickFrameTarget(c, value, costCalculator); + if (mine.cost == Target::MinimumFrameCost) { + return mine; + } else if (mine.cost < best.cost) { + best = mine; + } + } + + if (best.cost >= Target::StealUniquePenalty + and c->availableGeneralRegisterCount == 0) { + // there are no free registers left, so moving from memory to + // memory isn't an option - try harder to find an available frame + // site: + best = pickAnyFrameTarget(c, value, costCalculator); + assertT(c, best.cost <= 3); + } + + if (best.cost == Target::Impossible) { + abort(c); + } + + return best; +} + +} // namespace regalloc +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/compiler/regalloc.h b/sgx-jvm/avian/src/codegen/compiler/regalloc.h new file mode 100644 index 0000000000..a50b2e1ccc --- /dev/null +++ b/sgx-jvm/avian/src/codegen/compiler/regalloc.h @@ -0,0 +1,129 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_COMPILER_REGALLOC_H +#define AVIAN_CODEGEN_COMPILER_REGALLOC_H + +#include "avian/common.h" + +#include +#include + +namespace avian { + +namespace util { +class Aborter; +} // namespace util + +namespace codegen { +namespace compiler { + +using namespace avian::util; + +class Context; +class Value; +class SiteMask; +class Resource; +class Read; + +class RegisterAllocator { + public: + Aborter* a; + const RegisterFile* registerFile; + + RegisterAllocator(Aborter* a, const RegisterFile* registerFile); +}; + +class Target { + public: + static const unsigned MinimumRegisterCost = 0; + static const unsigned MinimumFrameCost = 1; + static const unsigned StealPenalty = 2; + static const unsigned StealUniquePenalty = 4; + static const unsigned IndirectMovePenalty = 4; + static const unsigned LowRegisterPenalty = 10; + static const unsigned Impossible = 20; + + Target() : cost(Impossible) + { + } + + Target(int16_t index, lir::Operand::Type type, unsigned cost) + : index(index), type(type), cost(cost) + { + } + + Target(Register reg, unsigned cost) + : index(reg.index()), type(lir::Operand::Type::RegisterPair), cost(cost) + { + } + + int16_t index; + lir::Operand::Type type; + uint8_t cost; +}; + +class CostCalculator { + public: + virtual unsigned cost(Context* c, SiteMask mask) = 0; +}; + +unsigned resourceCost(Context* c, + Value* v, + Resource* r, + SiteMask mask, + CostCalculator* costCalculator); + +bool pickRegisterTarget(Context* c, + Register i, + Value* v, + RegisterMask mask, + Register* target, + unsigned* cost, + CostCalculator* costCalculator = 0); + +Register pickRegisterTarget(Context* c, + Value* v, + RegisterMask mask, + unsigned* cost, + CostCalculator* costCalculator = 0); + +Target pickRegisterTarget(Context* c, + Value* v, + RegisterMask mask, + CostCalculator* costCalculator = 0); + +unsigned frameCost(Context* c, + Value* v, + int frameIndex, + CostCalculator* costCalculator); + +Target pickFrameTarget(Context* c, Value* v, CostCalculator* costCalculator); + +Target pickAnyFrameTarget(Context* c, Value* v, CostCalculator* costCalculator); + +Target pickTarget(Context* c, + Value* value, + const SiteMask& mask, + unsigned registerPenalty, + Target best, + CostCalculator* costCalculator); + +Target pickTarget(Context* c, + Read* read, + bool intersectRead, + unsigned registerReserveCount, + CostCalculator* costCalculator); + +} // namespace regalloc +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_COMPILER_REGALLOC_H diff --git a/sgx-jvm/avian/src/codegen/compiler/resource.cpp b/sgx-jvm/avian/src/codegen/compiler/resource.cpp new file mode 100644 index 0000000000..85b49d604c --- /dev/null +++ b/sgx-jvm/avian/src/codegen/compiler/resource.cpp @@ -0,0 +1,249 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "codegen/compiler/context.h" +#include "codegen/compiler/resource.h" +#include "codegen/compiler/value.h" + +namespace avian { +namespace codegen { +namespace compiler { + +const bool DebugResources = false; + +void steal(Context* c, Resource* r, Value* thief); + +void decrementAvailableGeneralRegisterCount(Context* c) +{ + assertT(c, c->availableGeneralRegisterCount); + --c->availableGeneralRegisterCount; + + if (DebugResources) { + fprintf( + stderr, "%d registers available\n", c->availableGeneralRegisterCount); + } +} + +void incrementAvailableGeneralRegisterCount(Context* c) +{ + ++c->availableGeneralRegisterCount; + + if (DebugResources) { + fprintf( + stderr, "%d registers available\n", c->availableGeneralRegisterCount); + } +} + +void freezeResource(Context* c, Resource* r, Value* v) +{ + if (DebugResources) { + char buffer[256]; + r->toString(c, buffer, 256); + fprintf(stderr, "%p freeze %s to %d\n", v, buffer, r->freezeCount + 1); + } + + ++r->freezeCount; +} + +void thawResource(Context* c, Resource* r, Value* v) +{ + if (not r->reserved) { + if (DebugResources) { + char buffer[256]; + r->toString(c, buffer, 256); + fprintf(stderr, "%p thaw %s to %d\n", v, buffer, r->freezeCount - 1); + } + + assertT(c, r->freezeCount); + + --r->freezeCount; + } +} + +Resource::Resource(bool reserved) + : value(0), + site(0), + previousAcquired(0), + nextAcquired(0), + freezeCount(0), + referenceCount(0), + reserved(reserved) +{ +} + +RegisterResource::RegisterResource(bool reserved) : Resource(reserved) +{ +} + +void RegisterResource::freeze(Context* c, Value* v) +{ + if (not reserved) { + freezeResource(c, this, v); + + if (freezeCount == 1 + and c->regFile->generalRegisters.contains(index(c))) { + decrementAvailableGeneralRegisterCount(c); + } + } +} + +void RegisterResource::thaw(Context* c, Value* v) +{ + if (not reserved) { + thawResource(c, this, v); + + if (freezeCount == 0 + and c->regFile->generalRegisters.contains(index(c))) { + incrementAvailableGeneralRegisterCount(c); + } + } +} + +unsigned RegisterResource::toString(Context* c, + char* buffer, + unsigned bufferSize) +{ + return vm::snprintf(buffer, bufferSize, "register %d", index(c)); +} + +Register RegisterResource::index(Context* c) +{ + return Register(this - c->registerResources); +} + +void RegisterResource::increment(Context* c) +{ + if (not this->reserved) { + if (DebugResources) { + char buffer[256]; + this->toString(c, buffer, 256); + fprintf(stderr, "increment %s to %d\n", buffer, this->referenceCount + 1); + } + + ++this->referenceCount; + + if (this->referenceCount == 1 + and c->regFile->generalRegisters.contains(this->index(c))) { + decrementAvailableGeneralRegisterCount(c); + } + } +} + +void RegisterResource::decrement(Context* c) +{ + if (not this->reserved) { + if (DebugResources) { + char buffer[256]; + this->toString(c, buffer, 256); + fprintf(stderr, "decrement %s to %d\n", buffer, this->referenceCount - 1); + } + + assertT(c, this->referenceCount > 0); + + --this->referenceCount; + + if (this->referenceCount == 0 + and c->regFile->generalRegisters.contains(this->index(c))) { + incrementAvailableGeneralRegisterCount(c); + } + } +} + +void FrameResource::freeze(Context* c, Value* v) +{ + freezeResource(c, this, v); +} + +void FrameResource::thaw(Context* c, Value* v) +{ + thawResource(c, this, v); +} + +unsigned FrameResource::toString(Context* c, char* buffer, unsigned bufferSize) +{ + return vm::snprintf(buffer, bufferSize, "frame %d", index(c)); +} + +unsigned FrameResource::index(Context* c) +{ + return this - c->frameResources; +} + +void acquire(Context* c, Resource* resource, Value* value, Site* site) +{ + assertT(c, value); + assertT(c, site); + + if (not resource->reserved) { + if (DebugResources) { + char buffer[256]; + resource->toString(c, buffer, 256); + fprintf(stderr, "%p acquire %s\n", value, buffer); + } + + if (resource->value) { + assertT(c, resource->value->findSite(resource->site)); + assertT(c, not value->findSite(resource->site)); + + steal(c, resource, value); + } + + if (c->acquiredResources) { + c->acquiredResources->previousAcquired = resource; + resource->nextAcquired = c->acquiredResources; + } + c->acquiredResources = resource; + + resource->value = value; + resource->site = site; + } +} + +void release(Context* c, + Resource* resource, + Value* value UNUSED, + Site* site UNUSED) +{ + if (not resource->reserved) { + if (DebugResources) { + char buffer[256]; + resource->toString(c, buffer, 256); + fprintf(stderr, "%p release %s\n", resource->value, buffer); + } + + assertT(c, resource->value); + assertT(c, resource->site); + + assertT(c, resource->value->isBuddyOf(value)); + assertT(c, site == resource->site); + + Resource* next = resource->nextAcquired; + if (next) { + next->previousAcquired = resource->previousAcquired; + resource->nextAcquired = 0; + } + + Resource* previous = resource->previousAcquired; + if (previous) { + previous->nextAcquired = next; + resource->previousAcquired = 0; + } else { + assertT(c, c->acquiredResources == resource); + c->acquiredResources = next; + } + + resource->value = 0; + resource->site = 0; + } +} + +} // namespace compiler +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/compiler/resource.h b/sgx-jvm/avian/src/codegen/compiler/resource.h new file mode 100644 index 0000000000..e74543e376 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/compiler/resource.h @@ -0,0 +1,77 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_COMPILER_RESOURCE_H +#define AVIAN_CODEGEN_COMPILER_RESOURCE_H + +namespace avian { +namespace codegen { +namespace compiler { + +class Context; +class Value; +class Site; + +class Resource { + public: + Resource(bool reserved = false); + + virtual void freeze(Context*, Value*) = 0; + + virtual void thaw(Context*, Value*) = 0; + + virtual unsigned toString(Context*, char*, unsigned) = 0; + + Value* value; + Site* site; + Resource* previousAcquired; + Resource* nextAcquired; + uint8_t freezeCount; + uint8_t referenceCount; + bool reserved; +}; + +class RegisterResource : public Resource { + public: + RegisterResource(bool reserved); + + virtual void freeze(Context*, Value*); + + virtual void thaw(Context*, Value*); + + virtual unsigned toString(Context* c, char* buffer, unsigned bufferSize); + + virtual Register index(Context*); + + void increment(Context*); + + void decrement(Context*); +}; + +class FrameResource : public Resource { + public: + virtual void freeze(Context*, Value*); + + virtual void thaw(Context*, Value*); + + virtual unsigned toString(Context* c, char* buffer, unsigned bufferSize); + + virtual unsigned index(Context*); +}; + +void acquire(Context* c, Resource* resource, Value* value, Site* site); + +void release(Context* c, Resource* resource, Value* value, Site* site); + +} // namespace compiler +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_COMPILER_RESOURCE_H diff --git a/sgx-jvm/avian/src/codegen/compiler/site.cpp b/sgx-jvm/avian/src/codegen/compiler/site.cpp new file mode 100644 index 0000000000..d3c59065b4 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/compiler/site.cpp @@ -0,0 +1,706 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/target.h" + +#include "codegen/compiler/context.h" +#include "codegen/compiler/value.h" +#include "codegen/compiler/site.h" +#include "codegen/compiler/resource.h" +#include "codegen/compiler/frame.h" +#include "codegen/compiler/promise.h" + +namespace avian { +namespace codegen { +namespace compiler { + +int intersectFrameIndexes(int a, int b) +{ + if (a == NoFrameIndex or b == NoFrameIndex) + return NoFrameIndex; + if (a == AnyFrameIndex) + return b; + if (b == AnyFrameIndex) + return a; + if (a == b) + return a; + return NoFrameIndex; +} + +SiteMask SiteMask::intersectionWith(const SiteMask& b) +{ + return SiteMask(typeMask & b.typeMask, + registerMask & b.registerMask, + intersectFrameIndexes(frameIndex, b.frameIndex)); +} + +SiteIterator::SiteIterator(Context* c, + Value* v, + bool includeBuddies, + bool includeNextWord) + : c(c), + originalValue(v), + currentValue(v), + includeBuddies(includeBuddies), + includeNextWord(includeNextWord), + pass(0), + next_(findNext(&(v->sites))), + previous(0) +{ +} + +Site** SiteIterator::findNext(Site** p) +{ + while (true) { + if (*p) { + if (pass == 0 or (*p)->registerSize(c) > c->targetInfo.pointerSize) { + return p; + } else { + p = &((*p)->next); + } + } else { + if (includeBuddies) { + Value* v = currentValue->buddy; + if (v != originalValue) { + currentValue = v; + p = &(v->sites); + continue; + } + } + + if (includeNextWord and pass == 0) { + Value* v = originalValue->nextWord; + if (v != originalValue) { + pass = 1; + originalValue = v; + currentValue = v; + p = &(v->sites); + continue; + } + } + + return 0; + } + } +} + +bool SiteIterator::hasMore() +{ + if (previous) { + next_ = findNext(&((*previous)->next)); + previous = 0; + } + return next_ != 0; +} + +Site* SiteIterator::next() +{ + previous = next_; + return *previous; +} + +void SiteIterator::remove(Context* c) +{ + (*previous)->release(c, originalValue); + *previous = (*previous)->next; + next_ = findNext(previous); + previous = 0; +} + +unsigned Site::registerSize(Context* c) +{ + return c->targetInfo.pointerSize; +} + +Site* constantSite(Context* c, Promise* value) +{ + return new (c->zone) ConstantSite(value); +} + +Site* constantSite(Context* c, int64_t value) +{ + return constantSite(c, resolvedPromise(c, value)); +} + +class AddressSite : public Site { + public: + AddressSite(Promise* address) : address(address) + { + } + + virtual unsigned toString(Context*, char* buffer, unsigned bufferSize) + { + if (address->resolved()) { + return vm::snprintf( + buffer, bufferSize, "address %" LLD, address->value()); + } else { + return vm::snprintf(buffer, bufferSize, "address unresolved"); + } + } + + virtual unsigned copyCost(Context*, Site* s) + { + return (s == this ? 0 : AddressCopyCost); + } + + virtual bool match(Context*, const SiteMask& mask) + { + return mask.typeMask & lir::Operand::AddressMask; + } + + virtual bool loneMatch(Context*, const SiteMask&) + { + return false; + } + + virtual bool matchNextWord(Context* c, Site*, unsigned) + { + abort(c); + } + + virtual lir::Operand::Type type(Context*) + { + return lir::Operand::Type::Address; + } + + virtual void asAssemblerOperand(Context* c UNUSED, + Site* high UNUSED, + lir::Operand* result) + { + assertT(c, high == this); + + new (result) lir::Address(address); + } + + virtual Site* copy(Context* c) + { + return addressSite(c, address); + } + + virtual Site* copyLow(Context* c) + { + abort(c); + } + + virtual Site* copyHigh(Context* c) + { + abort(c); + } + + virtual Site* makeNextWord(Context* c, unsigned) + { + abort(c); + } + + virtual SiteMask mask(Context*) + { + return SiteMask(lir::Operand::AddressMask, 0, NoFrameIndex); + } + + virtual SiteMask nextWordMask(Context* c, unsigned) + { + abort(c); + } + + Promise* address; +}; + +Site* addressSite(Context* c, Promise* address) +{ + return new (c->zone) AddressSite(address); +} + +RegisterSite::RegisterSite(RegisterMask mask, Register number) + : mask_(mask), number(number) +{ +} + +unsigned RegisterSite::toString(Context*, char* buffer, unsigned bufferSize) +{ + if (number != NoRegister) { + return vm::snprintf(buffer, bufferSize, "%p register %d", this, number); + } else { + return vm::snprintf( + buffer, bufferSize, "%p register unacquired (mask %d)", this, mask_); + } +} + +unsigned RegisterSite::copyCost(Context* c, Site* s) +{ + assertT(c, number != NoRegister); + + if (s and (this == s + or (s->type(c) == lir::Operand::Type::RegisterPair + and (static_cast(s)->mask_.contains(number))))) { + return 0; + } else { + return RegisterCopyCost; + } +} + +bool RegisterSite::match(Context* c UNUSED, const SiteMask& mask) +{ + assertT(c, number != NoRegister); + + if ((mask.typeMask & lir::Operand::RegisterPairMask)) { + return mask.registerMask.contains(number); + } else { + return false; + } +} + +bool RegisterSite::loneMatch(Context* c UNUSED, const SiteMask& mask) +{ + assertT(c, number != NoRegister); + + if ((mask.typeMask & lir::Operand::RegisterPairMask)) { + return mask.registerMask.containsExactly(number); + } else { + return false; + } +} + +bool RegisterSite::matchNextWord(Context* c, Site* s, unsigned) +{ + assertT(c, number != NoRegister); + + if (s->type(c) != lir::Operand::Type::RegisterPair) { + return false; + } + + RegisterSite* rs = static_cast(s); + unsigned size = rs->registerSize(c); + if (size > c->targetInfo.pointerSize) { + assertT(c, number != NoRegister); + return number == rs->number; + } else { + RegisterMask mask = c->regFile->generalRegisters; + return mask.contains(number) and mask.contains(rs->number); + } +} + +void RegisterSite::acquire(Context* c, Value* v) +{ + Target target; + if (number != NoRegister) { + target = Target(number, 0); + } else { + target = pickRegisterTarget(c, v, mask_); + expect(c, target.cost < Target::Impossible); + } + + RegisterResource* resource = c->registerResources + target.index; + compiler::acquire(c, resource, v, this); + + number = Register(target.index); +} + +void RegisterSite::release(Context* c, Value* v) +{ + assertT(c, number != NoRegister); + + compiler::release(c, c->registerResources + number.index(), v, this); +} + +void RegisterSite::freeze(Context* c, Value* v) +{ + assertT(c, number != NoRegister); + + c->registerResources[number.index()].freeze(c, v); +} + +void RegisterSite::thaw(Context* c, Value* v) +{ + assertT(c, number != NoRegister); + + c->registerResources[number.index()].thaw(c, v); +} + +bool RegisterSite::frozen(Context* c UNUSED) +{ + assertT(c, number != NoRegister); + + return c->registerResources[number.index()].freezeCount != 0; +} + +lir::Operand::Type RegisterSite::type(Context*) +{ + return lir::Operand::Type::RegisterPair; +} + +void RegisterSite::asAssemblerOperand(Context* c UNUSED, + Site* high, + lir::Operand* result) +{ + assertT(c, number != NoRegister); + + Register highNumber; + if (high != this) { + highNumber = static_cast(high)->number; + assertT(c, highNumber != NoRegister); + } else { + highNumber = NoRegister; + } + + new (result) lir::RegisterPair(number, highNumber); +} + +Site* RegisterSite::copy(Context* c) +{ + RegisterMask mask; + + if (number != NoRegister) { + mask = RegisterMask(number); + } else { + mask = mask_; + } + + return freeRegisterSite(c, mask); +} + +Site* RegisterSite::copyLow(Context* c) +{ + abort(c); +} + +Site* RegisterSite::copyHigh(Context* c) +{ + abort(c); +} + +Site* RegisterSite::makeNextWord(Context* c, unsigned) +{ + assertT(c, number != NoRegister); + assertT(c, c->regFile->generalRegisters.contains(number)); + + return freeRegisterSite(c, c->regFile->generalRegisters); +} + +SiteMask RegisterSite::mask(Context* c UNUSED) +{ + return SiteMask(lir::Operand::RegisterPairMask, mask_, NoFrameIndex); +} + +SiteMask RegisterSite::nextWordMask(Context* c, unsigned) +{ + assertT(c, number != NoRegister); + + if (registerSize(c) > c->targetInfo.pointerSize) { + return SiteMask(lir::Operand::RegisterPairMask, number, NoFrameIndex); + } else { + return SiteMask(lir::Operand::RegisterPairMask, + c->regFile->generalRegisters, + NoFrameIndex); + } +} + +unsigned RegisterSite::registerSize(Context* c) +{ + assertT(c, number != NoRegister); + + if (c->regFile->floatRegisters.contains(number)) { + return c->arch->floatRegisterSize(); + } else { + return c->targetInfo.pointerSize; + } +} + +RegisterMask RegisterSite::registerMask(Context* c UNUSED) +{ + assertT(c, number != NoRegister); + + return RegisterMask(number); +} + +Site* registerSite(Context* c, Register number) +{ + assertT(c, number != NoRegister); + assertT(c, + (c->regFile->generalRegisters + | c->regFile->floatRegisters).contains(number)); + + return new (c->zone) RegisterSite(RegisterMask(number), number); +} + +Site* freeRegisterSite(Context* c, RegisterMask mask) +{ + return new (c->zone) RegisterSite(mask, NoRegister); +} + +MemorySite::MemorySite(Register base, int offset, Register index, unsigned scale) + : acquired(false), base(base), offset(offset), index(index), scale(scale) +{ +} + +unsigned MemorySite::toString(Context*, char* buffer, unsigned bufferSize) +{ + if (acquired) { + return vm::snprintf( + buffer, bufferSize, "memory %d 0x%x %d %d", base, offset, index, scale); + } else { + return vm::snprintf(buffer, bufferSize, "memory unacquired"); + } +} + +unsigned MemorySite::copyCost(Context* c, Site* s) +{ + assertT(c, acquired); + + if (s and (this == s or (s->type(c) == lir::Operand::Type::Memory + and static_cast(s)->base == base + and static_cast(s)->offset == offset + and static_cast(s)->index == index + and static_cast(s)->scale == scale))) { + return 0; + } else { + return MemoryCopyCost; + } +} + +bool MemorySite::conflicts(const SiteMask& mask) +{ + return (mask.typeMask & lir::Operand::RegisterPairMask) != 0 + and (!mask.registerMask.contains(base) + or (index != NoRegister + and !mask.registerMask.contains(index))); +} + +bool MemorySite::match(Context* c, const SiteMask& mask) +{ + assertT(c, acquired); + + if (mask.typeMask & lir::Operand::MemoryMask) { + if (mask.frameIndex >= 0) { + if (base == c->arch->stack()) { + assertT(c, index == NoRegister); + return static_cast(frameIndexToOffset(c, mask.frameIndex)) + == offset; + } else { + return false; + } + } else { + return true; + } + } else { + return false; + } +} + +bool MemorySite::loneMatch(Context* c, const SiteMask& mask) +{ + assertT(c, acquired); + + if (mask.typeMask & lir::Operand::MemoryMask) { + if (base == c->arch->stack()) { + assertT(c, index == NoRegister); + + if (mask.frameIndex == AnyFrameIndex) { + return false; + } else { + return true; + } + } + } + return false; +} + +bool MemorySite::matchNextWord(Context* c, Site* s, unsigned index) +{ + if (s->type(c) == lir::Operand::Type::Memory) { + MemorySite* ms = static_cast(s); + return ms->base == this->base + and ((index == 1 + and ms->offset + == static_cast(this->offset + + c->targetInfo.pointerSize)) + or (index == 0 + and this->offset + == static_cast(ms->offset + + c->targetInfo.pointerSize))) + and ms->index == this->index and ms->scale == this->scale; + } else { + return false; + } +} + +void MemorySite::acquire(Context* c, Value* v) +{ + c->registerResources[base.index()].increment(c); + if (index != NoRegister) { + c->registerResources[index.index()].increment(c); + } + + if (base == c->arch->stack()) { + assertT(c, index == NoRegister); + assertT(c, not c->frameResources[offsetToFrameIndex(c, offset)].reserved); + + compiler::acquire( + c, c->frameResources + offsetToFrameIndex(c, offset), v, this); + } + + acquired = true; +} + +void MemorySite::release(Context* c, Value* v) +{ + if (base == c->arch->stack()) { + assertT(c, index == NoRegister); + assertT(c, not c->frameResources[offsetToFrameIndex(c, offset)].reserved); + + compiler::release( + c, c->frameResources + offsetToFrameIndex(c, offset), v, this); + } + + c->registerResources[base.index()].decrement(c); + if (index != NoRegister) { + c->registerResources[index.index()].decrement(c); + } + + acquired = false; +} + +void MemorySite::freeze(Context* c, Value* v) +{ + if (base == c->arch->stack()) { + c->frameResources[offsetToFrameIndex(c, offset)].freeze(c, v); + } else { + c->registerResources[base.index()].increment(c); + if (index != NoRegister) { + c->registerResources[index.index()].increment(c); + } + } +} + +void MemorySite::thaw(Context* c, Value* v) +{ + if (base == c->arch->stack()) { + c->frameResources[offsetToFrameIndex(c, offset)].thaw(c, v); + } else { + c->registerResources[base.index()].decrement(c); + if (index != NoRegister) { + c->registerResources[index.index()].decrement(c); + } + } +} + +bool MemorySite::frozen(Context* c) +{ + return base == c->arch->stack() + and c->frameResources[offsetToFrameIndex(c, offset)].freezeCount != 0; +} + +lir::Operand::Type MemorySite::type(Context*) +{ + return lir::Operand::Type::Memory; +} + +void MemorySite::asAssemblerOperand(Context* c UNUSED, + Site* high UNUSED, + lir::Operand* result) +{ + // todo: endianness? + assertT(c, + high == this + or (static_cast(high)->base == base + and static_cast(high)->offset + == static_cast(offset + c->targetInfo.pointerSize) + and static_cast(high)->index == index + and static_cast(high)->scale == scale)); + + assertT(c, acquired); + + new (result) lir::Memory(base, offset, index, scale); +} + +Site* MemorySite::copy(Context* c) +{ + return memorySite(c, base, offset, index, scale); +} + +Site* MemorySite::copyHalf(Context* c, bool add) +{ + if (add) { + return memorySite( + c, base, offset + c->targetInfo.pointerSize, index, scale); + } else { + return copy(c); + } +} + +Site* MemorySite::copyLow(Context* c) +{ + return copyHalf(c, c->arch->bigEndian()); +} + +Site* MemorySite::copyHigh(Context* c) +{ + return copyHalf(c, not c->arch->bigEndian()); +} + +Site* MemorySite::makeNextWord(Context* c, unsigned index) +{ + return memorySite(c, + base, + offset + ((index == 1) xor c->arch->bigEndian() + ? c->targetInfo.pointerSize + : -c->targetInfo.pointerSize), + this->index, + scale); +} + +SiteMask MemorySite::mask(Context* c) +{ + return SiteMask(lir::Operand::MemoryMask, + 0, + (base == c->arch->stack()) + ? static_cast(offsetToFrameIndex(c, offset)) + : NoFrameIndex); +} + +SiteMask MemorySite::nextWordMask(Context* c, unsigned index) +{ + int frameIndex; + if (base == c->arch->stack()) { + assertT(c, this->index == NoRegister); + frameIndex = static_cast(offsetToFrameIndex(c, offset)) + + ((index == 1) xor c->arch->bigEndian() ? 1 : -1); + } else { + frameIndex = NoFrameIndex; + } + return SiteMask(lir::Operand::MemoryMask, 0, frameIndex); +} + +bool MemorySite::isVolatile(Context* c) +{ + return base != c->arch->stack(); +} + +MemorySite* memorySite(Context* c, + Register base, + int offset, + Register index, + unsigned scale) +{ + return new (c->zone) MemorySite(base, offset, index, scale); +} + +MemorySite* frameSite(Context* c, int frameIndex) +{ + assertT(c, frameIndex >= 0); + return memorySite(c, + c->arch->stack(), + frameIndexToOffset(c, frameIndex), + NoRegister, + 0); +} + +} // namespace compiler +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/compiler/site.h b/sgx-jvm/avian/src/codegen/compiler/site.h new file mode 100644 index 0000000000..9629455c88 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/compiler/site.h @@ -0,0 +1,371 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_COMPILER_SITE_H +#define AVIAN_CODEGEN_COMPILER_SITE_H + +#include + +#include "codegen/compiler/value.h" +#include "codegen/compiler/context.h" + +namespace avian { +namespace codegen { +namespace compiler { + +class Context; + +const unsigned RegisterCopyCost = 1; +const unsigned AddressCopyCost = 2; +const unsigned ConstantCopyCost = 3; +const unsigned MemoryCopyCost = 4; +const unsigned CopyPenalty = 10; + +class SiteMask { + public: + SiteMask() : typeMask(~0), registerMask(~0), frameIndex(AnyFrameIndex) + { + } + + SiteMask(uint8_t typeMask, RegisterMask registerMask, int frameIndex) + : typeMask(typeMask), registerMask(registerMask), frameIndex(frameIndex) + { + } + + SiteMask intersectionWith(const SiteMask& b); + + static SiteMask fixedRegisterMask(Register number) + { + return SiteMask(lir::Operand::RegisterPairMask, 1 << number.index(), NoFrameIndex); + } + + static SiteMask lowPart(const OperandMask& mask) + { + return SiteMask(mask.typeMask, mask.lowRegisterMask, AnyFrameIndex); + } + + static SiteMask highPart(const OperandMask& mask) + { + return SiteMask(mask.typeMask, mask.highRegisterMask, AnyFrameIndex); + } + + uint8_t typeMask; + RegisterMask registerMask; + int frameIndex; +}; + +class Site { + public: + Site() : next(0) + { + } + + virtual Site* readTarget(Context*, Read*) + { + return this; + } + + virtual unsigned toString(Context*, char*, unsigned) = 0; + + virtual unsigned copyCost(Context*, Site*) = 0; + + virtual bool match(Context*, const SiteMask&) = 0; + + virtual bool loneMatch(Context*, const SiteMask&) = 0; + + virtual bool matchNextWord(Context*, Site*, unsigned) = 0; + + virtual void acquire(Context*, Value*) + { + } + + virtual void release(Context*, Value*) + { + } + + virtual void freeze(Context*, Value*) + { + } + + virtual void thaw(Context*, Value*) + { + } + + virtual bool frozen(Context*) + { + return false; + } + + virtual lir::Operand::Type type(Context*) = 0; + + virtual void asAssemblerOperand(Context*, Site*, lir::Operand*) = 0; + + virtual Site* copy(Context*) = 0; + + virtual Site* copyLow(Context*) = 0; + + virtual Site* copyHigh(Context*) = 0; + + virtual Site* makeNextWord(Context*, unsigned) = 0; + + virtual SiteMask mask(Context*) = 0; + + virtual SiteMask nextWordMask(Context*, unsigned) = 0; + + virtual unsigned registerSize(Context*); + + virtual RegisterMask registerMask(Context*) + { + return RegisterMask(0); + } + + virtual bool isVolatile(Context*) + { + return false; + } + + Site* next; +}; + +class SiteIterator { + public: + SiteIterator(Context* c, + Value* v, + bool includeBuddies = true, + bool includeNextWord = true); + + Site** findNext(Site** p); + bool hasMore(); + Site* next(); + void remove(Context* c); + + Context* c; + Value* originalValue; + Value* currentValue; + bool includeBuddies; + bool includeNextWord; + uint8_t pass; + Site** next_; + Site** previous; +}; + +Site* constantSite(Context* c, Promise* value); +Site* constantSite(Context* c, int64_t value); + +Promise* combinedPromise(Context* c, Promise* low, Promise* high); +Promise* shiftMaskPromise(Context* c, + Promise* base, + unsigned shift, + int64_t mask); + +class ConstantSite : public Site { + public: + ConstantSite(Promise* value) : value(value) + { + } + + virtual unsigned toString(Context*, char* buffer, unsigned bufferSize) + { + if (value->resolved()) { + return vm::snprintf(buffer, bufferSize, "constant %" LLD, value->value()); + } else { + return vm::snprintf(buffer, bufferSize, "constant unresolved"); + } + } + + virtual unsigned copyCost(Context*, Site* s) + { + return (s == this ? 0 : ConstantCopyCost); + } + + virtual bool match(Context*, const SiteMask& mask) + { + return mask.typeMask & lir::Operand::ConstantMask; + } + + virtual bool loneMatch(Context*, const SiteMask&) + { + return true; + } + + virtual bool matchNextWord(Context* c, Site* s, unsigned) + { + return s->type(c) == lir::Operand::Type::Constant; + } + + virtual lir::Operand::Type type(Context*) + { + return lir::Operand::Type::Constant; + } + + virtual void asAssemblerOperand(Context* c, Site* high, lir::Operand* result) + { + Promise* v = value; + if (high != this) { + v = combinedPromise(c, value, static_cast(high)->value); + } + new (result) lir::Constant(v); + } + + virtual Site* copy(Context* c) + { + return constantSite(c, value); + } + + virtual Site* copyLow(Context* c) + { + return constantSite(c, shiftMaskPromise(c, value, 0, 0xFFFFFFFF)); + } + + virtual Site* copyHigh(Context* c) + { + return constantSite(c, shiftMaskPromise(c, value, 32, 0xFFFFFFFF)); + } + + virtual Site* makeNextWord(Context* c, unsigned) + { + abort(c); + } + + virtual SiteMask mask(Context*) + { + return SiteMask(lir::Operand::ConstantMask, 0, NoFrameIndex); + } + + virtual SiteMask nextWordMask(Context*, unsigned) + { + return SiteMask(lir::Operand::ConstantMask, 0, NoFrameIndex); + } + + Promise* value; +}; + +Site* addressSite(Context* c, Promise* address); + +class RegisterSite : public Site { + public: + RegisterSite(RegisterMask mask, Register number); + + virtual unsigned toString(Context*, char* buffer, unsigned bufferSize); + + virtual unsigned copyCost(Context* c, Site* s); + + virtual bool match(Context* c UNUSED, const SiteMask& mask); + + virtual bool loneMatch(Context* c UNUSED, const SiteMask& mask); + + virtual bool matchNextWord(Context* c, Site* s, unsigned); + + virtual void acquire(Context* c, Value* v); + + virtual void release(Context* c, Value* v); + + virtual void freeze(Context* c, Value* v); + + virtual void thaw(Context* c, Value* v); + + virtual bool frozen(Context* c UNUSED); + + virtual lir::Operand::Type type(Context*); + + virtual void asAssemblerOperand(Context* c UNUSED, + Site* high, + lir::Operand* result); + + virtual Site* copy(Context* c); + + virtual Site* copyLow(Context* c); + + virtual Site* copyHigh(Context* c); + + virtual Site* makeNextWord(Context* c, unsigned); + + virtual SiteMask mask(Context* c UNUSED); + + virtual SiteMask nextWordMask(Context* c, unsigned); + + virtual unsigned registerSize(Context* c); + + virtual RegisterMask registerMask(Context* c UNUSED); + + RegisterMask mask_; + Register number; +}; + +Site* registerSite(Context* c, Register number); +Site* freeRegisterSite(Context* c, RegisterMask mask); + +class MemorySite : public Site { + public: + MemorySite(Register base, int offset, Register index, unsigned scale); + + virtual unsigned toString(Context*, char* buffer, unsigned bufferSize); + + virtual unsigned copyCost(Context* c, Site* s); + + bool conflicts(const SiteMask& mask); + + virtual bool match(Context* c, const SiteMask& mask); + + virtual bool loneMatch(Context* c, const SiteMask& mask); + + virtual bool matchNextWord(Context* c, Site* s, unsigned index); + + virtual void acquire(Context* c, Value* v); + + virtual void release(Context* c, Value* v); + + virtual void freeze(Context* c, Value* v); + + virtual void thaw(Context* c, Value* v); + + virtual bool frozen(Context* c); + + virtual lir::Operand::Type type(Context*); + + virtual void asAssemblerOperand(Context* c UNUSED, + Site* high UNUSED, + lir::Operand* result); + + virtual Site* copy(Context* c); + + Site* copyHalf(Context* c, bool add); + + virtual Site* copyLow(Context* c); + + virtual Site* copyHigh(Context* c); + + virtual Site* makeNextWord(Context* c, unsigned index); + + virtual SiteMask mask(Context* c); + + virtual SiteMask nextWordMask(Context* c, unsigned index); + + virtual bool isVolatile(Context* c); + + bool acquired; + Register base; + int offset; + Register index; + unsigned scale; +}; + +MemorySite* memorySite(Context* c, + Register base, + int offset = 0, + Register index = NoRegister, + unsigned scale = 1); +MemorySite* frameSite(Context* c, int frameIndex); + +} // namespace compiler +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_COMPILER_SITE_H diff --git a/sgx-jvm/avian/src/codegen/compiler/value.cpp b/sgx-jvm/avian/src/codegen/compiler/value.cpp new file mode 100644 index 0000000000..b4c44e8837 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/compiler/value.cpp @@ -0,0 +1,187 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/target.h" + +#include "codegen/compiler/regalloc.h" +#include "codegen/compiler/site.h" + +namespace avian { +namespace codegen { +namespace compiler { + +Value::Value(Site* site, Site* target, ir::Type type) + : ir::Value(type), + reads(0), + lastRead(0), + sites(site), + source(0), + target(target), + buddy(this), + nextWord(this), + home(NoFrameIndex), + wordIndex(0) +{ +} + +bool Value::findSite(Site* site) +{ + for (Site* s = this->sites; s; s = s->next) { + if (s == site) + return true; + } + return false; +} + +bool Value::isBuddyOf(Value* b) +{ + Value* a = this; + if (a == b) + return true; + for (Value* p = a->buddy; p != a; p = p->buddy) { + if (p == b) + return true; + } + return false; +} + +void Value::addSite(Context* c, Site* s) +{ + if (not this->findSite(s)) { + if (DebugSites) { + char buffer[256]; + s->toString(c, buffer, 256); + fprintf(stderr, "add site %s to %p\n", buffer, this); + } + s->acquire(c, this); + s->next = this->sites; + this->sites = s; + } +} + +void Value::grow(Context* c) +{ + assertT(c, this->nextWord == this); + + Value* next = value(c, this->type); + this->nextWord = next; + next->nextWord = this; + next->wordIndex = 1; +} + +void Value::maybeSplit(Context* c) +{ + if (this->nextWord == this) { + this->split(c); + } +} + +void Value::split(Context* c) +{ + this->grow(c); + for (SiteIterator it(c, this); it.hasMore();) { + Site* s = it.next(); + this->removeSite(c, s); + + this->addSite(c, s->copyLow(c)); + this->nextWord->addSite(c, s->copyHigh(c)); + } +} + +void Value::removeSite(Context* c, Site* s) +{ + for (SiteIterator it(c, this); it.hasMore();) { + if (s == it.next()) { + if (DebugSites) { + char buffer[256]; + s->toString(c, buffer, 256); + fprintf(stderr, "remove site %s from %p\n", buffer, this); + } + it.remove(c); + break; + } + } + if (DebugSites) { + fprintf(stderr, "%p has more: %d\n", this, this->hasSite(c)); + } + assertT(c, not this->findSite(s)); +} + +bool Value::hasSite(Context* c) +{ + SiteIterator it(c, this); + return it.hasMore(); +} + +bool Value::uniqueSite(Context* c, Site* s) +{ + SiteIterator it(c, this); + Site* p UNUSED = it.next(); + if (it.hasMore()) { + // the site is not this word's only site, but if the site is + // shared with the next word, it may be that word's only site + if (this->nextWord != this + and s->registerSize(c) > c->targetInfo.pointerSize) { + SiteIterator nit(c, this->nextWord); + Site* p = nit.next(); + if (nit.hasMore()) { + return false; + } else { + return p == s; + } + } else { + return false; + } + } else { + assertT(c, p == s); + return true; + } +} + +void Value::clearSites(Context* c) +{ + if (DebugSites) { + fprintf(stderr, "clear sites for %p\n", this); + } + for (SiteIterator it(c, this); it.hasMore();) { + it.next(); + it.remove(c); + } +} + +#ifndef NDEBUG +bool Value::hasBuddy(Context* c, Value* b) +{ + Value* a = this; + if (a == b) { + return true; + } + + int i = 0; + for (Value* p = a->buddy; p != a; p = p->buddy) { + if (p == b) { + return true; + } + if (++i > 1000) { + abort(c); + } + } + return false; +} +#endif // not NDEBUG + +Value* value(Context* c, ir::Type type, Site* site, Site* target) +{ + return new (c->zone) Value(site, target, type); +} + +} // namespace regalloc +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/compiler/value.h b/sgx-jvm/avian/src/codegen/compiler/value.h new file mode 100644 index 0000000000..76f67987b9 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/compiler/value.h @@ -0,0 +1,84 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_COMPILER_VALUE_H +#define AVIAN_CODEGEN_COMPILER_VALUE_H + +#include +#include + +namespace avian { +namespace codegen { +namespace compiler { + +class Read; +class Site; + +const int AnyFrameIndex = -2; +const int NoFrameIndex = -1; + +const bool DebugSites = false; + +class Value : public ir::Value { + public: + Read* reads; + Read* lastRead; + Site* sites; + Site* source; + Site* target; + Value* buddy; + Value* nextWord; + int16_t home; + uint8_t wordIndex; + + Value(Site* site, Site* target, ir::Type type); + + bool findSite(Site* site); + + bool isBuddyOf(Value* b); + + void addSite(Context* c, Site* s); + + void grow(Context* c); + + void maybeSplit(Context* c); + + void split(Context* c); + + void removeSite(Context* c, Site* s); + + bool hasSite(Context* c); + + bool uniqueSite(Context* c, Site* s); + + void clearSites(Context* c); + +#ifndef NDEBUG + bool hasBuddy(Context* c, Value* b); +#endif // not NDEBUG +}; + +inline bool isFloatValue(ir::Value* a) +{ + return static_cast(a)->type.flavor() == ir::Type::Float; +} + +inline bool isGeneralValue(ir::Value* a) +{ + return !isFloatValue(a); +} + +Value* value(Context* c, ir::Type type, Site* site = 0, Site* target = 0); + +} // namespace compiler +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_COMPILER_VALUE_H diff --git a/sgx-jvm/avian/src/codegen/runtime.cpp b/sgx-jvm/avian/src/codegen/runtime.cpp new file mode 100644 index 0000000000..b868464492 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/runtime.cpp @@ -0,0 +1,282 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include + +namespace avian { +namespace codegen { +namespace runtime { + +static bool isNaN(double v) +{ + return fpclassify(v) == FP_NAN; +} + +static bool isNaN(float v) +{ + return fpclassify(v) == FP_NAN; +} + +int64_t compareDoublesG(uint64_t bi, uint64_t ai) +{ + double a = vm::bitsToDouble(ai); + double b = vm::bitsToDouble(bi); + + if (isNaN(a) or isNaN(b)) { + return 1; + } else if (a < b) { + return -1; + } else if (a > b) { + return 1; + } else if (a == b) { + return 0; + } else { + return 1; + } +} + +int64_t compareDoublesL(uint64_t bi, uint64_t ai) +{ + double a = vm::bitsToDouble(ai); + double b = vm::bitsToDouble(bi); + + if (isNaN(a) or isNaN(b)) { + return -1; + } else if (a < b) { + return -1; + } else if (a > b) { + return 1; + } else if (a == b) { + return 0; + } else { + return -1; + } +} + +int64_t compareFloatsG(uint32_t bi, uint32_t ai) +{ + float a = vm::bitsToFloat(ai); + float b = vm::bitsToFloat(bi); + + if (isNaN(a) or isNaN(b)) { + return 1; + } + if (a < b) { + return -1; + } else if (a > b) { + return 1; + } else if (a == b) { + return 0; + } else { + return 1; + } +} + +int64_t compareFloatsL(uint32_t bi, uint32_t ai) +{ + float a = vm::bitsToFloat(ai); + float b = vm::bitsToFloat(bi); + + if (isNaN(a) or isNaN(b)) { + return -1; + } + if (a < b) { + return -1; + } else if (a > b) { + return 1; + } else if (a == b) { + return 0; + } else { + return -1; + } +} + +int64_t compareLongs(uint64_t b, uint64_t a) +{ + if (a < b) { + return -1; + } else if (a > b) { + return 1; + } else { + return 0; + } +} + +uint64_t addDouble(uint64_t b, uint64_t a) +{ + return vm::doubleToBits(vm::bitsToDouble(a) + vm::bitsToDouble(b)); +} + +uint64_t subtractDouble(uint64_t b, uint64_t a) +{ + return vm::doubleToBits(vm::bitsToDouble(a) - vm::bitsToDouble(b)); +} + +uint64_t multiplyDouble(uint64_t b, uint64_t a) +{ + return vm::doubleToBits(vm::bitsToDouble(a) * vm::bitsToDouble(b)); +} + +uint64_t divideDouble(uint64_t b, uint64_t a) +{ + return vm::doubleToBits(vm::bitsToDouble(a) / vm::bitsToDouble(b)); +} + +uint64_t moduloDouble(uint64_t b, uint64_t a) +{ + return vm::doubleToBits(fmod(vm::bitsToDouble(a), vm::bitsToDouble(b))); +} + +uint64_t negateDouble(uint64_t a) +{ + return vm::doubleToBits(-vm::bitsToDouble(a)); +} + +uint64_t squareRootDouble(uint64_t a) +{ + return vm::doubleToBits(sqrt(vm::bitsToDouble(a))); +} + +uint64_t doubleToFloat(int64_t a) +{ + return vm::floatToBits(static_cast(vm::bitsToDouble(a))); +} + +int64_t doubleToInt(int64_t a) +{ + double f = vm::bitsToDouble(a); + switch (fpclassify(f)) { + case FP_NAN: + return 0; + case FP_INFINITE: + return signbit(f) ? INT32_MIN : INT32_MAX; + default: + return f >= INT32_MAX + ? INT32_MAX + : (f <= INT32_MIN ? INT32_MIN : static_cast(f)); + } +} + +int64_t doubleToLong(int64_t a) +{ + double f = vm::bitsToDouble(a); + switch (fpclassify(f)) { + case FP_NAN: + return 0; + case FP_INFINITE: + return signbit(f) ? INT64_MIN : INT64_MAX; + default: + return f >= INT64_MAX + ? INT64_MAX + : (f <= INT64_MIN ? INT64_MIN : static_cast(f)); + } +} + +uint64_t addFloat(uint32_t b, uint32_t a) +{ + return vm::floatToBits(vm::bitsToFloat(a) + vm::bitsToFloat(b)); +} + +uint64_t subtractFloat(uint32_t b, uint32_t a) +{ + return vm::floatToBits(vm::bitsToFloat(a) - vm::bitsToFloat(b)); +} + +uint64_t multiplyFloat(uint32_t b, uint32_t a) +{ + return vm::floatToBits(vm::bitsToFloat(a) * vm::bitsToFloat(b)); +} + +uint64_t divideFloat(uint32_t b, uint32_t a) +{ + return vm::floatToBits(vm::bitsToFloat(a) / vm::bitsToFloat(b)); +} + +uint64_t moduloFloat(uint32_t b, uint32_t a) +{ + return vm::floatToBits(fmod(vm::bitsToFloat(a), vm::bitsToFloat(b))); +} + +uint64_t negateFloat(uint32_t a) +{ + return vm::floatToBits(-vm::bitsToFloat(a)); +} + +uint64_t absoluteFloat(uint32_t a) +{ + return vm::floatToBits(fabsf(vm::bitsToFloat(a))); +} + +int64_t absoluteLong(int64_t a) +{ + return a > 0 ? a : -a; +} + +int64_t absoluteInt(int32_t a) +{ + return a > 0 ? a : -a; +} + +uint64_t floatToDouble(int32_t a) +{ + return vm::doubleToBits(static_cast(vm::bitsToFloat(a))); +} + +int64_t floatToInt(int32_t a) +{ + float f = vm::bitsToFloat(a); + switch (fpclassify(f)) { + case FP_NAN: + return 0; + case FP_INFINITE: + return signbit(f) ? INT32_MIN : INT32_MAX; + default: + return f >= INT32_MAX + ? INT32_MAX + : (f <= INT32_MIN ? INT32_MIN : static_cast(f)); + } +} + +int64_t floatToLong(int32_t a) +{ + float f = vm::bitsToFloat(a); + switch (fpclassify(f)) { + case FP_NAN: + return 0; + case FP_INFINITE: + return signbit(f) ? INT64_MIN : INT64_MAX; + default: + return static_cast(f); + } +} + +uint64_t intToDouble(int32_t a) +{ + return vm::doubleToBits(static_cast(a)); +} + +uint64_t intToFloat(int32_t a) +{ + return vm::floatToBits(static_cast(a)); +} + +uint64_t longToDouble(int64_t a) +{ + return vm::doubleToBits(static_cast(a)); +} + +uint64_t longToFloat(int64_t a) +{ + return vm::floatToBits(static_cast(a)); +} + +} // namespace runtime +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/target/CMakeLists.txt b/sgx-jvm/avian/src/codegen/target/CMakeLists.txt new file mode 100644 index 0000000000..55edb6b112 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(arm) +add_subdirectory(x86) diff --git a/sgx-jvm/avian/src/codegen/target/arm/CMakeLists.txt b/sgx-jvm/avian/src/codegen/target/arm/CMakeLists.txt new file mode 100644 index 0000000000..23faf6694f --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/arm/CMakeLists.txt @@ -0,0 +1,9 @@ +add_library(avian_codegen_arm + assembler.cpp + block.cpp + context.cpp + fixup.cpp + multimethod.cpp + operations32.cpp + operations64.cpp +) diff --git a/sgx-jvm/avian/src/codegen/target/arm/assembler.cpp b/sgx-jvm/avian/src/codegen/target/arm/assembler.cpp new file mode 100644 index 0000000000..a72bd5edf5 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/arm/assembler.cpp @@ -0,0 +1,1178 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include + +#include +#include +#include + +#include "context.h" +#include "block.h" +#include "fixup.h" +#include "multimethod.h" +#include "encode.h" +#include "operations.h" +#include "registers.h" +#include "../multimethod.h" + +#include "avian/alloc-vector.h" +#include + +using namespace vm; +using namespace avian::codegen; +using namespace avian::util; + +namespace avian { +namespace codegen { +namespace arm { + +namespace isa { +// HARDWARE FLAGS +bool vfpSupported() +{ +// TODO: Use at runtime detection +#if (defined __ARM_PCS_VFP) || (defined ARCH_arm64) + // armhf + return true; +#else + // armel + // TODO: allow VFP use for -mfloat-abi=softfp armel builds. + // GCC -mfloat-abi=softfp flag allows use of VFP while remaining compatible + // with soft-float code. + return false; +#endif +} +} // namespace isa + +const RegisterFile MyRegisterFileWithoutFloats(GPR_MASK, 0); +const RegisterFile MyRegisterFileWithFloats(GPR_MASK, FPR_MASK); + +const unsigned FrameHeaderSize = TargetBytesPerWord / 4; + +const unsigned StackAlignmentInBytes = TargetBytesPerWord * 2; +const unsigned StackAlignmentInWords = StackAlignmentInBytes + / TargetBytesPerWord; + +void resolve(MyBlock*); + +unsigned padding(MyBlock*, unsigned); + +class ConstantPoolEntry; + +// BEGIN OPERATION COMPILERS + +using namespace isa; + +// END OPERATION COMPILERS + +unsigned argumentFootprint(unsigned footprint) +{ + return max(pad(footprint, StackAlignmentInWords), StackAlignmentInWords); +} + +void nextFrame(ArchitectureContext* con, + uint32_t* start, + unsigned size UNUSED, + unsigned footprint, + void* link, + bool, + int targetParameterFootprint UNUSED, + void** ip, + void** stack) +{ + assertT(con, *ip >= start); + assertT(con, *ip <= start + (size / 4)); + + uint32_t* instruction = static_cast(*ip); + + if ((*start >> 20) == (TargetBytesPerWord == 8 ? 0xf94 : 0xe59)) { + // skip stack overflow check + start += TargetBytesPerWord == 8 ? 4 : 3; + } + + if (instruction <= start) { + *ip = link; + return; + } + + unsigned offset = footprint + FrameHeaderSize; + + if (instruction <= start + 2) { + *ip = link; + *stack = static_cast(*stack) + offset; + return; + } + + if (*instruction == (TargetBytesPerWord == 8 ? 0xd61f03c0 : 0xe12fff1e)) { + // return + *ip = link; + return; + } + + if (TailCalls and targetParameterFootprint >= 0) { + if (argumentFootprint(targetParameterFootprint) > StackAlignmentInWords) { + offset += argumentFootprint(targetParameterFootprint) + - StackAlignmentInWords; + } + + // check for post-non-tail-call stack adjustment of the form "sub + // sp, sp, #offset": + if (TargetBytesPerWord == 8 and (*instruction & 0xff0003ff) == 0xd10003ff) { + unsigned value = (*instruction >> 10) & 0xfff; + unsigned shift = (*instruction >> 22) & 1; + switch (shift) { + case 0: + offset -= value / TargetBytesPerWord; + break; + case 1: + offset -= (value << 12) / TargetBytesPerWord; + break; + default: + abort(con); + } + } else if (TargetBytesPerWord == 4 and (*instruction >> 12) == 0xe24dd) { + unsigned value = *instruction & 0xff; + unsigned rotation = (*instruction >> 8) & 0xf; + switch (rotation) { + case 0: + offset -= value / TargetBytesPerWord; + break; + case 15: + offset -= value; + break; + default: + abort(con); + } + } + + // todo: check for and handle tail calls + } + + *ip = static_cast(*stack)[offset - 1]; + *stack = static_cast(*stack) + offset; +} + +class MyArchitecture : public Architecture { + public: + MyArchitecture(System* system) : con(system), referenceCount(0) + { + populateTables(&con); + } + + virtual unsigned floatRegisterSize() + { + return vfpSupported() ? 8 : 0; + } + + virtual const RegisterFile* registerFile() + { + return vfpSupported() ? &MyRegisterFileWithFloats + : &MyRegisterFileWithoutFloats; + } + + virtual Register scratch() + { + return Register(5); + } + + virtual Register stack() + { + return StackRegister; + } + + virtual Register thread() + { + return ThreadRegister; + } + + virtual Register returnLow() + { + return Register(0); + } + + virtual Register returnHigh() + { + return Register(1); + } + + virtual Register virtualCallTarget() + { + return Register(4); + } + + virtual Register virtualCallIndex() + { + return Register(3); + } + + virtual ir::TargetInfo targetInfo() + { + return ir::TargetInfo(TargetBytesPerWord); + } + + virtual bool bigEndian() + { + return false; + } + + virtual uintptr_t maximumImmediateJump() + { + return 0x1FFFFFF; + } + + virtual bool reserved(Register register_) + { + switch (register_.index()) { + case LinkRegister.index(): + case FrameRegister.index(): + case StackRegister.index(): + case ThreadRegister.index(): + case ProgramCounter.index(): + return true; + case 18: + // x18 is a reserved platform register on arm64 + return TargetBytesPerWord == 8; + + default: + return false; + } + } + + virtual unsigned frameFootprint(unsigned footprint) + { + return max(footprint, StackAlignmentInWords); + } + + virtual unsigned argumentFootprint(unsigned footprint) + { + return arm::argumentFootprint(footprint); + } + + virtual bool argumentAlignment() + { +#ifdef __APPLE__ + return false; +#else + return true; +#endif + } + + virtual bool argumentRegisterAlignment() + { +#ifdef __APPLE__ + return false; +#else + return true; +#endif + } + + virtual unsigned argumentRegisterCount() + { + return TargetBytesPerWord; + } + + virtual Register argumentRegister(unsigned index) + { + assertT(&con, index < argumentRegisterCount()); + + return Register(index); + } + + virtual bool hasLinkRegister() + { + return true; + } + + virtual unsigned stackAlignmentInWords() + { + return StackAlignmentInWords; + } + + virtual bool matchCall(void* returnAddress, void* target) + { + uint32_t* instruction = static_cast(returnAddress) - 1; + + return *instruction == static_cast( + bl(static_cast(target) + - reinterpret_cast(instruction))); + } + + virtual void updateCall(lir::UnaryOperation op UNUSED, + void* returnAddress, + void* newTarget) + { + switch (op) { + case lir::Call: + case lir::Jump: + case lir::AlignedCall: + case lir::AlignedJump: { + updateOffset(con.s, + static_cast(returnAddress) - 4, + reinterpret_cast(newTarget)); + } break; + + case lir::LongCall: + case lir::LongJump: + case lir::AlignedLongCall: + case lir::AlignedLongJump: { + uint32_t* p = static_cast(returnAddress) - 2; + if (TargetBytesPerWord == 8) { + const int32_t mask = (PoolOffsetMask >> 2) << 5; + *reinterpret_cast(p + ((*p & mask) >> 5)) = newTarget; + } else { + *reinterpret_cast(p + (((*p & PoolOffsetMask) + 8) / 4)) + = newTarget; + } + } break; + + default: + abort(&con); + } + } + + virtual unsigned constantCallSize() + { + return 4; + } + + virtual void setConstant(void* dst, uint64_t constant) + { + *static_cast(dst) = constant; + } + + virtual unsigned alignFrameSize(unsigned sizeInWords) + { + return pad(sizeInWords + FrameHeaderSize, StackAlignmentInWords) + - FrameHeaderSize; + } + + virtual void nextFrame(void* start, + unsigned size, + unsigned footprint, + void* link, + bool mostRecent, + int targetParameterFootprint, + void** ip, + void** stack) + { + arm::nextFrame(&con, + static_cast(start), + size, + footprint, + link, + mostRecent, + targetParameterFootprint, + ip, + stack); + } + + virtual void* frameIp(void* stack) + { + return stack ? static_cast(stack)[returnAddressOffset()] : 0; + } + + virtual unsigned frameHeaderSize() + { + return FrameHeaderSize; + } + + virtual unsigned frameReturnAddressSize() + { + return 0; + } + + virtual unsigned frameFooterSize() + { + return 0; + } + + virtual int returnAddressOffset() + { + return -1; + } + + virtual int framePointerOffset() + { + return 0; + } + + virtual bool alwaysCondensed(lir::BinaryOperation) + { + return false; + } + + virtual bool alwaysCondensed(lir::TernaryOperation) + { + return false; + } + + virtual void plan(lir::UnaryOperation, + unsigned, + OperandMask& aMask, + bool* thunk) + { + aMask.typeMask = lir::Operand::RegisterPairMask | lir::Operand::ConstantMask; + aMask.setLowHighRegisterMasks(AnyRegisterMask, AnyRegisterMask); + *thunk = false; + } + + virtual void planSource(lir::BinaryOperation op, + unsigned aSize, + OperandMask& aMask, + unsigned bSize, + bool* thunk) + { + *thunk = false; + aMask.typeMask = ~0; + aMask.setLowHighRegisterMasks(GPR_MASK, GPR_MASK); + + switch (op) { + case lir::Negate: + aMask.typeMask = lir::Operand::RegisterPairMask; + aMask.setLowHighRegisterMasks(GPR_MASK, GPR_MASK); + break; + + case lir::Absolute: + *thunk = true; + break; + + case lir::FloatAbsolute: + case lir::FloatSquareRoot: + case lir::FloatNegate: + case lir::Float2Float: + if (vfpSupported()) { + aMask.typeMask = lir::Operand::RegisterPairMask; + aMask.setLowHighRegisterMasks(FPR_MASK, FPR_MASK); + } else { + *thunk = true; + } + break; + + case lir::Float2Int: + // todo: Java requires different semantics than VFP for + // converting floats to integers, we we need to either use + // thunks or produce inline machine code which handles edge + // cases properly. + if (false && vfpSupported() && bSize <= TargetBytesPerWord) { + aMask.typeMask = lir::Operand::RegisterPairMask; + aMask.setLowHighRegisterMasks(FPR_MASK, FPR_MASK); + } else { + *thunk = true; + } + break; + + case lir::Int2Float: + if (vfpSupported() && aSize <= TargetBytesPerWord) { + aMask.typeMask = lir::Operand::RegisterPairMask; + aMask.setLowHighRegisterMasks(GPR_MASK, GPR_MASK); + } else { + *thunk = true; + } + break; + + default: + break; + } + } + + virtual void planDestination(lir::BinaryOperation op, + unsigned, + const OperandMask& aMask, + unsigned, + OperandMask& bMask) + { + bMask.typeMask = lir::Operand::RegisterPairMask | lir::Operand::MemoryMask; + bMask.setLowHighRegisterMasks(GPR_MASK, GPR_MASK); + + switch (op) { + case lir::Negate: + bMask.typeMask = lir::Operand::RegisterPairMask; + bMask.setLowHighRegisterMasks(GPR_MASK, GPR_MASK); + break; + + case lir::FloatAbsolute: + case lir::FloatSquareRoot: + case lir::FloatNegate: + case lir::Float2Float: + case lir::Int2Float: + bMask.typeMask = lir::Operand::RegisterPairMask; + bMask.setLowHighRegisterMasks(FPR_MASK, FPR_MASK); + break; + + case lir::Float2Int: + bMask.typeMask = lir::Operand::RegisterPairMask; + bMask.setLowHighRegisterMasks(GPR_MASK, GPR_MASK); + break; + + case lir::Move: + if (!(aMask.typeMask & lir::Operand::RegisterPairMask)) { + bMask.typeMask = lir::Operand::RegisterPairMask; + } + break; + + default: + break; + } + } + + virtual void planMove(unsigned, + OperandMask& srcMask, + OperandMask& tmpMask, + const OperandMask& dstMask) + { + srcMask.typeMask = ~0; + srcMask.setLowHighRegisterMasks(AnyRegisterMask, AnyRegisterMask); + + tmpMask.typeMask = 0; + tmpMask.setLowHighRegisterMasks(0, 0); + + if (dstMask.typeMask & lir::Operand::MemoryMask) { + // can't move directly from memory or constant to memory + srcMask.typeMask = lir::Operand::RegisterPairMask; + tmpMask.typeMask = lir::Operand::RegisterPairMask; + tmpMask.setLowHighRegisterMasks(GPR_MASK, GPR_MASK); + } else if (vfpSupported() && dstMask.typeMask & lir::Operand::RegisterPairMask + && dstMask.lowRegisterMask & FPR_MASK) { + srcMask.typeMask = tmpMask.typeMask = lir::Operand::RegisterPairMask + | lir::Operand::MemoryMask; + tmpMask.setLowHighRegisterMasks(AnyRegisterMask, AnyRegisterMask); + } + } + + virtual void planSource(lir::TernaryOperation op, + unsigned, + OperandMask& aMask, + unsigned bSize, + OperandMask& bMask, + unsigned, + bool* thunk) + { + aMask.typeMask = lir::Operand::RegisterPairMask | lir::Operand::ConstantMask; + aMask.setLowHighRegisterMasks(GPR_MASK, GPR_MASK); + + bMask.typeMask = lir::Operand::RegisterPairMask; + bMask.setLowHighRegisterMasks(GPR_MASK, GPR_MASK); + + *thunk = false; + + switch (op) { + case lir::ShiftLeft: + case lir::ShiftRight: + case lir::UnsignedShiftRight: + if (bSize > TargetBytesPerWord) + aMask.typeMask = bMask.typeMask = lir::Operand::RegisterPairMask; + break; + + case lir::Add: + case lir::Subtract: + case lir::Or: + case lir::Xor: + case lir::Multiply: + aMask.typeMask = bMask.typeMask = lir::Operand::RegisterPairMask; + break; + + // todo: Although ARM has instructions for integer division and + // remainder, they don't trap on division by zero, which is why + // we use thunks. Alternatively, we could generate inline code + // with an explicit zero check, which would probably be a bit + // faster. + case lir::Divide: + case lir::Remainder: + case lir::FloatRemainder: + *thunk = true; + break; + + case lir::FloatAdd: + case lir::FloatSubtract: + case lir::FloatMultiply: + case lir::FloatDivide: + if (vfpSupported()) { + aMask.typeMask = lir::Operand::RegisterPairMask; + aMask.setLowHighRegisterMasks(FPR_MASK, FPR_MASK); + bMask = aMask; + } else { + *thunk = true; + } + break; + + case lir::JumpIfFloatEqual: + case lir::JumpIfFloatNotEqual: + case lir::JumpIfFloatLess: + case lir::JumpIfFloatGreater: + case lir::JumpIfFloatLessOrEqual: + case lir::JumpIfFloatGreaterOrEqual: + case lir::JumpIfFloatLessOrUnordered: + case lir::JumpIfFloatGreaterOrUnordered: + case lir::JumpIfFloatLessOrEqualOrUnordered: + case lir::JumpIfFloatGreaterOrEqualOrUnordered: + if (vfpSupported()) { + aMask.typeMask = lir::Operand::RegisterPairMask; + aMask.setLowHighRegisterMasks(FPR_MASK, FPR_MASK); + bMask = aMask; + } else { + *thunk = true; + } + break; + + default: + break; + } + } + + virtual void planDestination(lir::TernaryOperation op, + unsigned, + const OperandMask& aMask UNUSED, + unsigned, + const OperandMask& bMask, + unsigned, + OperandMask& cMask) + { + if (isBranch(op)) { + cMask.typeMask = lir::Operand::ConstantMask; + cMask.setLowHighRegisterMasks(0, 0); + } else { + cMask.typeMask = lir::Operand::RegisterPairMask; + cMask.lowRegisterMask = bMask.lowRegisterMask; + cMask.highRegisterMask = bMask.highRegisterMask; + } + } + + virtual Assembler* makeAssembler(Alloc* allocator, Zone* zone); + + virtual void acquire() + { + ++referenceCount; + } + + virtual void release() + { + if (--referenceCount == 0) { + con.s->free(this); + } + } + + ArchitectureContext con; + unsigned referenceCount; +}; + +class MyAssembler : public Assembler { + public: + MyAssembler(System* s, Alloc* a, Zone* zone, MyArchitecture* arch) + : con(s, a, zone), arch_(arch) + { + } + + virtual void setClient(Client* client) + { + assertT(&con, con.client == 0); + con.client = client; + } + + virtual Architecture* arch() + { + return arch_; + } + + virtual void checkStackOverflow(uintptr_t handler, + unsigned stackLimitOffsetFromThread) + { + lir::RegisterPair stack(StackRegister); + lir::Memory stackLimit(ThreadRegister, stackLimitOffsetFromThread); + lir::Constant handlerConstant(new (con.zone) ResolvedPromise(handler)); + branchRM(&con, + lir::JumpIfGreaterOrEqual, + TargetBytesPerWord, + &stack, + &stackLimit, + &handlerConstant); + } + + virtual void saveFrame(unsigned stackOffset, unsigned ipOffset) + { + lir::RegisterPair link(LinkRegister); + lir::Memory linkDst(ThreadRegister, ipOffset); + moveRM(&con, TargetBytesPerWord, &link, TargetBytesPerWord, &linkDst); + + lir::RegisterPair stack(StackRegister); + lir::Memory stackDst(ThreadRegister, stackOffset); + moveRM(&con, TargetBytesPerWord, &stack, TargetBytesPerWord, &stackDst); + } + + virtual void pushFrame(unsigned argumentCount, ...) + { + struct Argument { + unsigned size; + lir::Operand::Type type; + lir::Operand* operand; + }; + RUNTIME_ARRAY(Argument, arguments, argumentCount); + + va_list a; + va_start(a, argumentCount); + unsigned footprint = 0; + for (unsigned i = 0; i < argumentCount; ++i) { + RUNTIME_ARRAY_BODY(arguments)[i].size = va_arg(a, unsigned); + RUNTIME_ARRAY_BODY(arguments)[i].type + = static_cast(va_arg(a, int)); + RUNTIME_ARRAY_BODY(arguments)[i].operand = va_arg(a, lir::Operand*); + footprint += ceilingDivide(RUNTIME_ARRAY_BODY(arguments)[i].size, + TargetBytesPerWord); + } + va_end(a); + + allocateFrame(arch_->alignFrameSize(footprint)); + + unsigned offset = 0; + for (unsigned i = 0; i < argumentCount; ++i) { + if (i < arch_->argumentRegisterCount()) { + lir::RegisterPair dst(arch_->argumentRegister(i)); + + apply(lir::Move, + OperandInfo(RUNTIME_ARRAY_BODY(arguments)[i].size, + RUNTIME_ARRAY_BODY(arguments)[i].type, + RUNTIME_ARRAY_BODY(arguments)[i].operand), + OperandInfo(pad(RUNTIME_ARRAY_BODY(arguments)[i].size, + TargetBytesPerWord), + lir::Operand::Type::RegisterPair, + &dst)); + + offset += ceilingDivide(RUNTIME_ARRAY_BODY(arguments)[i].size, + TargetBytesPerWord); + } else { + lir::Memory dst(StackRegister, offset * TargetBytesPerWord); + + apply(lir::Move, + OperandInfo(RUNTIME_ARRAY_BODY(arguments)[i].size, + RUNTIME_ARRAY_BODY(arguments)[i].type, + RUNTIME_ARRAY_BODY(arguments)[i].operand), + OperandInfo(pad(RUNTIME_ARRAY_BODY(arguments)[i].size, + TargetBytesPerWord), + lir::Operand::Type::Memory, + &dst)); + + offset += ceilingDivide(RUNTIME_ARRAY_BODY(arguments)[i].size, + TargetBytesPerWord); + } + } + } + + virtual void allocateFrame(unsigned footprint) + { + footprint += FrameHeaderSize; + + // larger frames may require multiple subtract/add instructions + // to allocate/deallocate, and nextFrame will need to be taught + // how to handle them: + assertT(&con, footprint < 256); + + // todo: the ARM ABI says the frame preamble should be of the form + // + // stp x29, x30, [sp,#-footprint]! + // mov x29, sp + // + // and the frame should be popped with e.g. + // + // ldp x29, x30, [sp],#footprint + // br x30 + // + // However, that will invalidate a lot of assumptions elsewhere + // about the return address being stored at the opposite end of + // the frame, so lots of other code will need to change before we + // can do that. The code below can be enabled as a starting point + // when we're ready to tackle that. + if (false and TargetBytesPerWord == 8) { + // stp x29, x30, [sp,#-footprint]! + con.code.append4(0xa9800000 | ((-footprint & 0x7f) << 15) + | (StackRegister.index() << 5) + | (LinkRegister.index() << 10) | FrameRegister.index()); + + lir::RegisterPair stack(StackRegister); + lir::RegisterPair frame(FrameRegister); + moveRR(&con, TargetBytesPerWord, &stack, TargetBytesPerWord, &frame); + } else { + lir::RegisterPair stack(StackRegister); + ResolvedPromise footprintPromise(footprint * TargetBytesPerWord); + lir::Constant footprintConstant(&footprintPromise); + subC(&con, TargetBytesPerWord, &footprintConstant, &stack, &stack); + + lir::RegisterPair returnAddress(LinkRegister); + lir::Memory returnAddressDst(StackRegister, + (footprint - 1) * TargetBytesPerWord); + moveRM(&con, + TargetBytesPerWord, + &returnAddress, + TargetBytesPerWord, + &returnAddressDst); + } + } + + virtual void adjustFrame(unsigned difference) + { + lir::RegisterPair stack(StackRegister); + ResolvedPromise differencePromise(difference * TargetBytesPerWord); + lir::Constant differenceConstant(&differencePromise); + subC(&con, TargetBytesPerWord, &differenceConstant, &stack, &stack); + } + + virtual void popFrame(unsigned footprint) + { + footprint += FrameHeaderSize; + + // see comment regarding the ARM64 ABI in allocateFrame + if (false and TargetBytesPerWord == 8) { + // ldp x29, x30, [sp],#footprint + con.code.append4(0xa8c00000 | (footprint << 15) | (31 << 5) | (30 << 10) + | 29); + } else { + lir::RegisterPair returnAddress(LinkRegister); + lir::Memory returnAddressSrc(StackRegister, + (footprint - 1) * TargetBytesPerWord); + moveMR(&con, + TargetBytesPerWord, + &returnAddressSrc, + TargetBytesPerWord, + &returnAddress); + + lir::RegisterPair stack(StackRegister); + ResolvedPromise footprintPromise(footprint * TargetBytesPerWord); + lir::Constant footprintConstant(&footprintPromise); + addC(&con, TargetBytesPerWord, &footprintConstant, &stack, &stack); + } + } + + virtual void popFrameForTailCall(unsigned footprint, + int offset, + Register returnAddressSurrogate, + Register framePointerSurrogate UNUSED) + { + assertT(&con, framePointerSurrogate == NoRegister); + + if (TailCalls) { + if (offset) { + footprint += FrameHeaderSize; + + lir::RegisterPair link(LinkRegister); + lir::Memory returnAddressSrc(StackRegister, + (footprint - 1) * TargetBytesPerWord); + moveMR(&con, + TargetBytesPerWord, + &returnAddressSrc, + TargetBytesPerWord, + &link); + + lir::RegisterPair stack(StackRegister); + ResolvedPromise footprintPromise((footprint - offset) + * TargetBytesPerWord); + lir::Constant footprintConstant(&footprintPromise); + addC(&con, TargetBytesPerWord, &footprintConstant, &stack, &stack); + + if (returnAddressSurrogate != NoRegister) { + assertT(&con, offset > 0); + + lir::RegisterPair ras(returnAddressSurrogate); + lir::Memory dst(StackRegister, (offset - 1) * TargetBytesPerWord); + moveRM(&con, TargetBytesPerWord, &ras, TargetBytesPerWord, &dst); + } + } else { + popFrame(footprint); + } + } else { + abort(&con); + } + } + + virtual void popFrameAndPopArgumentsAndReturn(unsigned frameFootprint, + unsigned argumentFootprint) + { + popFrame(frameFootprint); + + assertT(&con, argumentFootprint >= StackAlignmentInWords); + assertT(&con, (argumentFootprint % StackAlignmentInWords) == 0); + + unsigned offset; + if (TailCalls and argumentFootprint > StackAlignmentInWords) { + offset = argumentFootprint - StackAlignmentInWords; + + lir::RegisterPair stack(StackRegister); + ResolvedPromise adjustmentPromise(offset * TargetBytesPerWord); + lir::Constant adjustment(&adjustmentPromise); + addC(&con, TargetBytesPerWord, &adjustment, &stack, &stack); + } else { + offset = 0; + } + + return_(&con); + } + + virtual void popFrameAndUpdateStackAndReturn(unsigned footprint, + unsigned stackOffsetFromThread) + { + footprint += FrameHeaderSize; + + // see comment regarding the ARM64 ABI in allocateFrame + if (false and TargetBytesPerWord == 8) { + // ldp x29, x30, [sp],#footprint + con.code.append4(0xa8c00000 | (footprint << 15) | (31 << 5) | (30 << 10) + | 29); + } else { + lir::RegisterPair returnAddress(LinkRegister); + lir::Memory returnAddressSrc(StackRegister, + (footprint - 1) * TargetBytesPerWord); + moveMR(&con, + TargetBytesPerWord, + &returnAddressSrc, + TargetBytesPerWord, + &returnAddress); + } + + lir::RegisterPair stack(StackRegister); + lir::Memory newStackSrc(ThreadRegister, stackOffsetFromThread); + moveMR(&con, TargetBytesPerWord, &newStackSrc, TargetBytesPerWord, &stack); + + return_(&con); + } + + virtual void apply(lir::Operation op) + { + arch_->con.operations[op](&con); + } + + virtual void apply(lir::UnaryOperation op, OperandInfo a) + { + arch_->con.unaryOperations[Multimethod::index(op, a.type)]( + &con, a.size, a.operand); + } + + virtual void apply(lir::BinaryOperation op, OperandInfo a, OperandInfo b) + { + arch_->con.binaryOperations[index(&(arch_->con), op, a.type, b.type)]( + &con, a.size, a.operand, b.size, b.operand); + } + + virtual void apply(lir::TernaryOperation op, + OperandInfo a, + OperandInfo b, + OperandInfo c) + { + if (isBranch(op)) { + assertT(&con, a.size == b.size); + assertT(&con, c.size == TargetBytesPerWord); + assertT(&con, c.type == lir::Operand::Type::Constant); + + arch_->con.branchOperations[branchIndex(&(arch_->con), a.type, b.type)]( + &con, op, a.size, a.operand, b.operand, c.operand); + } else { + assertT(&con, b.size == c.size); + assertT(&con, b.type == lir::Operand::Type::RegisterPair); + assertT(&con, c.type == lir::Operand::Type::RegisterPair); + + arch_->con.ternaryOperations[index(&(arch_->con), op, a.type)]( + &con, b.size, a.operand, b.operand, c.operand); + } + } + + virtual void setDestination(uint8_t* dst) + { + con.result = dst; + } + + virtual void write() + { + uint8_t* dst = con.result; + unsigned dstOffset = 0; + for (MyBlock* b = con.firstBlock; b; b = b->next) { + if (DebugPool) { + fprintf(stderr, "write block %p\n", b); + } + + unsigned blockOffset = 0; + for (PoolEvent* e = b->poolEventHead; e; e = e->next) { + unsigned size = e->offset - blockOffset; + memcpy(dst + dstOffset, + con.code.data.begin() + b->offset + blockOffset, + size); + blockOffset = e->offset; + dstOffset += size; + + unsigned poolSize = 0; + for (PoolOffset* o = e->poolOffsetHead; o; o = o->next) { + if (DebugPool) { + fprintf(stderr, + "visit pool offset %p %d in block %p\n", + o, + o->offset, + b); + } + + unsigned entry = dstOffset + poolSize; + + if (needJump(b)) { + entry += TargetBytesPerWord; + } + + o->entry->address = dst + entry; + + unsigned instruction = o->block->start + padding(o->block, o->offset) + + o->offset; + + int32_t* p = reinterpret_cast(dst + instruction); + + if (TargetBytesPerWord == 8) { + int32_t v = entry - instruction; + expect(&con, v == (v & PoolOffsetMask)); + + const int32_t mask = (PoolOffsetMask >> 2) << 5; + *p = (((v >> 2) << 5) & mask) | ((~mask) & *p); + } else { + int32_t v = (entry - 8) - instruction; + expect(&con, v == (v & PoolOffsetMask)); + + *p = (v & PoolOffsetMask) | ((~PoolOffsetMask) & *p); + } + + poolSize += TargetBytesPerWord; + } + + bool jump = needJump(b); + if (jump) { + expect(&con, TargetBytesPerWord == 4); + + write4(dst + dstOffset, + isa::b((poolSize + TargetBytesPerWord - 8) >> 2)); + } + + dstOffset += poolSize + (jump ? TargetBytesPerWord : 0); + } + + unsigned size = b->size - blockOffset; + + memcpy(dst + dstOffset, + con.code.data.begin() + b->offset + blockOffset, + size); + + dstOffset += size; + } + + for (Task* t = con.tasks; t; t = t->next) { + t->run(&con); + } + + for (ConstantPoolEntry* e = con.constantPool; e; e = e->next) { + if (e->constant->resolved()) { + *static_cast(e->address) = e->constant->value(); + } else { + new (e->constant->listen(sizeof(ConstantPoolListener))) + ConstantPoolListener( + con.s, + static_cast(e->address), + e->callOffset ? dst + e->callOffset->value() + 8 : 0); + } + if (false) { + fprintf(stderr, + "constant %p at %p\n", + reinterpret_cast(e->constant->value()), + e->address); + } + } + } + + virtual Promise* offset(bool forTrace) + { + return arm::offsetPromise(&con, forTrace); + } + + virtual Block* endBlock(bool startNew) + { + MyBlock* b = con.lastBlock; + b->size = con.code.length() - b->offset; + if (startNew) { + con.lastBlock = new (con.zone) MyBlock(&con, con.code.length()); + } else { + con.lastBlock = 0; + } + return b; + } + + virtual void endEvent() + { + MyBlock* b = con.lastBlock; + unsigned thisEventOffset = con.code.length() - b->offset; + if (b->poolOffsetHead) { + int32_t v = (thisEventOffset + TargetBytesPerWord - 8) + - b->poolOffsetHead->offset; + + if (v > 0 and v != (v & PoolOffsetMask)) { + appendPoolEvent(&con, + b, + b->lastEventOffset, + b->poolOffsetHead, + b->lastPoolOffsetTail); + + if (DebugPool) { + for (PoolOffset* o = b->poolOffsetHead; + o != b->lastPoolOffsetTail->next; + o = o->next) { + fprintf(stderr, + "in endEvent, include %p %d in pool event %p at offset %d " + "in block %p\n", + o, + o->offset, + b->poolEventTail, + b->lastEventOffset, + b); + } + } + + b->poolOffsetHead = b->lastPoolOffsetTail->next; + b->lastPoolOffsetTail->next = 0; + if (b->poolOffsetHead == 0) { + b->poolOffsetTail = 0; + } + } + } + b->lastEventOffset = thisEventOffset; + b->lastPoolOffsetTail = b->poolOffsetTail; + } + + virtual unsigned length() + { + return con.code.length(); + } + + virtual unsigned footerSize() + { + return 0; + } + + virtual void dispose() + { + con.code.dispose(); + } + + Context con; + MyArchitecture* arch_; +}; + +Assembler* MyArchitecture::makeAssembler(Alloc* allocator, Zone* zone) +{ + return new (zone) MyAssembler(this->con.s, allocator, zone, this); +} + +} // namespace arm + +Architecture* makeArchitectureArm(System* system, bool) +{ + return new (allocate(system, sizeof(arm::MyArchitecture))) + arm::MyArchitecture(system); +} + +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/target/arm/block.cpp b/sgx-jvm/avian/src/codegen/target/arm/block.cpp new file mode 100644 index 0000000000..fe2805e143 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/arm/block.cpp @@ -0,0 +1,49 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "context.h" +#include "block.h" + +namespace avian { +namespace codegen { +namespace arm { + +void resolve(MyBlock*); + +unsigned padding(MyBlock*, unsigned); + +MyBlock::MyBlock(Context* context, unsigned offset) + : context(context), + next(0), + poolOffsetHead(0), + poolOffsetTail(0), + lastPoolOffsetTail(0), + poolEventHead(0), + poolEventTail(0), + lastEventOffset(0), + offset(offset), + start(~0), + size(0) +{ +} + +unsigned MyBlock::resolve(unsigned start, Assembler::Block* next) +{ + this->start = start; + this->next = static_cast(next); + + arm::resolve(this); + + return start + size + padding(this, size); +} + +} // namespace arm +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/target/arm/block.h b/sgx-jvm/avian/src/codegen/target/arm/block.h new file mode 100644 index 0000000000..55f383c78f --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/arm/block.h @@ -0,0 +1,46 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_ARM_BLOCK_H +#define AVIAN_CODEGEN_ASSEMBLER_ARM_BLOCK_H + +#include +#include + +namespace avian { +namespace codegen { +namespace arm { + +class PoolEvent; + +class MyBlock : public Assembler::Block { + public: + MyBlock(Context* context, unsigned offset); + + virtual unsigned resolve(unsigned start, Assembler::Block* next); + + Context* context; + MyBlock* next; + PoolOffset* poolOffsetHead; + PoolOffset* poolOffsetTail; + PoolOffset* lastPoolOffsetTail; + PoolEvent* poolEventHead; + PoolEvent* poolEventTail; + unsigned lastEventOffset; + unsigned offset; + unsigned start; + unsigned size; +}; + +} // namespace arm +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_ARM_BLOCK_H diff --git a/sgx-jvm/avian/src/codegen/target/arm/context.cpp b/sgx-jvm/avian/src/codegen/target/arm/context.cpp new file mode 100644 index 0000000000..fa455fa622 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/arm/context.cpp @@ -0,0 +1,36 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "context.h" +#include "block.h" + +namespace avian { +namespace codegen { +namespace arm { + +Context::Context(vm::System* s, util::Alloc* a, vm::Zone* zone) + : s(s), + zone(zone), + client(0), + code(s, a, 1024), + tasks(0), + result(0), + firstBlock(new (zone) MyBlock(this, 0)), + lastBlock(firstBlock), + poolOffsetHead(0), + poolOffsetTail(0), + constantPool(0), + constantPoolCount(0) +{ +} + +} // namespace arm +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/target/arm/context.h b/sgx-jvm/avian/src/codegen/target/arm/context.h new file mode 100644 index 0000000000..d5eb90b8c6 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/arm/context.h @@ -0,0 +1,113 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_ARM_CONTEXT_H +#define AVIAN_CODEGEN_ASSEMBLER_ARM_CONTEXT_H + +#include +#include +#include "avian/alloc-vector.h" + +namespace vm { +class System; +class Zone; +} // namespace vm + +namespace avian { + +namespace util { +class Aborter; +class Alloc; +} // namespace util + +namespace codegen { +namespace arm { + +class Task; +class MyBlock; +class PoolOffset; +class ConstantPoolEntry; + +class Context { + public: + Context(vm::System* s, util::Alloc* a, vm::Zone* zone); + + vm::System* s; + vm::Zone* zone; + Assembler::Client* client; + vm::Vector code; + Task* tasks; + uint8_t* result; + MyBlock* firstBlock; + MyBlock* lastBlock; + PoolOffset* poolOffsetHead; + PoolOffset* poolOffsetTail; + ConstantPoolEntry* constantPool; + unsigned constantPoolCount; +}; + +typedef void (*OperationType)(Context*); + +typedef void (*UnaryOperationType)(Context*, unsigned, lir::Operand*); + +typedef void (*BinaryOperationType)(Context*, + unsigned, + lir::Operand*, + unsigned, + lir::Operand*); + +typedef void (*TernaryOperationType)(Context*, + unsigned, + lir::Operand*, + lir::Operand*, + lir::Operand*); + +typedef void (*BranchOperationType)(Context*, + lir::TernaryOperation, + unsigned, + lir::Operand*, + lir::Operand*, + lir::Operand*); + +class ArchitectureContext { + public: + ArchitectureContext(vm::System* s) : s(s) + { + } + + vm::System* s; + OperationType operations[lir::OperationCount]; + UnaryOperationType + unaryOperations[lir::UnaryOperationCount * lir::Operand::TypeCount]; + BinaryOperationType binaryOperations[lir::BinaryOperationCount + * lir::Operand::TypeCount + * lir::Operand::TypeCount]; + TernaryOperationType ternaryOperations[lir::NonBranchTernaryOperationCount + * lir::Operand::TypeCount]; + BranchOperationType branchOperations[lir::BranchOperationCount + * lir::Operand::TypeCount + * lir::Operand::TypeCount]; +}; + +inline avian::util::Aborter* getAborter(Context* c) +{ + return c->s; +} + +inline avian::util::Aborter* getAborter(ArchitectureContext* c) +{ + return c->s; +} + +} // namespace arm +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_ARM_CONTEXT_H diff --git a/sgx-jvm/avian/src/codegen/target/arm/encode.h b/sgx-jvm/avian/src/codegen/target/arm/encode.h new file mode 100644 index 0000000000..b6ea050fe2 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/arm/encode.h @@ -0,0 +1,692 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_ARM_ENCODE_H +#define AVIAN_CODEGEN_ASSEMBLER_ARM_ENCODE_H + +#include +#include + +namespace avian { +namespace codegen { +namespace arm { + +namespace isa { + +// SYSTEM REGISTERS +const int FPSID = 0x0; +const int FPSCR = 0x1; +const int FPEXC = 0x8; +// INSTRUCTION OPTIONS +enum CONDITION { + EQ, + NE, + CS, + CC, + MI, + PL, + VS, + VC, + HI, + LS, + GE, + LT, + GT, + LE, + AL, + NV +}; +enum SHIFTOP { LSL, LSR, ASR, ROR }; +// INSTRUCTION FORMATS +inline int + DATA(int cond, int opcode, int S, Register Rn, Register Rd, int shift, int Sh, Register Rm) +{ + return cond << 28 | opcode << 21 | S << 20 | Rn.index() << 16 | Rd.index() << 12 | shift << 7 + | Sh << 5 | Rm.index(); +} +inline int + DATAS(int cond, int opcode, int S, Register Rn, Register Rd, Register Rs, int Sh, Register Rm) +{ + return cond << 28 | opcode << 21 | S << 20 | Rn.index() << 16 | Rd.index() << 12 | Rs.index() << 8 + | Sh << 5 | 1 << 4 | Rm.index(); +} +inline int DATAI(int cond, int opcode, int S, Register Rn, Register Rd, int rot, int imm) +{ + return cond << 28 | 1 << 25 | opcode << 21 | S << 20 | Rn.index() << 16 | Rd.index() << 12 + | rot << 8 | (imm & 0xff); +} +inline int BRANCH(int cond, int L, int offset) +{ + return cond << 28 | 5 << 25 | L << 24 | (offset & 0xffffff); +} +inline int BRANCHX(int cond, int L, Register Rm) +{ + return cond << 28 | 0x4bffc << 6 | L << 5 | 1 << 4 | Rm.index(); +} +inline int MULTIPLY(int cond, int mul, int S, Register Rd, Register Rn, Register Rs, Register Rm) +{ + return cond << 28 | mul << 21 | S << 20 | Rd.index() << 16 | Rn.index() << 12 | Rs.index() << 8 + | 9 << 4 | Rm.index(); +} +inline int XFER(int cond, + int P, + int U, + int B, + int W, + int L, + Register Rn, + Register Rd, + int shift, + int Sh, + Register Rm) +{ + return cond << 28 | 3 << 25 | P << 24 | U << 23 | B << 22 | W << 21 | L << 20 + | Rn.index() << 16 | Rd.index() << 12 | shift << 7 | Sh << 5 | Rm.index(); +} +inline int XFERI(int cond, + int P, + int U, + int B, + int W, + int L, + Register Rn, + Register Rd, + int offset) +{ + return cond << 28 | 2 << 25 | P << 24 | U << 23 | B << 22 | W << 21 | L << 20 + | Rn.index() << 16 | Rd.index() << 12 | (offset & 0xfff); +} +inline int XFER2(int cond, + int P, + int U, + int W, + int L, + Register Rn, + Register Rd, + int S, + int H, + Register Rm) +{ + return cond << 28 | P << 24 | U << 23 | W << 21 | L << 20 | Rn.index() << 16 + | Rd.index() << 12 | 1 << 7 | S << 6 | H << 5 | 1 << 4 | Rm.index(); +} +inline int XFER2I(int cond, + int P, + int U, + int W, + int L, + Register Rn, + Register Rd, + int offsetH, + int S, + int H, + int offsetL) +{ + return cond << 28 | P << 24 | U << 23 | 1 << 22 | W << 21 | L << 20 | Rn.index() << 16 + | Rd.index() << 12 | offsetH << 8 | 1 << 7 | S << 6 | H << 5 | 1 << 4 + | (offsetL & 0xf); +} +inline int COOP(int cond, + int opcode_1, + int CRn, + int CRd, + int cp_num, + int opcode_2, + int CRm) +{ + return cond << 28 | 0xe << 24 | opcode_1 << 20 | CRn << 16 | CRd << 12 + | cp_num << 8 | opcode_2 << 5 | CRm; +} +inline int COXFER(int cond, + int P, + int U, + int N, + int W, + int L, + Register Rn, + int CRd, + int cp_num, + int offset) // offset is in words, not bytes +{ + return cond << 28 | 0x6 << 25 | P << 24 | U << 23 | N << 22 | W << 21 + | L << 20 | Rn.index() << 16 | CRd << 12 | cp_num << 8 | (offset & 0xff) >> 2; +} +inline int COREG(int cond, + int opcode_1, + int L, + int CRn, + Register Rd, + int cp_num, + int opcode_2, + int CRm) +{ + return cond << 28 | 0xe << 24 | opcode_1 << 21 | L << 20 | CRn << 16 + | Rd.index() << 12 | cp_num << 8 | opcode_2 << 5 | 1 << 4 | CRm; +} +inline int + COREG2(int cond, int L, Register Rn, Register Rd, int cp_num, int opcode, int CRm) +{ + return cond << 28 | 0xc4 << 20 | L << 20 | Rn.index() << 16 | Rd.index() << 12 | cp_num << 8 + | opcode << 4 | CRm; +} +// FIELD CALCULATORS +inline int calcU(int imm) +{ + return imm >= 0 ? 1 : 0; +} +// INSTRUCTIONS +// The "cond" and "S" fields are set using the SETCOND() and SETS() functions +inline int b(int offset) +{ + return BRANCH(AL, 0, offset); +} +inline int bl(int offset) +{ + return BRANCH(AL, 1, offset); +} +inline int bx(Register Rm) +{ + return BRANCHX(AL, 0, Rm); +} +inline int blx(Register Rm) +{ + return BRANCHX(AL, 1, Rm); +} +inline int and_(Register Rd, Register Rn, Register Rm, int Sh = 0, int shift = 0) +{ + return DATA(AL, 0x0, 0, Rn, Rd, shift, Sh, Rm); +} +inline int eor(Register Rd, Register Rn, Register Rm, int Sh = 0, int shift = 0) +{ + return DATA(AL, 0x1, 0, Rn, Rd, shift, Sh, Rm); +} +inline int rsb(Register Rd, Register Rn, Register Rm, int Sh = 0, int shift = 0) +{ + return DATA(AL, 0x3, 0, Rn, Rd, shift, Sh, Rm); +} +inline int add(Register Rd, Register Rn, Register Rm, int Sh = 0, int shift = 0) +{ + return DATA(AL, 0x4, 0, Rn, Rd, shift, Sh, Rm); +} +inline int adc(Register Rd, Register Rn, Register Rm, int Sh = 0, int shift = 0) +{ + return DATA(AL, 0x5, 0, Rn, Rd, shift, Sh, Rm); +} +inline int rsc(Register Rd, Register Rn, Register Rm, int Sh = 0, int shift = 0) +{ + return DATA(AL, 0x7, 0, Rn, Rd, shift, Sh, Rm); +} +inline int cmp(Register Rn, Register Rm, int Sh = 0, int shift = 0) +{ + return DATA(AL, 0xa, 1, Rn, Register(0), shift, Sh, Rm); +} +inline int orr(Register Rd, Register Rn, Register Rm, int Sh = 0, int shift = 0) +{ + return DATA(AL, 0xc, 0, Rn, Rd, shift, Sh, Rm); +} +inline int mov(Register Rd, Register Rm, int Sh = 0, int shift = 0) +{ + return DATA(AL, 0xd, 0, Register(0), Rd, shift, Sh, Rm); +} +inline int mvn(Register Rd, Register Rm, int Sh = 0, int shift = 0) +{ + return DATA(AL, 0xf, 0, Register(0), Rd, shift, Sh, Rm); +} +inline int andi(Register Rd, Register Rn, int imm, int rot = 0) +{ + return DATAI(AL, 0x0, 0, Rn, Rd, rot, imm); +} +inline int subi(Register Rd, Register Rn, int imm, int rot = 0) +{ + return DATAI(AL, 0x2, 0, Rn, Rd, rot, imm); +} +inline int rsbi(Register Rd, Register Rn, int imm, int rot = 0) +{ + return DATAI(AL, 0x3, 0, Rn, Rd, rot, imm); +} +inline int addi(Register Rd, Register Rn, int imm, int rot = 0) +{ + return DATAI(AL, 0x4, 0, Rn, Rd, rot, imm); +} +inline int adci(Register Rd, Register Rn, int imm, int rot = 0) +{ + return DATAI(AL, 0x5, 0, Rn, Rd, rot, imm); +} +inline int bici(Register Rd, Register Rn, int imm, int rot = 0) +{ + return DATAI(AL, 0xe, 0, Rn, Rd, rot, imm); +} +inline int cmpi(Register Rn, int imm, int rot = 0) +{ + return DATAI(AL, 0xa, 1, Rn, Register(0), rot, imm); +} +inline int movi(Register Rd, int imm, int rot = 0) +{ + return DATAI(AL, 0xd, 0, Register(0), Rd, rot, imm); +} +inline int orrsh(Register Rd, Register Rn, Register Rm, Register Rs, int Sh) +{ + return DATAS(AL, 0xc, 0, Rn, Rd, Rs, Sh, Rm); +} +inline int movsh(Register Rd, Register Rm, Register Rs, int Sh) +{ + return DATAS(AL, 0xd, 0, Register(0), Rd, Rs, Sh, Rm); +} +inline int mul(Register Rd, Register Rm, Register Rs) +{ + return MULTIPLY(AL, 0, 0, Rd, Register(0), Rs, Rm); +} +inline int mla(Register Rd, Register Rm, Register Rs, Register Rn) +{ + return MULTIPLY(AL, 1, 0, Rd, Rn, Rs, Rm); +} +inline int umull(Register RdLo, Register RdHi, Register Rm, Register Rs) +{ + return MULTIPLY(AL, 4, 0, RdHi, RdLo, Rs, Rm); +} +inline int ldr(Register Rd, Register Rn, Register Rm, int W = 0) +{ + return XFER(AL, 1, 1, 0, W, 1, Rn, Rd, 0, 0, Rm); +} +inline int ldri(Register Rd, Register Rn, int imm, int W = 0) +{ + return XFERI(AL, 1, calcU(imm), 0, W, 1, Rn, Rd, abs(imm)); +} +inline int ldrb(Register Rd, Register Rn, Register Rm) +{ + return XFER(AL, 1, 1, 1, 0, 1, Rn, Rd, 0, 0, Rm); +} +inline int ldrbi(Register Rd, Register Rn, int imm) +{ + return XFERI(AL, 1, calcU(imm), 1, 0, 1, Rn, Rd, abs(imm)); +} +inline int str(Register Rd, Register Rn, Register Rm, int W = 0) +{ + return XFER(AL, 1, 1, 0, W, 0, Rn, Rd, 0, 0, Rm); +} +inline int stri(Register Rd, Register Rn, int imm, int W = 0) +{ + return XFERI(AL, 1, calcU(imm), 0, W, 0, Rn, Rd, abs(imm)); +} +inline int strb(Register Rd, Register Rn, Register Rm) +{ + return XFER(AL, 1, 1, 1, 0, 0, Rn, Rd, 0, 0, Rm); +} +inline int strbi(Register Rd, Register Rn, int imm) +{ + return XFERI(AL, 1, calcU(imm), 1, 0, 0, Rn, Rd, abs(imm)); +} +inline int ldrh(Register Rd, Register Rn, Register Rm) +{ + return XFER2(AL, 1, 1, 0, 1, Rn, Rd, 0, 1, Rm); +} +inline int ldrhi(Register Rd, Register Rn, int imm) +{ + return XFER2I(AL, + 1, + calcU(imm), + 0, + 1, + Rn, + Rd, + abs(imm) >> 4 & 0xf, + 0, + 1, + abs(imm) & 0xf); +} +inline int strh(Register Rd, Register Rn, Register Rm) +{ + return XFER2(AL, 1, 1, 0, 0, Rn, Rd, 0, 1, Rm); +} +inline int strhi(Register Rd, Register Rn, int imm) +{ + return XFER2I(AL, + 1, + calcU(imm), + 0, + 0, + Rn, + Rd, + abs(imm) >> 4 & 0xf, + 0, + 1, + abs(imm) & 0xf); +} +inline int ldrsh(Register Rd, Register Rn, Register Rm) +{ + return XFER2(AL, 1, 1, 0, 1, Rn, Rd, 1, 1, Rm); +} +inline int ldrshi(Register Rd, Register Rn, int imm) +{ + return XFER2I(AL, + 1, + calcU(imm), + 0, + 1, + Rn, + Rd, + abs(imm) >> 4 & 0xf, + 1, + 1, + abs(imm) & 0xf); +} +inline int ldrsb(Register Rd, Register Rn, Register Rm) +{ + return XFER2(AL, 1, 1, 0, 1, Rn, Rd, 1, 0, Rm); +} +inline int ldrsbi(Register Rd, Register Rn, int imm) +{ + return XFER2I(AL, + 1, + calcU(imm), + 0, + 1, + Rn, + Rd, + abs(imm) >> 4 & 0xf, + 1, + 0, + abs(imm) & 0xf); +} +// breakpoint instruction, this really has its own instruction format +inline int bkpt(int16_t immed) +{ + return 0xe1200070 | (((unsigned)immed & 0xffff) >> 4 << 8) | (immed & 0xf); +} +// COPROCESSOR INSTRUCTIONS +inline int mcr(int coproc, + int opcode_1, + Register Rd, + int CRn, + int CRm, + int opcode_2 = 0) +{ + return COREG(AL, opcode_1, 0, CRn, Rd, coproc, opcode_2, CRm); +} +inline int mcrr(int coproc, int opcode, Register Rd, Register Rn, int CRm) +{ + return COREG2(AL, 0, Rn, Rd, coproc, opcode, CRm); +} +inline int mrc(int coproc, + int opcode_1, + Register Rd, + int CRn, + int CRm, + int opcode_2 = 0) +{ + return COREG(AL, opcode_1, 1, CRn, Rd, coproc, opcode_2, CRm); +} +inline int mrrc(int coproc, int opcode, Register Rd, Register Rn, int CRm) +{ + return COREG2(AL, 1, Rn, Rd, coproc, opcode, CRm); +} +// VFP FLOATING-POINT INSTRUCTIONS +inline int fmuls(int Sd, int Sn, int Sm) +{ + return COOP(AL, + (Sd & 1) << 2 | 2, + Sn >> 1, + Sd >> 1, + 10, + (Sn & 1) << 2 | (Sm & 1), + Sm >> 1); +} +inline int fadds(int Sd, int Sn, int Sm) +{ + return COOP(AL, + (Sd & 1) << 2 | 3, + Sn >> 1, + Sd >> 1, + 10, + (Sn & 1) << 2 | (Sm & 1), + Sm >> 1); +} +inline int fsubs(int Sd, int Sn, int Sm) +{ + return COOP(AL, + (Sd & 1) << 2 | 3, + Sn >> 1, + Sd >> 1, + 10, + (Sn & 1) << 2 | (Sm & 1) | 2, + Sm >> 1); +} +inline int fdivs(int Sd, int Sn, int Sm) +{ + return COOP(AL, + (Sd & 1) << 2 | 8, + Sn >> 1, + Sd >> 1, + 10, + (Sn & 1) << 2 | (Sm & 1), + Sm >> 1); +} +inline int fmuld(int Dd, int Dn, int Dm) +{ + return COOP(AL, 2, Dn, Dd, 11, 0, Dm); +} +inline int faddd(int Dd, int Dn, int Dm) +{ + return COOP(AL, 3, Dn, Dd, 11, 0, Dm); +} +inline int fsubd(int Dd, int Dn, int Dm) +{ + return COOP(AL, 3, Dn, Dd, 11, 2, Dm); +} +inline int fdivd(int Dd, int Dn, int Dm) +{ + return COOP(AL, 8, Dn, Dd, 11, 0, Dm); +} +inline int fcpys(int Sd, int Sm) +{ + return COOP(AL, 0xb | (Sd & 1) << 2, 0, Sd >> 1, 10, 2 | (Sm & 1), Sm >> 1); +} +inline int fabss(int Sd, int Sm) +{ + return COOP(AL, 0xb | (Sd & 1) << 2, 0, Sd >> 1, 10, 6 | (Sm & 1), Sm >> 1); +} +inline int fnegs(int Sd, int Sm) +{ + return COOP(AL, 0xb | (Sd & 1) << 2, 1, Sd >> 1, 10, 2 | (Sm & 1), Sm >> 1); +} +inline int fsqrts(int Sd, int Sm) +{ + return COOP(AL, 0xb | (Sd & 1) << 2, 1, Sd >> 1, 10, 6 | (Sm & 1), Sm >> 1); +} +inline int fcmps(int Sd, int Sm) +{ + return COOP(AL, 0xb | (Sd & 1) << 2, 4, Sd >> 1, 10, 2 | (Sm & 1), Sm >> 1); +} +inline int fcvtds(int Dd, int Sm) +{ + return COOP(AL, 0xb, 7, Dd, 10, 6 | (Sm & 1), Sm >> 1); +} +inline int fsitos(int Sd, int Sm) +{ + return COOP(AL, 0xb | (Sd & 1) << 2, 8, Sd >> 1, 10, 6 | (Sm & 1), Sm >> 1); +} +inline int ftosizs(int Sd, int Sm) +{ + return COOP(AL, 0xb | (Sd & 1) << 2, 0xd, Sd >> 1, 10, 6 | (Sm & 1), Sm >> 1); +} +inline int fcpyd(int Dd, int Dm) +{ + return COOP(AL, 0xb, 0, Dd, 11, 2, Dm); +} +inline int fabsd(int Dd, int Dm) +{ + return COOP(AL, 0xb, 0, Dd, 11, 6, Dm); +} +inline int fnegd(int Dd, int Dm) +{ + return COOP(AL, 0xb, 1, Dd, 11, 2, Dm); +} +inline int fsqrtd(int Dd, int Dm) +{ + return COOP(AL, 0xb, 1, Dd, 11, 6, Dm); +} +// double-precision comparison instructions +inline int fcmpd(int Dd, int Dm) +{ + return COOP(AL, 0xb, 4, Dd, 11, 2, Dm); +} +// double-precision conversion instructions +inline int fcvtsd(int Sd, int Dm) +{ + return COOP(AL, 0xb | (Sd & 1) << 2, 7, Sd >> 1, 11, 6, Dm); +} +inline int fsitod(int Dd, int Sm) +{ + return COOP(AL, 0xb, 8, Dd, 11, 6 | (Sm & 1), Sm >> 1); +} +inline int ftosizd(int Sd, int Dm) +{ + return COOP(AL, 0xb | (Sd & 1) << 2, 0xd, Sd >> 1, 11, 6, Dm); +} +// single load/store instructions for both precision types +inline int flds(int Sd, Register Rn, int offset = 0) +{ + return COXFER(AL, 1, 1, Sd & 1, 0, 1, Rn, Sd >> 1, 10, offset); +}; +inline int fldd(int Dd, Register Rn, int offset = 0) +{ + return COXFER(AL, 1, 1, 0, 0, 1, Rn, Dd, 11, offset); +}; +inline int fsts(int Sd, Register Rn, int offset = 0) +{ + return COXFER(AL, 1, 1, Sd & 1, 0, 0, Rn, Sd >> 1, 10, offset); +}; +inline int fstd(int Dd, Register Rn, int offset = 0) +{ + return COXFER(AL, 1, 1, 0, 0, 0, Rn, Dd, 11, offset); +}; +// move between GPRs and FPRs +inline int fmsr(int Sn, Register Rd) +{ + return mcr(10, 0, Rd, Sn >> 1, 0, (Sn & 1) << 2); +} +inline int fmrs(Register Rd, int Sn) +{ + return mrc(10, 0, Rd, Sn >> 1, 0, (Sn & 1) << 2); +} +// move to/from VFP system registers +inline int fmrx(Register Rd, int reg) +{ + return mrc(10, 7, Rd, reg, 0); +} +// these move around pairs of single-precision registers +inline int fmdrr(int Dm, Register Rd, Register Rn) +{ + return mcrr(11, 1, Rd, Rn, Dm); +} +inline int fmrrd(Register Rd, Register Rn, int Dm) +{ + return mrrc(11, 1, Rd, Rn, Dm); +} +// FLAG SETTERS +inline int SETCOND(int ins, int cond) +{ + return ((ins & 0x0fffffff) | (cond << 28)); +} +inline int SETS(int ins) +{ + return ins | 1 << 20; +} +// PSEUDO-INSTRUCTIONS +inline int lsl(Register Rd, Register Rm, Register Rs) +{ + return movsh(Rd, Rm, Rs, LSL); +} +inline int lsli(Register Rd, Register Rm, int imm) +{ + return mov(Rd, Rm, LSL, imm); +} +inline int lsr(Register Rd, Register Rm, Register Rs) +{ + return movsh(Rd, Rm, Rs, LSR); +} +inline int lsri(Register Rd, Register Rm, int imm) +{ + return mov(Rd, Rm, LSR, imm); +} +inline int asr(Register Rd, Register Rm, Register Rs) +{ + return movsh(Rd, Rm, Rs, ASR); +} +inline int asri(Register Rd, Register Rm, int imm) +{ + return mov(Rd, Rm, ASR, imm); +} +inline int beq(int offset) +{ + return SETCOND(b(offset), EQ); +} +inline int bne(int offset) +{ + return SETCOND(b(offset), NE); +} +inline int bls(int offset) +{ + return SETCOND(b(offset), LS); +} +inline int bhi(int offset) +{ + return SETCOND(b(offset), HI); +} +inline int blt(int offset) +{ + return SETCOND(b(offset), LT); +} +inline int bgt(int offset) +{ + return SETCOND(b(offset), GT); +} +inline int ble(int offset) +{ + return SETCOND(b(offset), LE); +} +inline int bge(int offset) +{ + return SETCOND(b(offset), GE); +} +inline int blo(int offset) +{ + return SETCOND(b(offset), CC); +} +inline int bhs(int offset) +{ + return SETCOND(b(offset), CS); +} +inline int bpl(int offset) +{ + return SETCOND(b(offset), PL); +} +inline int fmstat() +{ + return fmrx(Register(15), FPSCR); +} +// todo: make this pretty: +inline int dmb() +{ + return 0xf57ff05f; +} + +} // namespace isa + +inline void emit(Context* con, int code) +{ + con->code.append4(code); +} + +} // namespace arm +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_ARM_ENCODE_H diff --git a/sgx-jvm/avian/src/codegen/target/arm/fixup.cpp b/sgx-jvm/avian/src/codegen/target/arm/fixup.cpp new file mode 100644 index 0000000000..d345ae3cba --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/arm/fixup.cpp @@ -0,0 +1,333 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "context.h" +#include "fixup.h" +#include "block.h" + +namespace { + +const unsigned InstructionSize = 4; + +} // namespace + +namespace avian { +namespace codegen { +namespace arm { + +using namespace util; + +unsigned padding(MyBlock*, unsigned); + +OffsetPromise::OffsetPromise(Context* con, + MyBlock* block, + unsigned offset, + bool forTrace) + : con(con), block(block), offset(offset), forTrace(forTrace) +{ +} + +bool OffsetPromise::resolved() +{ + return block->start != static_cast(~0); +} + +int64_t OffsetPromise::value() +{ + assertT(con, resolved()); + + unsigned o = offset - block->offset; + return block->start + padding(block, forTrace ? o - InstructionSize : o) + o; +} + +Promise* offsetPromise(Context* con, bool forTrace) +{ + return new (con->zone) + OffsetPromise(con, con->lastBlock, con->code.length(), forTrace); +} + +OffsetListener::OffsetListener(vm::System* s, uint8_t* instruction) + : s(s), instruction(instruction) +{ +} + +bool OffsetListener::resolve(int64_t value, void** location) +{ + void* p = updateOffset(s, instruction, value); + if (location) + *location = p; + return false; +} + +OffsetTask::OffsetTask(Task* next, Promise* promise, Promise* instructionOffset) + : Task(next), promise(promise), instructionOffset(instructionOffset) +{ +} + +void OffsetTask::run(Context* con) +{ + if (promise->resolved()) { + updateOffset( + con->s, con->result + instructionOffset->value(), promise->value()); + } else { + new (promise->listen(sizeof(OffsetListener))) + OffsetListener(con->s, con->result + instructionOffset->value()); + } +} + +void appendOffsetTask(Context* con, + Promise* promise, + Promise* instructionOffset) +{ + con->tasks = new (con->zone) + OffsetTask(con->tasks, promise, instructionOffset); +} + +bool bounded(int right, int left, int32_t v) +{ + return ((v << left) >> left) == v and ((v >> right) << right) == v; +} + +void* updateOffset(vm::System* s, uint8_t* instruction, int64_t value) +{ + int32_t* p = reinterpret_cast(instruction); + + int32_t v; + int32_t mask; + if (vm::TargetBytesPerWord == 8) { + if ((*p >> 24) == 0x54) { + // conditional branch + v = ((reinterpret_cast(value) - instruction) >> 2) << 5; + mask = 0xFFFFE0; + expect(s, bounded(5, 8, v)); + } else { + // unconditional branch + v = (reinterpret_cast(value) - instruction) >> 2; + mask = 0x3FFFFFF; + expect(s, bounded(0, 6, v)); + } + } else { + v = (reinterpret_cast(value) - (instruction + 8)) >> 2; + mask = 0xFFFFFF; + expect(s, bounded(0, 8, v)); + } + + *p = (v & mask) | ((~mask) & *p); + + return instruction + InstructionSize; +} + +ConstantPoolEntry::ConstantPoolEntry(Context* con, + Promise* constant, + ConstantPoolEntry* next, + Promise* callOffset) + : con(con), + constant(constant), + next(next), + callOffset(callOffset), + address(0) +{ +} + +int64_t ConstantPoolEntry::value() +{ + assertT(con, resolved()); + + return reinterpret_cast(address); +} + +bool ConstantPoolEntry::resolved() +{ + return address != 0; +} + +ConstantPoolListener::ConstantPoolListener(vm::System* s, + vm::target_uintptr_t* address, + uint8_t* returnAddress) + : s(s), address(address), returnAddress(returnAddress) +{ +} + +bool ConstantPoolListener::resolve(int64_t value, void** location) +{ + *address = value; + if (location) { + *location = returnAddress ? static_cast(returnAddress) : address; + } + return true; +} + +PoolOffset::PoolOffset(MyBlock* block, + ConstantPoolEntry* entry, + unsigned offset) + : block(block), entry(entry), next(0), offset(offset) +{ +} + +PoolEvent::PoolEvent(PoolOffset* poolOffsetHead, + PoolOffset* poolOffsetTail, + unsigned offset) + : poolOffsetHead(poolOffsetHead), + poolOffsetTail(poolOffsetTail), + next(0), + offset(offset) +{ +} + +void appendConstantPoolEntry(Context* con, + Promise* constant, + Promise* callOffset) +{ + if (constant->resolved()) { + // make a copy, since the original might be allocated on the + // stack, and we need our copy to live until assembly is complete + constant = new (con->zone) ResolvedPromise(constant->value()); + } + + con->constantPool = new (con->zone) + ConstantPoolEntry(con, constant, con->constantPool, callOffset); + + ++con->constantPoolCount; + + PoolOffset* o = new (con->zone) + PoolOffset(con->lastBlock, + con->constantPool, + con->code.length() - con->lastBlock->offset); + + if (DebugPool) { + fprintf(stderr, + "add pool offset %p %d to block %p\n", + o, + o->offset, + con->lastBlock); + } + + if (con->lastBlock->poolOffsetTail) { + con->lastBlock->poolOffsetTail->next = o; + } else { + con->lastBlock->poolOffsetHead = o; + } + con->lastBlock->poolOffsetTail = o; +} + +void appendPoolEvent(Context* con, + MyBlock* b, + unsigned offset, + PoolOffset* head, + PoolOffset* tail) +{ + PoolEvent* e = new (con->zone) PoolEvent(head, tail, offset); + + if (b->poolEventTail) { + b->poolEventTail->next = e; + } else { + b->poolEventHead = e; + } + b->poolEventTail = e; +} + +bool needJump(MyBlock* b) +{ + return b->next or b->size != (b->size & PoolOffsetMask); +} + +unsigned padding(MyBlock* b, unsigned offset) +{ + unsigned total = 0; + for (PoolEvent* e = b->poolEventHead; e; e = e->next) { + if (e->offset <= offset) { + if (needJump(b)) { + total += vm::TargetBytesPerWord; + } + for (PoolOffset* o = e->poolOffsetHead; o; o = o->next) { + total += vm::TargetBytesPerWord; + } + } else { + break; + } + } + return total; +} + +void resolve(MyBlock* b) +{ + Context* con = b->context; + + if (b->poolOffsetHead) { + if (con->poolOffsetTail) { + con->poolOffsetTail->next = b->poolOffsetHead; + } else { + con->poolOffsetHead = b->poolOffsetHead; + } + con->poolOffsetTail = b->poolOffsetTail; + } + + if (con->poolOffsetHead) { + bool append; + if (b->next == 0 or b->next->poolEventHead) { + append = true; + } else { + int32_t v + = (b->start + b->size + b->next->size + vm::TargetBytesPerWord - 8) + - (con->poolOffsetHead->offset + con->poolOffsetHead->block->start); + + append = (v != (v & PoolOffsetMask)); + + if (DebugPool) { + fprintf(stderr, + "current %p %d %d next %p %d %d\n", + b, + b->start, + b->size, + b->next, + b->start + b->size, + b->next->size); + fprintf(stderr, + "offset %p %d is of distance %d to next block; append? %d\n", + con->poolOffsetHead, + con->poolOffsetHead->offset, + v, + append); + } + } + + if (append) { +#ifndef NDEBUG + int32_t v + = (b->start + b->size - 8) + - (con->poolOffsetHead->offset + con->poolOffsetHead->block->start); + + expect(con, v == (v & PoolOffsetMask)); +#endif // not NDEBUG + + appendPoolEvent( + con, b, b->size, con->poolOffsetHead, con->poolOffsetTail); + + if (DebugPool) { + for (PoolOffset* o = con->poolOffsetHead; o; o = o->next) { + fprintf(stderr, + "include %p %d in pool event %p at offset %d in block %p\n", + o, + o->offset, + b->poolEventTail, + b->size, + b); + } + } + + con->poolOffsetHead = 0; + con->poolOffsetTail = 0; + } + } +} + +} // namespace arm +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/target/arm/fixup.h b/sgx-jvm/avian/src/codegen/target/arm/fixup.h new file mode 100644 index 0000000000..31340ed4bd --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/arm/fixup.h @@ -0,0 +1,153 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_ARM_PROMISE_H +#define AVIAN_CODEGEN_ASSEMBLER_ARM_PROMISE_H + +#include "avian/target.h" + +#include +#include +#include "avian/alloc-vector.h" + +namespace vm { +class System; +} + +namespace avian { +namespace codegen { +namespace arm { + +const bool DebugPool = false; + +const int32_t PoolOffsetMask = vm::TargetBytesPerWord == 8 ? 0x1FFFFF : 0xFFF; + +class Task { + public: + Task(Task* next) : next(next) + { + } + + virtual void run(Context* con) = 0; + + Task* next; +}; + +class OffsetPromise : public Promise { + public: + OffsetPromise(Context* con, MyBlock* block, unsigned offset, bool forTrace); + + virtual bool resolved(); + + virtual int64_t value(); + + Context* con; + MyBlock* block; + unsigned offset; + bool forTrace; +}; + +Promise* offsetPromise(Context* con, bool forTrace = false); + +class OffsetListener : public Promise::Listener { + public: + OffsetListener(vm::System* s, uint8_t* instruction); + + virtual bool resolve(int64_t value, void** location); + + vm::System* s; + uint8_t* instruction; +}; + +class OffsetTask : public Task { + public: + OffsetTask(Task* next, Promise* promise, Promise* instructionOffset); + + virtual void run(Context* con); + + Promise* promise; + Promise* instructionOffset; +}; + +void appendOffsetTask(Context* con, + Promise* promise, + Promise* instructionOffset); + +void* updateOffset(vm::System* s, uint8_t* instruction, int64_t value); + +class ConstantPoolEntry : public Promise { + public: + ConstantPoolEntry(Context* con, + Promise* constant, + ConstantPoolEntry* next, + Promise* callOffset); + + virtual int64_t value(); + + virtual bool resolved(); + + Context* con; + Promise* constant; + ConstantPoolEntry* next; + Promise* callOffset; + void* address; + unsigned constantPoolCount; +}; + +class ConstantPoolListener : public Promise::Listener { + public: + ConstantPoolListener(vm::System* s, + vm::target_uintptr_t* address, + uint8_t* returnAddress); + + virtual bool resolve(int64_t value, void** location); + + vm::System* s; + vm::target_uintptr_t* address; + uint8_t* returnAddress; +}; + +class PoolOffset { + public: + PoolOffset(MyBlock* block, ConstantPoolEntry* entry, unsigned offset); + + MyBlock* block; + ConstantPoolEntry* entry; + PoolOffset* next; + unsigned offset; +}; + +class PoolEvent { + public: + PoolEvent(PoolOffset* poolOffsetHead, + PoolOffset* poolOffsetTail, + unsigned offset); + + PoolOffset* poolOffsetHead; + PoolOffset* poolOffsetTail; + PoolEvent* next; + unsigned offset; +}; + +void appendConstantPoolEntry(Context* con, + Promise* constant, + Promise* callOffset); + +void appendPoolEvent(Context* con, + MyBlock* b, + unsigned offset, + PoolOffset* head, + PoolOffset* tail); + +} // namespace arm +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_ARM_PROMISE_H diff --git a/sgx-jvm/avian/src/codegen/target/arm/multimethod.cpp b/sgx-jvm/avian/src/codegen/target/arm/multimethod.cpp new file mode 100644 index 0000000000..94e3d6f3fb --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/arm/multimethod.cpp @@ -0,0 +1,142 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "context.h" +#include "operations.h" + +#include "multimethod.h" +#include "../multimethod.h" + +namespace avian { +namespace codegen { +namespace arm { + +using namespace util; + +unsigned index(ArchitectureContext*, + lir::BinaryOperation operation, + lir::Operand::Type operand1, + lir::Operand::Type operand2) +{ + return operation + (lir::BinaryOperationCount * (unsigned)operand1) + + (lir::BinaryOperationCount * lir::Operand::TypeCount * (unsigned)operand2); +} + +unsigned index(ArchitectureContext* con UNUSED, + lir::TernaryOperation operation, + lir::Operand::Type operand1) +{ + assertT(con, not isBranch(operation)); + + return operation + (lir::NonBranchTernaryOperationCount * (unsigned)operand1); +} + +unsigned branchIndex(ArchitectureContext* con UNUSED, + lir::Operand::Type operand1, + lir::Operand::Type operand2) +{ + return (unsigned)operand1 + (lir::Operand::TypeCount * (unsigned)operand2); +} + +void populateTables(ArchitectureContext* con) +{ + const lir::Operand::Type C = lir::Operand::Type::Constant; + const lir::Operand::Type A = lir::Operand::Type::Address; + const lir::Operand::Type R = lir::Operand::Type::RegisterPair; + const lir::Operand::Type M = lir::Operand::Type::Memory; + + OperationType* zo = con->operations; + UnaryOperationType* uo = con->unaryOperations; + BinaryOperationType* bo = con->binaryOperations; + TernaryOperationType* to = con->ternaryOperations; + BranchOperationType* bro = con->branchOperations; + + zo[lir::Return] = return_; + zo[lir::LoadBarrier] = loadBarrier; + zo[lir::StoreStoreBarrier] = storeStoreBarrier; + zo[lir::StoreLoadBarrier] = storeLoadBarrier; + zo[lir::Trap] = trap; + + uo[Multimethod::index(lir::LongCall, C)] = CAST1(longCallC); + + uo[Multimethod::index(lir::AlignedLongCall, C)] = CAST1(longCallC); + + uo[Multimethod::index(lir::LongJump, C)] = CAST1(longJumpC); + + uo[Multimethod::index(lir::AlignedLongJump, C)] = CAST1(longJumpC); + + uo[Multimethod::index(lir::Jump, R)] = CAST1(jumpR); + uo[Multimethod::index(lir::Jump, C)] = CAST1(jumpC); + + uo[Multimethod::index(lir::AlignedJump, R)] = CAST1(jumpR); + uo[Multimethod::index(lir::AlignedJump, C)] = CAST1(jumpC); + + uo[Multimethod::index(lir::Call, C)] = CAST1(callC); + uo[Multimethod::index(lir::Call, R)] = CAST1(callR); + + uo[Multimethod::index(lir::AlignedCall, C)] = CAST1(callC); + uo[Multimethod::index(lir::AlignedCall, R)] = CAST1(callR); + + bo[index(con, lir::Move, R, R)] = CAST2(moveRR); + bo[index(con, lir::Move, C, R)] = CAST2(moveCR); + bo[index(con, lir::Move, C, M)] = CAST2(moveCM); + bo[index(con, lir::Move, M, R)] = CAST2(moveMR); + bo[index(con, lir::Move, R, M)] = CAST2(moveRM); + bo[index(con, lir::Move, A, R)] = CAST2(moveAR); + + bo[index(con, lir::MoveZ, R, R)] = CAST2(moveZRR); + bo[index(con, lir::MoveZ, M, R)] = CAST2(moveZMR); + bo[index(con, lir::MoveZ, C, R)] = CAST2(moveCR); + + bo[index(con, lir::Negate, R, R)] = CAST2(negateRR); + + bo[index(con, lir::FloatAbsolute, R, R)] = CAST2(floatAbsoluteRR); + bo[index(con, lir::FloatNegate, R, R)] = CAST2(floatNegateRR); + bo[index(con, lir::Float2Float, R, R)] = CAST2(float2FloatRR); + bo[index(con, lir::Float2Int, R, R)] = CAST2(float2IntRR); + bo[index(con, lir::Int2Float, R, R)] = CAST2(int2FloatRR); + bo[index(con, lir::FloatSquareRoot, R, R)] = CAST2(floatSqrtRR); + + to[index(con, lir::Add, R)] = CAST3(addR); + + to[index(con, lir::Subtract, R)] = CAST3(subR); + + to[index(con, lir::Multiply, R)] = CAST3(multiplyR); + + to[index(con, lir::FloatAdd, R)] = CAST3(floatAddR); + to[index(con, lir::FloatSubtract, R)] = CAST3(floatSubtractR); + to[index(con, lir::FloatMultiply, R)] = CAST3(floatMultiplyR); + to[index(con, lir::FloatDivide, R)] = CAST3(floatDivideR); + + to[index(con, lir::ShiftLeft, R)] = CAST3(shiftLeftR); + to[index(con, lir::ShiftLeft, C)] = CAST3(shiftLeftC); + + to[index(con, lir::ShiftRight, R)] = CAST3(shiftRightR); + to[index(con, lir::ShiftRight, C)] = CAST3(shiftRightC); + + to[index(con, lir::UnsignedShiftRight, R)] = CAST3(unsignedShiftRightR); + to[index(con, lir::UnsignedShiftRight, C)] = CAST3(unsignedShiftRightC); + + to[index(con, lir::And, R)] = CAST3(andR); + to[index(con, lir::And, C)] = CAST3(andC); + + to[index(con, lir::Or, R)] = CAST3(orR); + + to[index(con, lir::Xor, R)] = CAST3(xorR); + + bro[branchIndex(con, R, R)] = CAST_BRANCH(branchRR); + bro[branchIndex(con, C, R)] = CAST_BRANCH(branchCR); + bro[branchIndex(con, C, M)] = CAST_BRANCH(branchCM); + bro[branchIndex(con, R, M)] = CAST_BRANCH(branchRM); +} + +} // namespace arm +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/target/arm/multimethod.h b/sgx-jvm/avian/src/codegen/target/arm/multimethod.h new file mode 100644 index 0000000000..2459b8bef6 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/arm/multimethod.h @@ -0,0 +1,45 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_ARM_MULTIMETHOD_H +#define AVIAN_CODEGEN_ASSEMBLER_ARM_MULTIMETHOD_H + +#include +#include + +#define CAST1(x) reinterpret_cast(x) +#define CAST2(x) reinterpret_cast(x) +#define CAST3(x) reinterpret_cast(x) +#define CAST_BRANCH(x) reinterpret_cast(x) + +namespace avian { +namespace codegen { +namespace arm { + +unsigned index(ArchitectureContext*, + lir::BinaryOperation operation, + lir::Operand::Type operand1, + lir::Operand::Type operand2); + +unsigned index(ArchitectureContext* con UNUSED, + lir::TernaryOperation operation, + lir::Operand::Type operand1); + +unsigned branchIndex(ArchitectureContext* con UNUSED, + lir::Operand::Type operand1, + lir::Operand::Type operand2); + +void populateTables(ArchitectureContext* con); + +} // namespace arm +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_ARM_MULTIMETHOD_H diff --git a/sgx-jvm/avian/src/codegen/target/arm/operations.h b/sgx-jvm/avian/src/codegen/target/arm/operations.h new file mode 100644 index 0000000000..08603bcd73 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/arm/operations.h @@ -0,0 +1,434 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_ARM_OPERATIONS_H +#define AVIAN_CODEGEN_ASSEMBLER_ARM_OPERATIONS_H + +#include "registers.h" + +namespace vm { +class System; +} + +namespace avian { +namespace codegen { +namespace arm { + +class Context; + +// shortcut functions + +inline Register newTemp(Context* con) +{ + return con->client->acquireTemporary(GPR_MASK); +} + +inline Register newTemp(Context* con, RegisterMask mask) +{ + return con->client->acquireTemporary(mask); +} + +inline void freeTemp(Context* con, Register r) +{ + con->client->releaseTemporary(r); +} + +inline int64_t getValue(lir::Constant* con) +{ + return con->value->value(); +} + +inline lir::RegisterPair makeTemp(Context* con) +{ + lir::RegisterPair tmp(newTemp(con)); + return tmp; +} + +inline lir::RegisterPair makeTemp64(Context* con) +{ + lir::RegisterPair tmp(newTemp(con), newTemp(con)); + return tmp; +} + +inline void freeTemp(Context* con, const lir::RegisterPair& tmp) +{ + if (tmp.low != NoRegister) + freeTemp(con, tmp.low); + if (tmp.high != NoRegister) + freeTemp(con, tmp.high); +} + +void shiftLeftR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* t); + +void moveRR(Context* con, + unsigned srcSize, + lir::RegisterPair* src, + unsigned dstSize, + lir::RegisterPair* dst); + +void shiftLeftC(Context* con, + unsigned size UNUSED, + lir::Constant* a, + lir::RegisterPair* b, + lir::RegisterPair* t); + +void shiftRightR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* t); + +void shiftRightC(Context* con, + unsigned size UNUSED, + lir::Constant* a, + lir::RegisterPair* b, + lir::RegisterPair* t); + +void unsignedShiftRightR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* t); + +void unsignedShiftRightC(Context* con, + unsigned size UNUSED, + lir::Constant* a, + lir::RegisterPair* b, + lir::RegisterPair* t); + +bool needJump(MyBlock* b); + +unsigned padding(MyBlock* b, unsigned offset); + +void resolve(MyBlock* b); + +void jumpR(Context* con, unsigned size UNUSED, lir::RegisterPair* target); + +void swapRR(Context* con, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize, + lir::RegisterPair* b); + +void moveRR(Context* con, + unsigned srcSize, + lir::RegisterPair* src, + unsigned dstSize, + lir::RegisterPair* dst); + +void moveZRR(Context* con, + unsigned srcSize, + lir::RegisterPair* src, + unsigned, + lir::RegisterPair* dst); + +void moveCR(Context* con, + unsigned size, + lir::Constant* src, + unsigned, + lir::RegisterPair* dst); + +void moveCR2(Context* con, + unsigned size, + lir::Constant* src, + lir::RegisterPair* dst, + Promise* callOffset); + +void moveCR(Context* con, + unsigned size, + lir::Constant* src, + unsigned, + lir::RegisterPair* dst); + +void addR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* t); + +void subR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* t); + +void addC(Context* con, + unsigned size, + lir::Constant* a, + lir::RegisterPair* b, + lir::RegisterPair* dst); + +void subC(Context* con, + unsigned size, + lir::Constant* a, + lir::RegisterPair* b, + lir::RegisterPair* dst); + +void multiplyR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* t); + +void floatAbsoluteRR(Context* con, + unsigned size, + lir::RegisterPair* a, + unsigned, + lir::RegisterPair* b); + +void floatNegateRR(Context* con, + unsigned size, + lir::RegisterPair* a, + unsigned, + lir::RegisterPair* b); + +void float2FloatRR(Context* con, + unsigned size, + lir::RegisterPair* a, + unsigned, + lir::RegisterPair* b); + +void float2IntRR(Context* con, + unsigned size, + lir::RegisterPair* a, + unsigned, + lir::RegisterPair* b); + +void int2FloatRR(Context* con, + unsigned, + lir::RegisterPair* a, + unsigned size, + lir::RegisterPair* b); + +void floatSqrtRR(Context* con, + unsigned size, + lir::RegisterPair* a, + unsigned, + lir::RegisterPair* b); + +void floatAddR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* t); + +void floatSubtractR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* t); + +void floatMultiplyR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* t); + +void floatDivideR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* t); + +int normalize(Context* con, + int offset, + int index, + unsigned scale, + bool* preserveIndex, + bool* release); + +void store(Context* con, + unsigned size, + lir::RegisterPair* src, + int base, + int offset, + int index, + unsigned scale, + bool preserveIndex); + +void moveRM(Context* con, + unsigned srcSize, + lir::RegisterPair* src, + unsigned dstSize UNUSED, + lir::Memory* dst); + +void load(Context* con, + unsigned srcSize, + int base, + int offset, + int index, + unsigned scale, + unsigned dstSize, + lir::RegisterPair* dst, + bool preserveIndex, + bool signExtend); + +void moveMR(Context* con, + unsigned srcSize, + lir::Memory* src, + unsigned dstSize, + lir::RegisterPair* dst); + +void moveZMR(Context* con, + unsigned srcSize, + lir::Memory* src, + unsigned dstSize, + lir::RegisterPair* dst); + +void andR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* dst); + +void andC(Context* con, + unsigned size, + lir::Constant* a, + lir::RegisterPair* b, + lir::RegisterPair* dst); + +void orR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* dst); + +void xorR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* dst); + +void moveAR2(Context* con, + unsigned srcSize, + lir::Address* src, + unsigned dstSize, + lir::RegisterPair* dst); + +void moveAR(Context* con, + unsigned srcSize, + lir::Address* src, + unsigned dstSize, + lir::RegisterPair* dst); + +void compareRR(Context* con, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void compareCR(Context* con, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b); + +void compareCM(Context* con, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::Memory* b); + +void compareRM(Context* con, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize, + lir::Memory* b); + +int32_t branch(Context* con, lir::TernaryOperation op); + +void conditional(Context* con, int32_t branch, lir::Constant* target); + +void branch(Context* con, lir::TernaryOperation op, lir::Constant* target); + +void branchLong(Context* con, + lir::TernaryOperation op, + lir::Operand* al, + lir::Operand* ah, + lir::Operand* bl, + lir::Operand* bh, + lir::Constant* target, + BinaryOperationType compareSigned, + BinaryOperationType compareUnsigned); + +void branchRR(Context* con, + lir::TernaryOperation op, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::Constant* target); + +void branchCR(Context* con, + lir::TernaryOperation op, + unsigned size, + lir::Constant* a, + lir::RegisterPair* b, + lir::Constant* target); + +void branchRM(Context* con, + lir::TernaryOperation op, + unsigned size, + lir::RegisterPair* a, + lir::Memory* b, + lir::Constant* target); + +void branchCM(Context* con, + lir::TernaryOperation op, + unsigned size, + lir::Constant* a, + lir::Memory* b, + lir::Constant* target); + +ShiftMaskPromise* shiftMaskPromise(Context* con, + Promise* base, + unsigned shift, + int64_t mask); + +void moveCM(Context* con, + unsigned srcSize, + lir::Constant* src, + unsigned dstSize, + lir::Memory* dst); + +void negateRR(Context* con, + unsigned srcSize, + lir::RegisterPair* src, + unsigned dstSize UNUSED, + lir::RegisterPair* dst); + +void callR(Context* con, unsigned size UNUSED, lir::RegisterPair* target); + +void callC(Context* con, unsigned size UNUSED, lir::Constant* target); + +void longCallC(Context* con, unsigned size UNUSED, lir::Constant* target); + +void longJumpC(Context* con, unsigned size UNUSED, lir::Constant* target); + +void jumpC(Context* con, unsigned size UNUSED, lir::Constant* target); + +void return_(Context* con); + +void trap(Context* con); + +void loadBarrier(Context*); + +void storeStoreBarrier(Context*); + +void storeLoadBarrier(Context*); + +} // namespace arm +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_ARM_OPERATIONS_H diff --git a/sgx-jvm/avian/src/codegen/target/arm/operations32.cpp b/sgx-jvm/avian/src/codegen/target/arm/operations32.cpp new file mode 100644 index 0000000000..5572dcae87 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/arm/operations32.cpp @@ -0,0 +1,1459 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "context.h" +#include "operations.h" +#include "encode.h" +#include "block.h" +#include "fixup.h" +#include "multimethod.h" + +#if TARGET_BYTES_PER_WORD == 4 + +namespace avian { +namespace codegen { +namespace arm { + +using namespace isa; +using namespace avian::util; + +inline bool isOfWidth(int64_t i, int size) +{ + return static_cast(i) >> size == 0; +} + +inline unsigned lo8(int64_t i) +{ + return (unsigned)(i & MASK_LO8); +} + +void andC(Context* con, + unsigned size, + lir::Constant* a, + lir::RegisterPair* b, + lir::RegisterPair* dst); + +void shiftLeftR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* t) +{ + if (size == 8) { + Register tmp1 = newTemp(con), tmp2 = newTemp(con), tmp3 = newTemp(con); + ResolvedPromise maskPromise(0x3F); + lir::Constant mask(&maskPromise); + lir::RegisterPair dst(tmp3); + andC(con, 4, &mask, a, &dst); + emit(con, lsl(tmp1, b->high, tmp3)); + emit(con, rsbi(tmp2, tmp3, 32)); + emit(con, orrsh(tmp1, tmp1, b->low, tmp2, LSR)); + emit(con, SETS(subi(t->high, tmp3, 32))); + emit(con, SETCOND(mov(t->high, tmp1), MI)); + emit(con, SETCOND(lsl(t->high, b->low, t->high), PL)); + emit(con, lsl(t->low, b->low, tmp3)); + freeTemp(con, tmp1); + freeTemp(con, tmp2); + freeTemp(con, tmp3); + } else { + Register tmp = newTemp(con); + ResolvedPromise maskPromise(0x1F); + lir::Constant mask(&maskPromise); + lir::RegisterPair dst(tmp); + andC(con, size, &mask, a, &dst); + emit(con, lsl(t->low, b->low, tmp)); + freeTemp(con, tmp); + } +} + +void moveRR(Context* con, + unsigned srcSize, + lir::RegisterPair* src, + unsigned dstSize, + lir::RegisterPair* dst); + +void shiftLeftC(Context* con, + unsigned size UNUSED, + lir::Constant* a, + lir::RegisterPair* b, + lir::RegisterPair* t) +{ + assertT(con, size == vm::TargetBytesPerWord); + if (getValue(a) & 0x1F) { + emit(con, lsli(t->low, b->low, getValue(a) & 0x1F)); + } else { + moveRR(con, size, b, size, t); + } +} + +void shiftRightR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* t) +{ + if (size == 8) { + Register tmp1 = newTemp(con), tmp2 = newTemp(con), tmp3 = newTemp(con); + ResolvedPromise maskPromise(0x3F); + lir::Constant mask(&maskPromise); + lir::RegisterPair dst(tmp3); + andC(con, 4, &mask, a, &dst); + emit(con, lsr(tmp1, b->low, tmp3)); + emit(con, rsbi(tmp2, tmp3, 32)); + emit(con, orrsh(tmp1, tmp1, b->high, tmp2, LSL)); + emit(con, SETS(subi(t->low, tmp3, 32))); + emit(con, SETCOND(mov(t->low, tmp1), MI)); + emit(con, SETCOND(asr(t->low, b->high, t->low), PL)); + emit(con, asr(t->high, b->high, tmp3)); + freeTemp(con, tmp1); + freeTemp(con, tmp2); + freeTemp(con, tmp3); + } else { + Register tmp = newTemp(con); + ResolvedPromise maskPromise(0x1F); + lir::Constant mask(&maskPromise); + lir::RegisterPair dst(tmp); + andC(con, size, &mask, a, &dst); + emit(con, asr(t->low, b->low, tmp)); + freeTemp(con, tmp); + } +} + +void shiftRightC(Context* con, + unsigned size UNUSED, + lir::Constant* a, + lir::RegisterPair* b, + lir::RegisterPair* t) +{ + assertT(con, size == vm::TargetBytesPerWord); + if (getValue(a) & 0x1F) { + emit(con, asri(t->low, b->low, getValue(a) & 0x1F)); + } else { + moveRR(con, size, b, size, t); + } +} + +void unsignedShiftRightR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* t) +{ + Register tmpShift = newTemp(con); + ResolvedPromise maskPromise(size == 8 ? 0x3F : 0x1F); + lir::Constant mask(&maskPromise); + lir::RegisterPair dst(tmpShift); + andC(con, 4, &mask, a, &dst); + emit(con, lsr(t->low, b->low, tmpShift)); + if (size == 8) { + Register tmpHi = newTemp(con), tmpLo = newTemp(con); + emit(con, SETS(rsbi(tmpHi, tmpShift, 32))); + emit(con, lsl(tmpLo, b->high, tmpHi)); + emit(con, orr(t->low, t->low, tmpLo)); + emit(con, addi(tmpHi, tmpShift, -32)); + emit(con, lsr(tmpLo, b->high, tmpHi)); + emit(con, orr(t->low, t->low, tmpLo)); + emit(con, lsr(t->high, b->high, tmpShift)); + freeTemp(con, tmpHi); + freeTemp(con, tmpLo); + } + freeTemp(con, tmpShift); +} + +void unsignedShiftRightC(Context* con, + unsigned size UNUSED, + lir::Constant* a, + lir::RegisterPair* b, + lir::RegisterPair* t) +{ + assertT(con, size == vm::TargetBytesPerWord); + if (getValue(a) & 0x1F) { + emit(con, lsri(t->low, b->low, getValue(a) & 0x1F)); + } else { + moveRR(con, size, b, size, t); + } +} + +void jumpR(Context* con, unsigned size UNUSED, lir::RegisterPair* target) +{ + assertT(con, size == vm::TargetBytesPerWord); + emit(con, bx(target->low)); +} + +void swapRR(Context* con, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize, + lir::RegisterPair* b) +{ + assertT(con, aSize == vm::TargetBytesPerWord); + assertT(con, bSize == vm::TargetBytesPerWord); + + lir::RegisterPair tmp(con->client->acquireTemporary(GPR_MASK)); + moveRR(con, aSize, a, bSize, &tmp); + moveRR(con, bSize, b, aSize, a); + moveRR(con, bSize, &tmp, bSize, b); + con->client->releaseTemporary(tmp.low); +} + +void moveRR(Context* con, + unsigned srcSize, + lir::RegisterPair* src, + unsigned dstSize, + lir::RegisterPair* dst) +{ + bool srcIsFpr = isFpr(src); + bool dstIsFpr = isFpr(dst); + if (srcIsFpr || dstIsFpr) { // FPR(s) involved + assertT(con, srcSize == dstSize); + const bool dprec = srcSize == 8; + if (srcIsFpr && dstIsFpr) { // FPR to FPR + if (dprec) + emit(con, fcpyd(fpr64(dst), fpr64(src))); // double + else + emit(con, fcpys(fpr32(dst), fpr32(src))); // single + } else if (srcIsFpr) { // FPR to GPR + if (dprec) + emit(con, fmrrd(dst->low, dst->high, fpr64(src))); + else + emit(con, fmrs(dst->low, fpr32(src))); + } else { // GPR to FPR + if (dprec) + emit(con, fmdrr(fpr64(dst->low), src->low, src->high)); + else + emit(con, fmsr(fpr32(dst), src->low)); + } + return; + } + + switch (srcSize) { + case 1: + emit(con, lsli(dst->low, src->low, 24)); + emit(con, asri(dst->low, dst->low, 24)); + break; + + case 2: + emit(con, lsli(dst->low, src->low, 16)); + emit(con, asri(dst->low, dst->low, 16)); + break; + + case 4: + case 8: + if (srcSize == 4 and dstSize == 8) { + moveRR(con, 4, src, 4, dst); + emit(con, asri(dst->high, src->low, 31)); + } else if (srcSize == 8 and dstSize == 8) { + lir::RegisterPair srcHigh(src->high); + lir::RegisterPair dstHigh(dst->high); + + if (src->high == dst->low) { + if (src->low == dst->high) { + swapRR(con, 4, src, 4, dst); + } else { + moveRR(con, 4, &srcHigh, 4, &dstHigh); + moveRR(con, 4, src, 4, dst); + } + } else { + moveRR(con, 4, src, 4, dst); + moveRR(con, 4, &srcHigh, 4, &dstHigh); + } + } else if (src->low != dst->low) { + emit(con, mov(dst->low, src->low)); + } + break; + + default: + abort(con); + } +} + +void moveZRR(Context* con, + unsigned srcSize, + lir::RegisterPair* src, + unsigned, + lir::RegisterPair* dst) +{ + switch (srcSize) { + case 2: + emit(con, lsli(dst->low, src->low, 16)); + emit(con, lsri(dst->low, dst->low, 16)); + break; + + default: + abort(con); + } +} + +void moveCR(Context* con, + unsigned size, + lir::Constant* src, + unsigned, + lir::RegisterPair* dst); + +void moveCR2(Context* con, + unsigned size, + lir::Constant* src, + lir::RegisterPair* dst, + Promise* callOffset) +{ + if (isFpr(dst)) { // floating-point + lir::RegisterPair tmp = size > 4 ? makeTemp64(con) : makeTemp(con); + moveCR(con, size, src, size, &tmp); + moveRR(con, size, &tmp, size, dst); + freeTemp(con, tmp); + } else if (size > 4) { + uint64_t value = (uint64_t)src->value->value(); + ResolvedPromise loBits(value & MASK_LO32); + lir::Constant srcLo(&loBits); + ResolvedPromise hiBits(value >> 32); + lir::Constant srcHi(&hiBits); + lir::RegisterPair dstHi(dst->high); + moveCR(con, 4, &srcLo, 4, dst); + moveCR(con, 4, &srcHi, 4, &dstHi); + } else if (callOffset == 0 and src->value->resolved() + and isOfWidth(getValue(src), 8)) { + emit(con, movi(dst->low, lo8(getValue(src)))); // fits in immediate + } else { + appendConstantPoolEntry(con, src->value, callOffset); + emit(con, ldri(dst->low, ProgramCounter, 0)); // load 32 bits + } +} + +void moveCR(Context* con, + unsigned size, + lir::Constant* src, + unsigned, + lir::RegisterPair* dst) +{ + moveCR2(con, size, src, dst, 0); +} + +void addR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* t) +{ + if (size == 8) { + emit(con, SETS(add(t->low, a->low, b->low))); + emit(con, adc(t->high, a->high, b->high)); + } else { + emit(con, add(t->low, a->low, b->low)); + } +} + +void subR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* t) +{ + if (size == 8) { + emit(con, SETS(rsb(t->low, a->low, b->low))); + emit(con, rsc(t->high, a->high, b->high)); + } else { + emit(con, rsb(t->low, a->low, b->low)); + } +} + +void addC(Context* con, + unsigned size, + lir::Constant* a, + lir::RegisterPair* b, + lir::RegisterPair* dst) +{ + assertT(con, size == vm::TargetBytesPerWord); + + int32_t v = a->value->value(); + if (v) { + if (v > 0 and v < 256) { + emit(con, addi(dst->low, b->low, v)); + } else if (v > 0 and v < 1024 and v % 4 == 0) { + emit(con, addi(dst->low, b->low, v >> 2, 15)); + } else { + // todo + abort(con); + } + } else { + moveRR(con, size, b, size, dst); + } +} + +void subC(Context* con, + unsigned size, + lir::Constant* a, + lir::RegisterPair* b, + lir::RegisterPair* dst) +{ + assertT(con, size == vm::TargetBytesPerWord); + + int32_t v = a->value->value(); + if (v) { + if (v > 0 and v < 256) { + emit(con, subi(dst->low, b->low, v)); + } else if (v > 0 and v < 1024 and v % 4 == 0) { + emit(con, subi(dst->low, b->low, v >> 2, 15)); + } else { + // todo + abort(con); + } + } else { + moveRR(con, size, b, size, dst); + } +} + +void multiplyR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* t) +{ + if (size == 8) { + bool useTemporaries = b->low == t->low; + Register tmpLow = useTemporaries ? con->client->acquireTemporary(GPR_MASK) + : t->low; + Register tmpHigh = useTemporaries ? con->client->acquireTemporary(GPR_MASK) + : t->high; + + emit(con, umull(tmpLow, tmpHigh, a->low, b->low)); + emit(con, mla(tmpHigh, a->low, b->high, tmpHigh)); + emit(con, mla(tmpHigh, a->high, b->low, tmpHigh)); + + if (useTemporaries) { + emit(con, mov(t->low, tmpLow)); + emit(con, mov(t->high, tmpHigh)); + con->client->releaseTemporary(tmpLow); + con->client->releaseTemporary(tmpHigh); + } + } else { + emit(con, mul(t->low, a->low, b->low)); + } +} + +void floatAbsoluteRR(Context* con, + unsigned size, + lir::RegisterPair* a, + unsigned, + lir::RegisterPair* b) +{ + if (size == 8) { + emit(con, fabsd(fpr64(b), fpr64(a))); + } else { + emit(con, fabss(fpr32(b), fpr32(a))); + } +} + +void floatNegateRR(Context* con, + unsigned size, + lir::RegisterPair* a, + unsigned, + lir::RegisterPair* b) +{ + if (size == 8) { + emit(con, fnegd(fpr64(b), fpr64(a))); + } else { + emit(con, fnegs(fpr32(b), fpr32(a))); + } +} + +void float2FloatRR(Context* con, + unsigned size, + lir::RegisterPair* a, + unsigned, + lir::RegisterPair* b) +{ + if (size == 8) { + emit(con, fcvtsd(fpr32(b), fpr64(a))); + } else { + emit(con, fcvtds(fpr64(b), fpr32(a))); + } +} + +void float2IntRR(Context* con, + unsigned size, + lir::RegisterPair* a, + unsigned, + lir::RegisterPair* b) +{ + Register tmp = newTemp(con, FPR_MASK); + int ftmp = fpr32(tmp); + if (size == 8) { // double to int + emit(con, ftosizd(ftmp, fpr64(a))); + } else { // float to int + emit(con, ftosizs(ftmp, fpr32(a))); + } // else thunked + emit(con, fmrs(b->low, ftmp)); + freeTemp(con, tmp); +} + +void int2FloatRR(Context* con, + unsigned, + lir::RegisterPair* a, + unsigned size, + lir::RegisterPair* b) +{ + emit(con, fmsr(fpr32(b), a->low)); + if (size == 8) { // int to double + emit(con, fsitod(fpr64(b), fpr32(b))); + } else { // int to float + emit(con, fsitos(fpr32(b), fpr32(b))); + } // else thunked +} + +void floatSqrtRR(Context* con, + unsigned size, + lir::RegisterPair* a, + unsigned, + lir::RegisterPair* b) +{ + if (size == 8) { + emit(con, fsqrtd(fpr64(b), fpr64(a))); + } else { + emit(con, fsqrts(fpr32(b), fpr32(a))); + } +} + +void floatAddR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* t) +{ + if (size == 8) { + emit(con, faddd(fpr64(t), fpr64(a), fpr64(b))); + } else { + emit(con, fadds(fpr32(t), fpr32(a), fpr32(b))); + } +} + +void floatSubtractR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* t) +{ + if (size == 8) { + emit(con, fsubd(fpr64(t), fpr64(b), fpr64(a))); + } else { + emit(con, fsubs(fpr32(t), fpr32(b), fpr32(a))); + } +} + +void floatMultiplyR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* t) +{ + if (size == 8) { + emit(con, fmuld(fpr64(t), fpr64(a), fpr64(b))); + } else { + emit(con, fmuls(fpr32(t), fpr32(a), fpr32(b))); + } +} + +void floatDivideR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* t) +{ + if (size == 8) { + emit(con, fdivd(fpr64(t), fpr64(b), fpr64(a))); + } else { + emit(con, fdivs(fpr32(t), fpr32(b), fpr32(a))); + } +} + +Register normalize(Context* con, + int offset, + Register index, + unsigned scale, + bool* preserveIndex, + bool* release) +{ + if (offset != 0 or scale != 1) { + lir::RegisterPair normalizedIndex( + *preserveIndex ? con->client->acquireTemporary(GPR_MASK) : index); + + if (*preserveIndex) { + *release = true; + *preserveIndex = false; + } else { + *release = false; + } + + Register scaled; + + if (scale != 1) { + lir::RegisterPair unscaledIndex(index); + + ResolvedPromise scalePromise(log(scale)); + lir::Constant scaleConstant(&scalePromise); + + shiftLeftC(con, + vm::TargetBytesPerWord, + &scaleConstant, + &unscaledIndex, + &normalizedIndex); + + scaled = normalizedIndex.low; + } else { + scaled = index; + } + + if (offset != 0) { + lir::RegisterPair untranslatedIndex(scaled); + + ResolvedPromise offsetPromise(offset); + lir::Constant offsetConstant(&offsetPromise); + + lir::RegisterPair tmp(con->client->acquireTemporary(GPR_MASK)); + moveCR(con, + vm::TargetBytesPerWord, + &offsetConstant, + vm::TargetBytesPerWord, + &tmp); + addR(con, + vm::TargetBytesPerWord, + &tmp, + &untranslatedIndex, + &normalizedIndex); + con->client->releaseTemporary(tmp.low); + } + + return normalizedIndex.low; + } else { + *release = false; + return index; + } +} + +void store(Context* con, + unsigned size, + lir::RegisterPair* src, + Register base, + int offset, + Register index, + unsigned scale, + bool preserveIndex) +{ + if (index != NoRegister) { + bool release; + Register normalized + = normalize(con, offset, index, scale, &preserveIndex, &release); + + if (!isFpr(src)) { // GPR store + switch (size) { + case 1: + emit(con, strb(src->low, base, normalized)); + break; + + case 2: + emit(con, strh(src->low, base, normalized)); + break; + + case 4: + emit(con, str(src->low, base, normalized)); + break; + + case 8: { // split into 2 32-bit stores + lir::RegisterPair srcHigh(src->high); + store(con, 4, &srcHigh, base, 0, normalized, 1, preserveIndex); + store(con, 4, src, base, 4, normalized, 1, preserveIndex); + } break; + + default: + abort(con); + } + } else { // FPR store + lir::RegisterPair base_(base), normalized_(normalized), + absAddr = makeTemp(con); + // FPR stores have only bases, so we must add the index + addR(con, vm::TargetBytesPerWord, &base_, &normalized_, &absAddr); + // double-precision + if (size == 8) + emit(con, fstd(fpr64(src), absAddr.low)); + // single-precision + else + emit(con, fsts(fpr32(src), absAddr.low)); + freeTemp(con, absAddr); + } + + if (release) + con->client->releaseTemporary(normalized); + } else if (size == 8 or abs(offset) == (abs(offset) & 0xFF) + or (size != 2 and abs(offset) == (abs(offset) & 0xFFF))) { + if (!isFpr(src)) { // GPR store + switch (size) { + case 1: + emit(con, strbi(src->low, base, offset)); + break; + + case 2: + emit(con, strhi(src->low, base, offset)); + break; + + case 4: + emit(con, stri(src->low, base, offset)); + break; + + case 8: { // split into 2 32-bit stores + lir::RegisterPair srcHigh(src->high); + store(con, 4, &srcHigh, base, offset, NoRegister, 1, false); + store(con, 4, src, base, offset + 4, NoRegister, 1, false); + } break; + + default: + abort(con); + } + } else { // FPR store + // double-precision + if (size == 8) + emit(con, fstd(fpr64(src), base, offset)); + // single-precision + else + emit(con, fsts(fpr32(src), base, offset)); + } + } else { + lir::RegisterPair tmp(con->client->acquireTemporary(GPR_MASK)); + ResolvedPromise offsetPromise(offset); + lir::Constant offsetConstant(&offsetPromise); + moveCR(con, + vm::TargetBytesPerWord, + &offsetConstant, + vm::TargetBytesPerWord, + &tmp); + + store(con, size, src, base, 0, tmp.low, 1, false); + + con->client->releaseTemporary(tmp.low); + } +} + +void moveRM(Context* con, + unsigned srcSize, + lir::RegisterPair* src, + unsigned dstSize UNUSED, + lir::Memory* dst) +{ + assertT(con, srcSize == dstSize); + + store( + con, srcSize, src, dst->base, dst->offset, dst->index, dst->scale, true); +} + +void load(Context* con, + unsigned srcSize, + Register base, + int offset, + Register index, + unsigned scale, + unsigned dstSize, + lir::RegisterPair* dst, + bool preserveIndex, + bool signExtend) +{ + if (index != NoRegister) { + bool release; + Register normalized + = normalize(con, offset, index, scale, &preserveIndex, &release); + + if (!isFpr(dst)) { // GPR load + switch (srcSize) { + case 1: + if (signExtend) { + emit(con, ldrsb(dst->low, base, normalized)); + } else { + emit(con, ldrb(dst->low, base, normalized)); + } + break; + + case 2: + if (signExtend) { + emit(con, ldrsh(dst->low, base, normalized)); + } else { + emit(con, ldrh(dst->low, base, normalized)); + } + break; + + case 4: + case 8: { + if (srcSize == 4 and dstSize == 8) { + load(con, 4, base, 0, normalized, 1, 4, dst, preserveIndex, false); + moveRR(con, 4, dst, 8, dst); + } else if (srcSize == 8 and dstSize == 8) { + lir::RegisterPair dstHigh(dst->high); + load(con, + 4, + base, + 0, + normalized, + 1, + 4, + &dstHigh, + preserveIndex, + false); + load(con, 4, base, 4, normalized, 1, 4, dst, preserveIndex, false); + } else { + emit(con, ldr(dst->low, base, normalized)); + } + } break; + + default: + abort(con); + } + } else { // FPR load + lir::RegisterPair base_(base), normalized_(normalized), + absAddr = makeTemp(con); + // VFP loads only have bases, so we must add the index + addR(con, vm::TargetBytesPerWord, &base_, &normalized_, &absAddr); + // double-precision + if (srcSize == 8) + emit(con, fldd(fpr64(dst), absAddr.low)); + // single-precision + else + emit(con, flds(fpr32(dst), absAddr.low)); + freeTemp(con, absAddr); + } + + if (release) + con->client->releaseTemporary(normalized); + } else if ((srcSize == 8 and dstSize == 8) + or abs(offset) == (abs(offset) & 0xFF) + or (srcSize != 2 and (srcSize != 1 or not signExtend) + and abs(offset) == (abs(offset) & 0xFFF))) { + if (!isFpr(dst)) { // GPR load + switch (srcSize) { + case 1: + if (signExtend) { + emit(con, ldrsbi(dst->low, base, offset)); + } else { + emit(con, ldrbi(dst->low, base, offset)); + } + break; + + case 2: + if (signExtend) { + emit(con, ldrshi(dst->low, base, offset)); + } else { + emit(con, ldrhi(dst->low, base, offset)); + } + break; + + case 4: + emit(con, ldri(dst->low, base, offset)); + break; + + case 8: { + if (dstSize == 8) { + lir::RegisterPair dstHigh(dst->high); + load(con, 4, base, offset, NoRegister, 1, 4, &dstHigh, false, false); + load(con, 4, base, offset + 4, NoRegister, 1, 4, dst, false, false); + } else { + emit(con, ldri(dst->low, base, offset)); + } + } break; + + default: + abort(con); + } + } else { // FPR load + // double-precision + if (srcSize == 8) + emit(con, fldd(fpr64(dst), base, offset)); + // single-precision + else + emit(con, flds(fpr32(dst), base, offset)); + } + } else { + lir::RegisterPair tmp(con->client->acquireTemporary(GPR_MASK)); + ResolvedPromise offsetPromise(offset); + lir::Constant offsetConstant(&offsetPromise); + moveCR(con, + vm::TargetBytesPerWord, + &offsetConstant, + vm::TargetBytesPerWord, + &tmp); + + load(con, srcSize, base, 0, tmp.low, 1, dstSize, dst, false, signExtend); + + con->client->releaseTemporary(tmp.low); + } +} + +void moveMR(Context* con, + unsigned srcSize, + lir::Memory* src, + unsigned dstSize, + lir::RegisterPair* dst) +{ + load(con, + srcSize, + src->base, + src->offset, + src->index, + src->scale, + dstSize, + dst, + true, + true); +} + +void moveZMR(Context* con, + unsigned srcSize, + lir::Memory* src, + unsigned dstSize, + lir::RegisterPair* dst) +{ + load(con, + srcSize, + src->base, + src->offset, + src->index, + src->scale, + dstSize, + dst, + true, + false); +} + +void andR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* dst) +{ + if (size == 8) + emit(con, and_(dst->high, a->high, b->high)); + emit(con, and_(dst->low, a->low, b->low)); +} + +void andC(Context* con, + unsigned size, + lir::Constant* a, + lir::RegisterPair* b, + lir::RegisterPair* dst) +{ + int64_t v = a->value->value(); + + if (size == 8) { + ResolvedPromise high((v >> 32) & 0xFFFFFFFF); + lir::Constant ah(&high); + + ResolvedPromise low(v & 0xFFFFFFFF); + lir::Constant al(&low); + + lir::RegisterPair bh(b->high); + lir::RegisterPair dh(dst->high); + + andC(con, 4, &al, b, dst); + andC(con, 4, &ah, &bh, &dh); + } else { + uint32_t v32 = static_cast(v); + if (v32 != 0xFFFFFFFF) { + if ((v32 & 0xFFFFFF00) == 0xFFFFFF00) { + emit(con, bici(dst->low, b->low, (~(v32 & 0xFF)) & 0xFF)); + } else if ((v32 & 0xFFFFFF00) == 0) { + emit(con, andi(dst->low, b->low, v32 & 0xFF)); + } else { + // todo: there are other cases we can handle in one + // instruction + + bool useTemporary = b->low == dst->low; + lir::RegisterPair tmp(dst->low); + if (useTemporary) { + tmp.low = con->client->acquireTemporary(GPR_MASK); + } + + moveCR(con, 4, a, 4, &tmp); + andR(con, 4, b, &tmp, dst); + + if (useTemporary) { + con->client->releaseTemporary(tmp.low); + } + } + } else { + moveRR(con, size, b, size, dst); + } + } +} + +void orR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* dst) +{ + if (size == 8) + emit(con, orr(dst->high, a->high, b->high)); + emit(con, orr(dst->low, a->low, b->low)); +} + +void xorR(Context* con, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* dst) +{ + if (size == 8) + emit(con, eor(dst->high, a->high, b->high)); + emit(con, eor(dst->low, a->low, b->low)); +} + +void moveAR2(Context* con, + unsigned srcSize, + lir::Address* src, + unsigned dstSize, + lir::RegisterPair* dst) +{ + assertT(con, srcSize == 4 and dstSize == 4); + + lir::Constant constant(src->address); + moveCR(con, srcSize, &constant, dstSize, dst); + + lir::Memory memory(dst->low, 0, NoRegister, 0); + moveMR(con, dstSize, &memory, dstSize, dst); +} + +void moveAR(Context* con, + unsigned srcSize, + lir::Address* src, + unsigned dstSize, + lir::RegisterPair* dst) +{ + moveAR2(con, srcSize, src, dstSize, dst); +} + +void compareRR(Context* con, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + assertT(con, !(isFpr(a) ^ isFpr(b))); // regs must be of the same type + + if (!isFpr(a)) { // GPR compare + assertT(con, aSize == 4 && bSize == 4); + /**/ // assertT(con, b->low != a->low); + emit(con, cmp(b->low, a->low)); + } else { // FPR compare + assertT(con, aSize == bSize); + if (aSize == 8) + emit(con, fcmpd(fpr64(b), fpr64(a))); // double + else + emit(con, fcmps(fpr32(b), fpr32(a))); // single + emit(con, fmstat()); + } +} + +void compareCR(Context* con, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b) +{ + assertT(con, aSize == 4 and bSize == 4); + + if (!isFpr(b) && a->value->resolved() && isOfWidth(a->value->value(), 8)) { + emit(con, cmpi(b->low, a->value->value())); + } else { + lir::RegisterPair tmp(con->client->acquireTemporary(GPR_MASK)); + moveCR(con, aSize, a, bSize, &tmp); + compareRR(con, bSize, &tmp, bSize, b); + con->client->releaseTemporary(tmp.low); + } +} + +void compareCM(Context* con, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::Memory* b) +{ + assertT(con, aSize == 4 and bSize == 4); + + lir::RegisterPair tmp(con->client->acquireTemporary(GPR_MASK)); + moveMR(con, bSize, b, bSize, &tmp); + compareCR(con, aSize, a, bSize, &tmp); + con->client->releaseTemporary(tmp.low); +} + +void compareRM(Context* con, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize, + lir::Memory* b) +{ + assertT(con, aSize == 4 and bSize == 4); + + lir::RegisterPair tmp(con->client->acquireTemporary(GPR_MASK)); + moveMR(con, bSize, b, bSize, &tmp); + compareRR(con, aSize, a, bSize, &tmp); + con->client->releaseTemporary(tmp.low); +} + +int32_t branch(Context* con, lir::TernaryOperation op) +{ + switch (op) { + case lir::JumpIfEqual: + case lir::JumpIfFloatEqual: + return beq(0); + + case lir::JumpIfNotEqual: + case lir::JumpIfFloatNotEqual: + return bne(0); + + case lir::JumpIfLess: + case lir::JumpIfFloatLess: + case lir::JumpIfFloatLessOrUnordered: + return blt(0); + + case lir::JumpIfGreater: + case lir::JumpIfFloatGreater: + return bgt(0); + + case lir::JumpIfLessOrEqual: + case lir::JumpIfFloatLessOrEqual: + case lir::JumpIfFloatLessOrEqualOrUnordered: + return ble(0); + + case lir::JumpIfGreaterOrEqual: + case lir::JumpIfFloatGreaterOrEqual: + return bge(0); + + case lir::JumpIfFloatGreaterOrUnordered: + return bhi(0); + + case lir::JumpIfFloatGreaterOrEqualOrUnordered: + return bpl(0); + + default: + abort(con); + } +} + +void conditional(Context* con, int32_t branch, lir::Constant* target) +{ + appendOffsetTask(con, target->value, offsetPromise(con)); + emit(con, branch); +} + +void branch(Context* con, lir::TernaryOperation op, lir::Constant* target) +{ + conditional(con, branch(con, op), target); +} + +void branchLong(Context* con, + lir::TernaryOperation op, + lir::Operand* al, + lir::Operand* ah, + lir::Operand* bl, + lir::Operand* bh, + lir::Constant* target, + BinaryOperationType compareSigned, + BinaryOperationType compareUnsigned) +{ + compareSigned(con, 4, ah, 4, bh); + + unsigned next = 0; + + switch (op) { + case lir::JumpIfEqual: + case lir::JumpIfFloatEqual: + next = con->code.length(); + emit(con, bne(0)); + + compareSigned(con, 4, al, 4, bl); + conditional(con, beq(0), target); + break; + + case lir::JumpIfNotEqual: + case lir::JumpIfFloatNotEqual: + conditional(con, bne(0), target); + + compareSigned(con, 4, al, 4, bl); + conditional(con, bne(0), target); + break; + + case lir::JumpIfLess: + case lir::JumpIfFloatLess: + conditional(con, blt(0), target); + + next = con->code.length(); + emit(con, bgt(0)); + + compareUnsigned(con, 4, al, 4, bl); + conditional(con, blo(0), target); + break; + + case lir::JumpIfGreater: + case lir::JumpIfFloatGreater: + conditional(con, bgt(0), target); + + next = con->code.length(); + emit(con, blt(0)); + + compareUnsigned(con, 4, al, 4, bl); + conditional(con, bhi(0), target); + break; + + case lir::JumpIfLessOrEqual: + case lir::JumpIfFloatLessOrEqual: + conditional(con, blt(0), target); + + next = con->code.length(); + emit(con, bgt(0)); + + compareUnsigned(con, 4, al, 4, bl); + conditional(con, bls(0), target); + break; + + case lir::JumpIfGreaterOrEqual: + case lir::JumpIfFloatGreaterOrEqual: + conditional(con, bgt(0), target); + + next = con->code.length(); + emit(con, blt(0)); + + compareUnsigned(con, 4, al, 4, bl); + conditional(con, bhs(0), target); + break; + + default: + abort(con); + } + + if (next) { + updateOffset(con->s, + con->code.data.begin() + next, + reinterpret_cast(con->code.data.begin() + + con->code.length())); + } +} + +void branchRR(Context* con, + lir::TernaryOperation op, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::Constant* target) +{ + if (!isFpr(a) && size > vm::TargetBytesPerWord) { + lir::RegisterPair ah(a->high); + lir::RegisterPair bh(b->high); + + branchLong( + con, op, a, &ah, b, &bh, target, CAST2(compareRR), CAST2(compareRR)); + } else { + compareRR(con, size, a, size, b); + branch(con, op, target); + } +} + +void branchCR(Context* con, + lir::TernaryOperation op, + unsigned size, + lir::Constant* a, + lir::RegisterPair* b, + lir::Constant* target) +{ + assertT(con, !isFloatBranch(op)); + + if (size > vm::TargetBytesPerWord) { + int64_t v = a->value->value(); + + ResolvedPromise low(v & ~static_cast(0)); + lir::Constant al(&low); + + ResolvedPromise high((v >> 32) & ~static_cast(0)); + lir::Constant ah(&high); + + lir::RegisterPair bh(b->high); + + branchLong( + con, op, &al, &ah, b, &bh, target, CAST2(compareCR), CAST2(compareCR)); + } else { + compareCR(con, size, a, size, b); + branch(con, op, target); + } +} + +void branchRM(Context* con, + lir::TernaryOperation op, + unsigned size, + lir::RegisterPair* a, + lir::Memory* b, + lir::Constant* target) +{ + assertT(con, !isFloatBranch(op)); + assertT(con, size <= vm::TargetBytesPerWord); + + compareRM(con, size, a, size, b); + branch(con, op, target); +} + +void branchCM(Context* con, + lir::TernaryOperation op, + unsigned size, + lir::Constant* a, + lir::Memory* b, + lir::Constant* target) +{ + assertT(con, !isFloatBranch(op)); + assertT(con, size <= vm::TargetBytesPerWord); + + compareCM(con, size, a, size, b); + branch(con, op, target); +} + +ShiftMaskPromise* shiftMaskPromise(Context* con, + Promise* base, + unsigned shift, + int64_t mask) +{ + return new (con->zone) ShiftMaskPromise(base, shift, mask); +} + +void moveCM(Context* con, + unsigned srcSize, + lir::Constant* src, + unsigned dstSize, + lir::Memory* dst) +{ + switch (dstSize) { + case 8: { + lir::Constant srcHigh(shiftMaskPromise(con, src->value, 32, 0xFFFFFFFF)); + lir::Constant srcLow(shiftMaskPromise(con, src->value, 0, 0xFFFFFFFF)); + + lir::Memory dstLow(dst->base, dst->offset + 4, dst->index, dst->scale); + + moveCM(con, 4, &srcLow, 4, &dstLow); + moveCM(con, 4, &srcHigh, 4, dst); + } break; + + default: + lir::RegisterPair tmp(con->client->acquireTemporary(GPR_MASK)); + moveCR(con, srcSize, src, dstSize, &tmp); + moveRM(con, dstSize, &tmp, dstSize, dst); + con->client->releaseTemporary(tmp.low); + } +} + +void negateRR(Context* con, + unsigned srcSize, + lir::RegisterPair* src, + unsigned dstSize UNUSED, + lir::RegisterPair* dst) +{ + assertT(con, srcSize == dstSize); + + emit(con, mvn(dst->low, src->low)); + emit(con, SETS(addi(dst->low, dst->low, 1))); + if (srcSize == 8) { + emit(con, mvn(dst->high, src->high)); + emit(con, adci(dst->high, dst->high, 0)); + } +} + +void callR(Context* con, unsigned size UNUSED, lir::RegisterPair* target) +{ + assertT(con, size == vm::TargetBytesPerWord); + emit(con, blx(target->low)); +} + +void callC(Context* con, unsigned size UNUSED, lir::Constant* target) +{ + assertT(con, size == vm::TargetBytesPerWord); + + appendOffsetTask(con, target->value, offsetPromise(con)); + emit(con, bl(0)); +} + +void longCallC(Context* con, unsigned size UNUSED, lir::Constant* target) +{ + assertT(con, size == vm::TargetBytesPerWord); + + lir::RegisterPair tmp(Register(4)); + moveCR2(con, vm::TargetBytesPerWord, target, &tmp, offsetPromise(con)); + callR(con, vm::TargetBytesPerWord, &tmp); +} + +void alignedLongCallC(Context* con, unsigned size, lir::Constant* target) +{ + longCallC(con, size, target); +} + +void longJumpC(Context* con, unsigned size UNUSED, lir::Constant* target) +{ + assertT(con, size == vm::TargetBytesPerWord); + + lir::RegisterPair tmp( + Register(4)); // a non-arg reg that we don't mind clobbering + moveCR2(con, vm::TargetBytesPerWord, target, &tmp, offsetPromise(con)); + jumpR(con, vm::TargetBytesPerWord, &tmp); +} + +void alignedLongJumpC(Context* con, unsigned size, lir::Constant* target) +{ + longJumpC(con, size, target); +} + +void jumpC(Context* con, unsigned size UNUSED, lir::Constant* target) +{ + assertT(con, size == vm::TargetBytesPerWord); + + appendOffsetTask(con, target->value, offsetPromise(con)); + emit(con, b(0)); +} + +void return_(Context* con) +{ + emit(con, bx(LinkRegister)); +} + +void trap(Context* con) +{ + emit(con, bkpt(0)); +} + +// todo: determine the minimal operation types and domains needed to +// implement the following barriers (see +// http://community.arm.com/groups/processors/blog/2011/10/19/memory-access-ordering-part-3--memory-access-ordering-in-the-arm-architecture). +// For now, we just use DMB SY as a conservative but not necessarily +// performant choice. + +void memoryBarrier(Context* con UNUSED) +{ +#ifndef AVIAN_ASSUME_ARMV6 + emit(con, dmb()); +#endif +} + +void loadBarrier(Context* con) +{ + memoryBarrier(con); +} + +void storeStoreBarrier(Context* con) +{ + memoryBarrier(con); +} + +void storeLoadBarrier(Context* con) +{ + memoryBarrier(con); +} + +} // namespace arm +} // namespace codegen +} // namespace avian + +#endif // TARGET_BYTES_PER_WORD == 4 diff --git a/sgx-jvm/avian/src/codegen/target/arm/operations64.cpp b/sgx-jvm/avian/src/codegen/target/arm/operations64.cpp new file mode 100644 index 0000000000..9bd905c472 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/arm/operations64.cpp @@ -0,0 +1,1630 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "context.h" +#include "operations.h" +#include "block.h" +#include "fixup.h" +#include "multimethod.h" + +#if TARGET_BYTES_PER_WORD == 8 + +namespace { + +using namespace avian::codegen; +using namespace avian::codegen::arm; + +Register fpr(Register reg) +{ + return Register(reg.index() - N_GPRS); +} + +Register fpr(lir::RegisterPair* reg) +{ + return fpr(reg->low); +} + +void append(Context* c, uint32_t instruction) +{ + c->code.append4(instruction); +} + +uint32_t lslv(Register Rd, Register Rn, Register Rm, unsigned size) +{ + return (size == 8 ? 0x9ac02000 : 0x1ac02000) | (Rm.index() << 16) + | (Rn.index() << 5) | Rd.index(); +} + +uint32_t ubfm(Register Rd, Register Rn, int r, int s, unsigned size) +{ + return (size == 8 ? 0xd3400000 : 0x53000000) | (r << 16) | (s << 10) + | (Rn.index() << 5) | Rd.index(); +} + +uint32_t sbfm(Register Rd, Register Rn, int r, int s, unsigned size) +{ + return (size == 8 ? 0x93400000 : 0x13000000) | (r << 16) | (s << 10) + | (Rn.index() << 5) | Rd.index(); +} + +uint32_t lsli(Register Rd, Register Rn, int shift, unsigned size) +{ + if (size == 4) { + return ubfm(Rd, Rn, (32 - shift) & 0x1f, 31 - shift, size); + } else { + return ubfm(Rd, Rn, (64 - shift) & 0x3f, 63 - shift, size); + } +} + +uint32_t asrv(Register Rd, Register Rn, Register Rm, unsigned size) +{ + return (size == 8 ? 0x9ac02800 : 0x1ac02800) | (Rm.index() << 16) + | (Rn.index() << 5) | Rd.index(); +} + +uint32_t lsrv(Register Rd, Register Rn, Register Rm, unsigned size) +{ + return (size == 8 ? 0x9ac02400 : 0x1ac02400) | (Rm.index() << 16) + | (Rn.index() << 5) | Rd.index(); +} + +uint32_t lsri(Register Rd, Register Rn, int shift, unsigned size) +{ + return ubfm(Rd, Rn, shift, size == 8 ? 63 : 31, size); +} + +uint32_t asri(Register Rd, Register Rn, int shift, unsigned size) +{ + return sbfm(Rd, Rn, shift, size == 8 ? 63 : 31, size); +} + +uint32_t sxtb(Register Rd, Register Rn) +{ + return sbfm(Rd, Rn, 0, 7, 8); +} + +uint32_t sxth(Register Rd, Register Rn) +{ + return sbfm(Rd, Rn, 0, 15, 8); +} + +uint32_t uxth(Register Rd, Register Rn) +{ + return ubfm(Rd, Rn, 0, 15, 4); +} + +uint32_t sxtw(Register Rd, Register Rn) +{ + return sbfm(Rd, Rn, 0, 31, 8); +} + +uint32_t br(Register Rn) +{ + return 0xd61f0000 | (Rn.index() << 5); +} + +uint32_t fmovFdFn(Register Fd, Register Fn, unsigned size) +{ + return (size == 8 ? 0x1e604000 : 0x1e204000) | (Fn.index() << 5) | Fd.index(); +} + +uint32_t fmovRdFn(Register Rd, Register Fn, unsigned size) +{ + return (size == 8 ? 0x9e660000 : 0x1e260000) | (Fn.index() << 5) | Rd.index(); +} + +uint32_t fmovFdRn(Register Fd, Register Rn, unsigned size) +{ + return (size == 8 ? 0x9e670000 : 0x1e270000) | (Rn.index() << 5) | Fd.index(); +} + +uint32_t orr(Register Rd, Register Rn, Register Rm, unsigned size) +{ + return (size == 8 ? 0xaa000000 : 0x2a000000) | (Rm.index() << 16) + | (Rn.index() << 5) | Rd.index(); +} + +uint32_t addi(Register Rd, Register Rn, int value, int shift, unsigned size) +{ + return (size == 8 ? 0x91000000 : 0x11000000) | (shift ? 0x400000 : 0) + | (value << 10) | (Rn.index() << 5) | Rd.index(); +} + +uint32_t mov(Register Rd, Register Rn, unsigned size) +{ + return Rn.index() == 31 or Rd.index() == 31 ? addi(Rd, Rn, 0, 0, size) + : orr(Rd, Register(31), Rn, size); +} + +uint32_t movz(Register Rd, int value, unsigned shift, unsigned size) +{ + return (size == 8 ? 0xd2800000 : 0x52800000) | ((shift >> 4) << 21) + | (value << 5) | Rd.index(); +} + +uint32_t movn(Register Rd, int value, unsigned shift, unsigned size) +{ + return (size == 8 ? 0x92800000 : 0x12800000) | ((shift >> 4) << 21) + | (value << 5) | Rd.index(); +} + +uint32_t movk(Register Rd, int value, unsigned shift, unsigned size) +{ + return (size == 8 ? 0xf2800000 : 0x72800000) | ((shift >> 4) << 21) + | (value << 5) | Rd.index(); +} + +uint32_t ldrPCRel(Register Rd, int offset, unsigned size) +{ + return (size == 8 ? 0x58000000 : 0x18000000) | ((offset >> 2) << 5) + | Rd.index(); +} + +uint32_t add(Register Rd, Register Rn, Register Rm, unsigned size) +{ + return (size == 8 ? 0x8b000000 : 0x0b000000) | (Rm.index() << 16) + | (Rn.index() << 5) | Rd.index(); +} + +uint32_t sub(Register Rd, Register Rn, Register Rm, unsigned size) +{ + return (size == 8 ? 0xcb000000 : 0x4b000000) | (Rm.index() << 16) + | (Rn.index() << 5) | Rd.index(); +} + +uint32_t and_(Register Rd, Register Rn, Register Rm, unsigned size) +{ + return (size == 8 ? 0x8a000000 : 0x0a000000) | (Rm.index() << 16) + | (Rn.index() << 5) | Rd.index(); +} + +uint32_t eor(Register Rd, Register Rn, Register Rm, unsigned size) +{ + return (size == 8 ? 0xca000000 : 0x4a000000) | (Rm.index() << 16) + | (Rn.index() << 5) | Rd.index(); +} + +uint32_t madd(Register Rd, Register Rn, Register Rm, Register Ra, unsigned size) +{ + return (size == 8 ? 0x9b000000 : 0x1b000000) | (Rm.index() << 16) + | (Ra.index() << 10) | (Rn.index() << 5) | Rd.index(); +} + +uint32_t mul(Register Rd, Register Rn, Register Rm, unsigned size) +{ + return madd(Rd, Rn, Rm, Register(31), size); +} + +uint32_t subi(Register Rd, Register Rn, int value, int shift, unsigned size) +{ + return (size == 8 ? 0xd1000000 : 0x51000000) | (shift ? 0x400000 : 0) + | (value << 10) | (Rn.index() << 5) | Rd.index(); +} + +uint32_t fabs_(Register Fd, Register Fn, unsigned size) +{ + return (size == 8 ? 0x1e60c000 : 0x1e20c000) | (Fn.index() << 5) | Fd.index(); +} + +uint32_t fneg(Register Fd, Register Fn, unsigned size) +{ + return (size == 8 ? 0x1e614000 : 0x1e214000) | (Fn.index() << 5) | Fd.index(); +} + +uint32_t fsqrt(Register Fd, Register Fn, unsigned size) +{ + return (size == 8 ? 0x1e61c000 : 0x1e21c000) | (Fn.index() << 5) | Fd.index(); +} + +uint32_t fadd(Register Fd, Register Fn, Register Fm, unsigned size) +{ + return (size == 8 ? 0x1e602800 : 0x1e202800) | (Fm.index() << 16) + | (Fn.index() << 5) | Fd.index(); +} + +uint32_t fsub(Register Fd, Register Fn, Register Fm, unsigned size) +{ + return (size == 8 ? 0x1e603800 : 0x1e203800) | (Fm.index() << 16) + | (Fn.index() << 5) | Fd.index(); +} + +uint32_t fmul(Register Fd, Register Fn, Register Fm, unsigned size) +{ + return (size == 8 ? 0x1e600800 : 0x1e200800) | (Fm.index() << 16) + | (Fn.index() << 5) | Fd.index(); +} + +uint32_t fdiv(Register Fd, Register Fn, Register Fm, unsigned size) +{ + return (size == 8 ? 0x1e601800 : 0x1e201800) | (Fm.index() << 16) + | (Fn.index() << 5) | Fd.index(); +} + +uint32_t fcvtSdDn(Register Fd, Register Fn) +{ + return 0x1e624000 | (Fn.index() << 5) | Fd.index(); +} + +uint32_t fcvtDdSn(Register Fd, Register Fn) +{ + return 0x1e22c000 | (Fn.index() << 5) | Fd.index(); +} + +uint32_t fcvtasXdDn(Register Rd, Register Fn) +{ + return 0x9e640000 | (Fn.index() << 5) | Rd.index(); +} + +uint32_t fcvtasWdSn(Register Rd, Register Fn) +{ + return 0x1e240000 | (Fn.index() << 5) | Rd.index(); +} + +uint32_t scvtfDdXn(Register Fd, Register Rn) +{ + return 0x9e620000 | (Rn.index() << 5) | Fd.index(); +} + +uint32_t scvtfSdWn(Register Fd, Register Rn) +{ + return 0x1e220000 | (Rn.index() << 5) | Fd.index(); +} + +uint32_t strFs(Register Fs, Register Rn, Register Rm, unsigned size) +{ + return (size == 8 ? 0xfc206800 : 0xbc206800) | (Rm.index() << 16) + | (Rn.index() << 5) | Fs.index(); +} + +uint32_t strb(Register Rs, Register Rn, Register Rm) +{ + return 0x38206800 | (Rm.index() << 16) | (Rn.index() << 5) | Rs.index(); +} + +uint32_t strh(Register Rs, Register Rn, Register Rm) +{ + return 0x78206800 | (Rm.index() << 16) | (Rn.index() << 5) | Rs.index(); +} + +uint32_t striFs(Register Fs, Register Rn, int offset, unsigned size) +{ + return (size == 8 ? 0xfd000000 : 0xbd000000) + | ((offset >> (size == 8 ? 3 : 2)) << 10) | (Rn.index() << 5) + | Fs.index(); +} + +uint32_t str(Register Rs, Register Rn, Register Rm, unsigned size) +{ + return (size == 8 ? 0xf8206800 : 0xb8206800) | (Rm.index() << 16) + | (Rn.index() << 5) | Rs.index(); +} + +uint32_t strbi(Register Rs, Register Rn, int offset) +{ + return 0x39000000 | (offset << 10) | (Rn.index() << 5) | Rs.index(); +} + +uint32_t strhi(Register Rs, Register Rn, int offset) +{ + return 0x79000000 | ((offset >> 1) << 10) | (Rn.index() << 5) | Rs.index(); +} + +uint32_t stri(Register Rs, Register Rn, int offset, unsigned size) +{ + return (size == 8 ? 0xf9000000 : 0xb9000000) + | ((offset >> (size == 8 ? 3 : 2)) << 10) | (Rn.index() << 5) + | Rs.index(); +} + +uint32_t ldrFd(Register Fd, Register Rn, Register Rm, unsigned size) +{ + return (size == 8 ? 0xfc606800 : 0xbc606800) | (Rm.index() << 16) + | (Rn.index() << 5) | Fd.index(); +} + +uint32_t ldrb(Register Rd, Register Rn, Register Rm) +{ + return 0x38606800 | (Rm.index() << 16) | (Rn.index() << 5) | Rd.index(); +} + +uint32_t ldrsb(Register Rd, Register Rn, Register Rm) +{ + return 0x38e06800 | (Rm.index() << 16) | (Rn.index() << 5) | Rd.index(); +} + +uint32_t ldrh(Register Rd, Register Rn, Register Rm) +{ + return 0x78606800 | (Rm.index() << 16) | (Rn.index() << 5) | Rd.index(); +} + +uint32_t ldrsh(Register Rd, Register Rn, Register Rm) +{ + return 0x78e06800 | (Rm.index() << 16) | (Rn.index() << 5) | Rd.index(); +} + +uint32_t ldrsw(Register Rd, Register Rn, Register Rm) +{ + return 0xb8a06800 | (Rm.index() << 16) | (Rn.index() << 5) | Rd.index(); +} + +uint32_t ldr(Register Rd, Register Rn, Register Rm, unsigned size) +{ + return (size == 8 ? 0xf8606800 : 0xb8606800) | (Rm.index() << 16) + | (Rn.index() << 5) | Rd.index(); +} + +uint32_t ldriFd(Register Fd, Register Rn, int offset, unsigned size) +{ + return (size == 8 ? 0xfd400000 : 0xbd400000) + | ((offset >> (size == 8 ? 3 : 2)) << 10) | (Rn.index() << 5) + | Fd.index(); +} + +uint32_t ldrbi(Register Rd, Register Rn, int offset) +{ + return 0x39400000 | (offset << 10) | (Rn.index() << 5) | Rd.index(); +} + +uint32_t ldrsbi(Register Rd, Register Rn, int offset) +{ + return 0x39c00000 | (offset << 10) | (Rn.index() << 5) | Rd.index(); +} + +uint32_t ldrhi(Register Rd, Register Rn, int offset) +{ + return 0x79400000 | ((offset >> 1) << 10) | (Rn.index() << 5) | Rd.index(); +} + +uint32_t ldrshi(Register Rd, Register Rn, int offset) +{ + return 0x79c00000 | ((offset >> 1) << 10) | (Rn.index() << 5) | Rd.index(); +} + +uint32_t ldrswi(Register Rd, Register Rn, int offset) +{ + return 0xb9800000 | ((offset >> 2) << 10) | (Rn.index() << 5) | Rd.index(); +} + +uint32_t ldri(Register Rd, Register Rn, int offset, unsigned size) +{ + return (size == 8 ? 0xf9400000 : 0xb9400000) + | ((offset >> (size == 8 ? 3 : 2)) << 10) | (Rn.index() << 5) + | Rd.index(); +} + +uint32_t fcmp(Register Fn, Register Fm, unsigned size) +{ + return (size == 8 ? 0x1e602000 : 0x1e202000) | (Fm.index() << 16) + | (Fn.index() << 5); +} + +uint32_t neg(Register Rd, Register Rm, unsigned size) +{ + return (size == 8 ? 0xcb0003e0 : 0x4b0003e0) | (Rm.index() << 16) + | Rd.index(); +} + +uint32_t cmp(Register Rn, Register Rm, unsigned size) +{ + return (size == 8 ? 0xeb00001f : 0x6b00001f) | (Rm.index() << 16) + | (Rn.index() == 31 ? 0x2063ff : (Rn.index() << 5)); +} + +uint32_t cmpi(Register Rn, int value, unsigned shift, unsigned size) +{ + return (size == 8 ? 0xf100001f : 0x7100001f) | (shift == 12 ? 0x400000 : 0) + | (value << 10) | (Rn.index() << 5); +} + +uint32_t b(int offset) +{ + return 0x14000000 | (offset >> 2); +} + +uint32_t bl(int offset) +{ + return 0x94000000 | (offset >> 2); +} + +uint32_t blr(Register Rn) +{ + return 0xd63f0000 | (Rn.index() << 5); +} + +uint32_t beq(int offset) +{ + return 0x54000000 | ((offset >> 2) << 5); +} + +uint32_t bne(int offset) +{ + return 0x54000001 | ((offset >> 2) << 5); +} + +uint32_t blt(int offset) +{ + return 0x5400000b | ((offset >> 2) << 5); +} + +uint32_t bgt(int offset) +{ + return 0x5400000c | ((offset >> 2) << 5); +} + +uint32_t ble(int offset) +{ + return 0x5400000d | ((offset >> 2) << 5); +} + +uint32_t bge(int offset) +{ + return 0x5400000a | ((offset >> 2) << 5); +} + +uint32_t bhi(int offset) +{ + return 0x54000008 | ((offset >> 2) << 5); +} + +uint32_t bpl(int offset) +{ + return 0x54000005 | ((offset >> 2) << 5); +} + +uint32_t brk(int flag) +{ + return 0xd4200020 | (flag << 5); +} + +uint32_t dmb(int flag) +{ + return 0xd50330bf | (flag << 8); +} + +} // namespace + +namespace avian { +namespace codegen { +namespace arm { + +using namespace avian::util; + +void shiftLeftR(Context* c, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* dst) +{ + append(c, lslv(dst->low, b->low, a->low, size)); +} + +void shiftLeftC(Context* c, + unsigned size, + lir::Constant* a, + lir::RegisterPair* b, + lir::RegisterPair* dst) +{ + uint64_t value = a->value->value(); + if (size == 4 and (value & 0x1F)) { + append(c, lsli(dst->low, b->low, value & 0x1F, 4)); + } else if (size == 8 and (value & 0x3F)) { + append(c, lsli(dst->low, b->low, value & 0x3F, 8)); + } else { + moveRR(c, size, b, size, dst); + } +} + +void shiftRightR(Context* c, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* dst) +{ + append(c, asrv(dst->low, b->low, a->low, size)); +} + +void shiftRightC(Context* c, + unsigned size UNUSED, + lir::Constant* a, + lir::RegisterPair* b, + lir::RegisterPair* dst) +{ + uint64_t value = a->value->value(); + if (size == 4 and (value & 0x1F)) { + append(c, asri(dst->low, b->low, value & 0x1F, 4)); + } else if (size == 8 and (value & 0x3F)) { + append(c, asri(dst->low, b->low, value & 0x3F, 8)); + } else { + moveRR(c, size, b, size, dst); + } +} + +void unsignedShiftRightR(Context* c, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* dst) +{ + append(c, lsrv(dst->low, b->low, a->low, size)); +} + +void unsignedShiftRightC(Context* c, + unsigned size UNUSED, + lir::Constant* a, + lir::RegisterPair* b, + lir::RegisterPair* dst) +{ + uint64_t value = a->value->value(); + if (size == 4 and (value & 0x1F)) { + append(c, lsri(dst->low, b->low, value & 0x1F, 4)); + } else if (size == 8 and (value & 0x3F)) { + append(c, lsri(dst->low, b->low, value & 0x3F, 8)); + } else { + moveRR(c, size, b, size, dst); + } +} + +void jumpR(Context* c, unsigned size UNUSED, lir::RegisterPair* target) +{ + assertT(c, size == vm::TargetBytesPerWord); + append(c, br(target->low)); +} + +void moveRR(Context* c, + unsigned srcSize, + lir::RegisterPair* src, + unsigned dstSize, + lir::RegisterPair* dst) +{ + bool srcIsFpr = isFpr(src); + bool dstIsFpr = isFpr(dst); + if (srcIsFpr or dstIsFpr) { + assertT(c, srcSize == dstSize); + + if (srcIsFpr and dstIsFpr) { + append(c, fmovFdFn(fpr(dst), fpr(src), srcSize)); + } else if (srcIsFpr) { + append(c, fmovRdFn(dst->low, fpr(src), srcSize)); + } else { + append(c, fmovFdRn(fpr(dst), src->low, srcSize)); + } + } else { + switch (srcSize) { + case 1: + append(c, sxtb(dst->low, src->low)); + break; + + case 2: + append(c, sxth(dst->low, src->low)); + break; + + case 4: + if (dstSize == 4) { + append(c, mov(dst->low, src->low, srcSize)); + } else { + append(c, sxtw(dst->low, src->low)); + } + break; + + case 8: + append(c, mov(dst->low, src->low, srcSize)); + break; + + default: + abort(c); + } + } +} + +void moveZRR(Context* c, + unsigned srcSize, + lir::RegisterPair* src, + unsigned, + lir::RegisterPair* dst) +{ + switch (srcSize) { + case 2: + append(c, uxth(dst->low, src->low)); + break; + + default: + abort(c); + } +} + +void moveCR2(Context* c, + unsigned size, + lir::Constant* src, + lir::RegisterPair* dst, + Promise* callOffset) +{ + if (isFpr(dst)) { + // todo: could use a single fmov here and avoid the temporary for + // constants that fit + lir::RegisterPair tmp(c->client->acquireTemporary(GPR_MASK)); + moveCR(c, size, src, size, &tmp); + moveRR(c, size, &tmp, size, dst); + c->client->releaseTemporary(tmp.low); + } else if (callOffset == 0 and src->value->resolved()) { + // todo: Is it better performance-wise to load using immediate + // moves or via a PC-relative constant pool? Does it depend on + // how many significant bits there are? + + int64_t value = src->value->value(); + if (value >= 0) { + append(c, movz(dst->low, value & 0xFFFF, 0, size)); + if (value >> 16) { + if ((value >> 16) & 0xFFFF) { + append(c, movk(dst->low, (value >> 16) & 0xFFFF, 16, size)); + } + if (value >> 32) { + if ((value >> 32) & 0xFFFF) { + append(c, movk(dst->low, (value >> 32) & 0xFFFF, 32, size)); + } + if (value >> 48) { + append(c, movk(dst->low, (value >> 48) & 0xFFFF, 48, size)); + } + } + } + } else { + append(c, movn(dst->low, (~value) & 0xFFFF, 0, size)); + if (~(value >> 16)) { + if (((value >> 16) & 0xFFFF) != 0xFFFF) { + append(c, movk(dst->low, (value >> 16) & 0xFFFF, 16, size)); + } + if (~(value >> 32)) { + if (((value >> 32) & 0xFFFF) != 0xFFFF) { + append(c, movk(dst->low, (value >> 32) & 0xFFFF, 32, size)); + } + if (~(value >> 48)) { + append(c, movk(dst->low, (value >> 48) & 0xFFFF, 48, size)); + } + } + } + } + } else { + appendConstantPoolEntry(c, src->value, callOffset); + append(c, ldrPCRel(dst->low, 0, size)); + } +} + +void moveCR(Context* c, + unsigned size, + lir::Constant* src, + unsigned, + lir::RegisterPair* dst) +{ + moveCR2(c, size, src, dst, 0); +} + +void addR(Context* c, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* dst) +{ + append(c, add(dst->low, a->low, b->low, size)); +} + +void subR(Context* c, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* dst) +{ + append(c, sub(dst->low, b->low, a->low, size)); +} + +void addC(Context* c, + unsigned size, + lir::Constant* a, + lir::RegisterPair* b, + lir::RegisterPair* dst) +{ + int64_t v = a->value->value(); + if (v) { + if (v > 0 and v < 0x1000) { + append(c, addi(dst->low, b->low, v, 0, size)); + } else if (v > 0 and v < 0x1000000 and v % 0x1000 == 0) { + append(c, addi(dst->low, b->low, v >> 12, 12, size)); + } else { + // todo + abort(c); + } + } else { + moveRR(c, size, b, size, dst); + } +} + +void subC(Context* c, + unsigned size, + lir::Constant* a, + lir::RegisterPair* b, + lir::RegisterPair* dst) +{ + int64_t v = a->value->value(); + if (v) { + if (v > 0 and v < 0x1000) { + append(c, subi(dst->low, b->low, v, 0, size)); + } else if (v > 0 and v < 0x1000000 and v % 0x1000 == 0) { + append(c, subi(dst->low, b->low, v >> 12, 12, size)); + } else { + // todo + abort(c); + } + } else { + moveRR(c, size, b, size, dst); + } +} + +void multiplyR(Context* c, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* dst) +{ + append(c, mul(dst->low, a->low, b->low, size)); +} + +void floatAbsoluteRR(Context* c, + unsigned size, + lir::RegisterPair* a, + unsigned, + lir::RegisterPair* b) +{ + append(c, fabs_(fpr(b), fpr(a), size)); +} + +void floatNegateRR(Context* c, + unsigned size, + lir::RegisterPair* a, + unsigned, + lir::RegisterPair* b) +{ + append(c, fneg(fpr(b), fpr(a), size)); +} + +void float2FloatRR(Context* c, + unsigned size, + lir::RegisterPair* a, + unsigned, + lir::RegisterPair* b) +{ + if (size == 8) { + append(c, fcvtSdDn(fpr(b), fpr(a))); + } else { + append(c, fcvtDdSn(fpr(b), fpr(a))); + } +} + +void float2IntRR(Context* c, + unsigned size, + lir::RegisterPair* a, + unsigned, + lir::RegisterPair* b) +{ + if (size == 8) { + append(c, fcvtasXdDn(b->low, fpr(a))); + } else { + append(c, fcvtasWdSn(b->low, fpr(a))); + } +} + +void int2FloatRR(Context* c, + unsigned, + lir::RegisterPair* a, + unsigned size, + lir::RegisterPair* b) +{ + if (size == 8) { + append(c, scvtfDdXn(fpr(b), a->low)); + } else { + append(c, scvtfSdWn(fpr(b), a->low)); + } +} + +void floatSqrtRR(Context* c, + unsigned size, + lir::RegisterPair* a, + unsigned, + lir::RegisterPair* b) +{ + append(c, fsqrt(fpr(b), fpr(a), size)); +} + +void floatAddR(Context* c, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* dst) +{ + append(c, fadd(fpr(dst), fpr(b), fpr(a), size)); +} + +void floatSubtractR(Context* c, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* dst) +{ + append(c, fsub(fpr(dst), fpr(b), fpr(a), size)); +} + +void floatMultiplyR(Context* c, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* dst) +{ + append(c, fmul(fpr(dst), fpr(b), fpr(a), size)); +} + +void floatDivideR(Context* c, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* dst) +{ + append(c, fdiv(fpr(dst), fpr(b), fpr(a), size)); +} + +Register normalize(Context* c, + int offset, + Register index, + unsigned scale, + bool* preserveIndex, + bool* release) +{ + if (offset != 0 or scale != 1) { + lir::RegisterPair normalizedIndex( + *preserveIndex ? c->client->acquireTemporary(GPR_MASK) : index); + + if (*preserveIndex) { + *release = true; + *preserveIndex = false; + } else { + *release = false; + } + + Register scaled; + + if (scale != 1) { + lir::RegisterPair unscaledIndex(index); + + ResolvedPromise scalePromise(log(scale)); + lir::Constant scaleConstant(&scalePromise); + + shiftLeftC(c, + vm::TargetBytesPerWord, + &scaleConstant, + &unscaledIndex, + &normalizedIndex); + + scaled = normalizedIndex.low; + } else { + scaled = index; + } + + if (offset != 0) { + lir::RegisterPair untranslatedIndex(scaled); + + ResolvedPromise offsetPromise(offset); + lir::Constant offsetConstant(&offsetPromise); + + lir::RegisterPair tmp(c->client->acquireTemporary(GPR_MASK)); + moveCR(c, + vm::TargetBytesPerWord, + &offsetConstant, + vm::TargetBytesPerWord, + &tmp); + addR(c, + vm::TargetBytesPerWord, + &tmp, + &untranslatedIndex, + &normalizedIndex); + c->client->releaseTemporary(tmp.low); + } + + return normalizedIndex.low; + } else { + *release = false; + return index; + } +} + +void store(Context* c, + unsigned size, + lir::RegisterPair* src, + Register base, + int offset, + Register index, + unsigned scale, + bool preserveIndex) +{ + if (index != NoRegister) { + bool release; + + // todo: browsing the instruction set, it looks like we could do a + // scaled store or load in a single instruction if the offset is + // zero, and we could simplify things for the case of non-zero + // offsets also + + Register normalized + = normalize(c, offset, index, scale, &preserveIndex, &release); + + if (isFpr(src)) { + switch (size) { + case 4: + case 8: + append(c, strFs(fpr(src->low), base, normalized, size)); + break; + + default: + abort(c); + } + } else { + switch (size) { + case 1: + append(c, strb(src->low, base, normalized)); + break; + + case 2: + append(c, strh(src->low, base, normalized)); + break; + + case 4: + case 8: + append(c, str(src->low, base, normalized, size)); + break; + + default: + abort(c); + } + } + + if (release) { + c->client->releaseTemporary(normalized); + } + } else if (abs(offset) == (abs(offset) & 0xFFF)) { + if (isFpr(src)) { + switch (size) { + case 4: + case 8: + assertT(c, offset == (offset & (size == 8 ? (~7) : (~3)))); + append(c, striFs(fpr(src->low), base, offset, size)); + break; + + default: + abort(c); + } + } else { // FPR store + switch (size) { + case 1: + append(c, strbi(src->low, base, offset)); + break; + + case 2: + assertT(c, offset == (offset & (~1))); + append(c, strhi(src->low, base, offset)); + break; + + case 4: + assertT(c, offset == (offset & (~3))); + append(c, stri(src->low, base, offset, size)); + break; + + case 8: + assertT(c, offset == (offset & (~7))); + append(c, stri(src->low, base, offset, size)); + break; + + default: + abort(c); + } + } + } else { + lir::RegisterPair tmp(c->client->acquireTemporary(GPR_MASK)); + ResolvedPromise offsetPromise(offset); + lir::Constant offsetConstant(&offsetPromise); + moveCR(c, + vm::TargetBytesPerWord, + &offsetConstant, + vm::TargetBytesPerWord, + &tmp); + + store(c, size, src, base, 0, tmp.low, 1, false); + + c->client->releaseTemporary(tmp.low); + } +} + +void moveRM(Context* c, + unsigned srcSize, + lir::RegisterPair* src, + unsigned dstSize UNUSED, + lir::Memory* dst) +{ + assertT(c, srcSize == dstSize); + + if (src->low.index() == 31) { + assertT(c, c->client == 0); // the compiler should never ask us to + // store the SP; we'll only get here + // when assembling a thunk + + lir::RegisterPair tmp(Register(9)); // we're in a thunk, so we can + // clobber this + + moveRR(c, srcSize, src, srcSize, &tmp); + store( + c, srcSize, &tmp, dst->base, dst->offset, dst->index, dst->scale, true); + } else { + store( + c, srcSize, src, dst->base, dst->offset, dst->index, dst->scale, true); + } +} + +void load(Context* c, + unsigned srcSize, + Register base, + int offset, + Register index, + unsigned scale, + unsigned dstSize, + lir::RegisterPair* dst, + bool preserveIndex, + bool signExtend) +{ + if (index != NoRegister) { + bool release; + Register normalized + = normalize(c, offset, index, scale, &preserveIndex, &release); + + if (isFpr(dst)) { // FPR load + switch (srcSize) { + case 4: + case 8: + append(c, ldrFd(fpr(dst->low), base, normalized, srcSize)); + break; + + default: + abort(c); + } + } else { + switch (srcSize) { + case 1: + if (signExtend) { + append(c, ldrsb(dst->low, base, normalized)); + } else { + append(c, ldrb(dst->low, base, normalized)); + } + break; + + case 2: + if (signExtend) { + append(c, ldrsh(dst->low, base, normalized)); + } else { + append(c, ldrh(dst->low, base, normalized)); + } + break; + + case 4: + case 8: + if (signExtend and srcSize == 4 and dstSize == 8) { + append(c, ldrsw(dst->low, base, normalized)); + } else { + append(c, ldr(dst->low, base, normalized, srcSize)); + } + break; + + default: + abort(c); + } + } + + if (release) { + c->client->releaseTemporary(normalized); + } + } else if (abs(offset) == (abs(offset) & 0xFFF)) { + if (isFpr(dst)) { + switch (srcSize) { + case 4: + case 8: + assertT(c, offset == (offset & (srcSize == 8 ? (~7) : (~3)))); + append(c, ldriFd(fpr(dst->low), base, offset, srcSize)); + break; + + default: + abort(c); + } + } else { + switch (srcSize) { + case 1: + if (signExtend) { + append(c, ldrsbi(dst->low, base, offset)); + } else { + append(c, ldrbi(dst->low, base, offset)); + } + break; + + case 2: + assertT(c, offset == (offset & (~1))); + if (signExtend) { + append(c, ldrshi(dst->low, base, offset)); + } else { + append(c, ldrhi(dst->low, base, offset)); + } + break; + + case 4: + case 8: + if (signExtend and srcSize == 4 and dstSize == 8) { + assertT(c, offset == (offset & (~3))); + append(c, ldrswi(dst->low, base, offset)); + } else { + assertT(c, offset == (offset & (srcSize == 8 ? (~7) : (~3)))); + append(c, ldri(dst->low, base, offset, srcSize)); + } + break; + + default: + abort(c); + } + } + } else { + lir::RegisterPair tmp(c->client->acquireTemporary(GPR_MASK)); + ResolvedPromise offsetPromise(offset); + lir::Constant offsetConstant(&offsetPromise); + moveCR(c, + vm::TargetBytesPerWord, + &offsetConstant, + vm::TargetBytesPerWord, + &tmp); + + load(c, srcSize, base, 0, tmp.low, 1, dstSize, dst, false, signExtend); + + c->client->releaseTemporary(tmp.low); + } +} + +void moveMR(Context* c, + unsigned srcSize, + lir::Memory* src, + unsigned dstSize, + lir::RegisterPair* dst) +{ + if (dst->low.index() == 31) { + assertT(c, c->client == 0); // the compiler should never ask us to + // load the SP; we'll only get here + // when assembling a thunk + + lir::RegisterPair tmp(Register(9)); // we're in a thunk, so we can + // clobber this + + load(c, srcSize, src->base, src->offset, src->index, src->scale, dstSize, &tmp, true, true); + moveRR(c, dstSize, &tmp, dstSize, dst); + } else { + load(c, + srcSize, + src->base, + src->offset, + src->index, + src->scale, + dstSize, + dst, + true, + true); + } +} + +void moveZMR(Context* c, + unsigned srcSize, + lir::Memory* src, + unsigned dstSize, + lir::RegisterPair* dst) +{ + load(c, + srcSize, + src->base, + src->offset, + src->index, + src->scale, + dstSize, + dst, + true, + false); +} + +void andR(Context* c, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* dst) +{ + append(c, and_(dst->low, a->low, b->low, size)); +} + +void andC(Context* c, + unsigned size, + lir::Constant* a, + lir::RegisterPair* b, + lir::RegisterPair* dst) +{ + int64_t v = a->value->value(); + + if (~v) { + bool useTemporary = b->low == dst->low; + lir::RegisterPair tmp(dst->low); + if (useTemporary) { + tmp.low = c->client->acquireTemporary(GPR_MASK); + } + + moveCR(c, size, a, size, &tmp); + andR(c, size, b, &tmp, dst); + + if (useTemporary) { + c->client->releaseTemporary(tmp.low); + } + } else { + moveRR(c, size, b, size, dst); + } +} + +void orR(Context* c, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* dst) +{ + append(c, orr(dst->low, a->low, b->low, size)); +} + +void xorR(Context* c, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::RegisterPair* dst) +{ + append(c, eor(dst->low, a->low, b->low, size)); +} + +void moveAR(Context* c, + unsigned srcSize, + lir::Address* src, + unsigned dstSize, + lir::RegisterPair* dst) +{ + assertT( + c, + srcSize == vm::TargetBytesPerWord and dstSize == vm::TargetBytesPerWord); + + lir::Constant constant(src->address); + moveCR(c, srcSize, &constant, dstSize, dst); + + lir::Memory memory(dst->low, 0, NoRegister, 0); + moveMR(c, dstSize, &memory, dstSize, dst); +} + +void compareRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + assertT(c, not(isFpr(a) xor isFpr(b))); + assertT(c, aSize == bSize); + + if (isFpr(a)) { + append(c, fcmp(fpr(b), fpr(a), aSize)); + } else { + append(c, cmp(b->low, a->low, aSize)); + } +} + +void compareCR(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + assertT(c, aSize == bSize); + + if (!isFpr(b) && a->value->resolved()) { + int64_t v = a->value->value(); + if (v == 0) { + append(c, cmp(b->low, Register(31), aSize)); + return; + } else if (v > 0 and v < 0x1000) { + append(c, cmpi(b->low, v, 0, aSize)); + return; + } else if (v > 0 and v < 0x1000000 and v % 0x1000 == 0) { + append(c, cmpi(b->low, v >> 12, 12, aSize)); + return; + } + } + + lir::RegisterPair tmp(c->client->acquireTemporary(GPR_MASK)); + moveCR(c, aSize, a, bSize, &tmp); + compareRR(c, bSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); +} + +void compareCM(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::Memory* b) +{ + assertT(c, aSize == bSize); + + lir::RegisterPair tmp(c->client->acquireTemporary(GPR_MASK)); + moveMR(c, bSize, b, bSize, &tmp); + compareCR(c, aSize, a, bSize, &tmp); + c->client->releaseTemporary(tmp.low); +} + +void compareRM(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize, + lir::Memory* b) +{ + assertT(c, aSize == bSize); + + lir::RegisterPair tmp(c->client->acquireTemporary(GPR_MASK)); + moveMR(c, bSize, b, bSize, &tmp); + compareRR(c, aSize, a, bSize, &tmp); + c->client->releaseTemporary(tmp.low); +} + +void compareMR(Context* c, + unsigned aSize, + lir::Memory* a, + unsigned bSize, + lir::RegisterPair* b) +{ + assertT(c, aSize == bSize); + + lir::RegisterPair tmp(c->client->acquireTemporary(GPR_MASK)); + moveMR(c, aSize, a, aSize, &tmp); + compareRR(c, aSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); +} + +int32_t branch(Context* c, lir::TernaryOperation op) +{ + switch (op) { + case lir::JumpIfEqual: + case lir::JumpIfFloatEqual: + return beq(0); + + case lir::JumpIfNotEqual: + case lir::JumpIfFloatNotEqual: + return bne(0); + + case lir::JumpIfLess: + case lir::JumpIfFloatLess: + case lir::JumpIfFloatLessOrUnordered: + return blt(0); + + case lir::JumpIfGreater: + case lir::JumpIfFloatGreater: + return bgt(0); + + case lir::JumpIfLessOrEqual: + case lir::JumpIfFloatLessOrEqual: + case lir::JumpIfFloatLessOrEqualOrUnordered: + return ble(0); + + case lir::JumpIfGreaterOrEqual: + case lir::JumpIfFloatGreaterOrEqual: + return bge(0); + + case lir::JumpIfFloatGreaterOrUnordered: + return bhi(0); + + case lir::JumpIfFloatGreaterOrEqualOrUnordered: + return bpl(0); + + default: + abort(c); + } +} + +void conditional(Context* c, int32_t branch, lir::Constant* target) +{ + appendOffsetTask(c, target->value, offsetPromise(c)); + append(c, branch); +} + +void branch(Context* c, lir::TernaryOperation op, lir::Constant* target) +{ + conditional(c, branch(c, op), target); +} + +void branchRR(Context* c, + lir::TernaryOperation op, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::Constant* target) +{ + compareRR(c, size, a, size, b); + branch(c, op, target); +} + +void branchCR(Context* c, + lir::TernaryOperation op, + unsigned size, + lir::Constant* a, + lir::RegisterPair* b, + lir::Constant* target) +{ + assertT(c, not isFloatBranch(op)); + + compareCR(c, size, a, size, b); + branch(c, op, target); +} + +void branchRM(Context* c, + lir::TernaryOperation op, + unsigned size, + lir::RegisterPair* a, + lir::Memory* b, + lir::Constant* target) +{ + assertT(c, not isFloatBranch(op)); + assertT(c, size <= vm::TargetBytesPerWord); + + if (a->low.index() == 31) { + // Stack overflow checks need to compare to the stack pointer, but + // we can only encode that in the opposite operand order we're + // given, so we need to reverse everything. Also, we can't do a + // conditional jump further than 2^19 instructions away, which can + // cause trouble with large code, so we branch past an + // unconditional branch which can jump further, which reverses the + // logic again. Confused? Good. + assertT(c, op == lir::JumpIfGreaterOrEqual); + compareMR(c, size, b, size, a); + append(c, bge(8)); + jumpC(c, vm::TargetBytesPerWord, target); + } else { + compareRM(c, size, a, size, b); + branch(c, op, target); + } +} + +void branchCM(Context* c, + lir::TernaryOperation op, + unsigned size, + lir::Constant* a, + lir::Memory* b, + lir::Constant* target) +{ + assertT(c, not isFloatBranch(op)); + assertT(c, size <= vm::TargetBytesPerWord); + + compareCM(c, size, a, size, b); + branch(c, op, target); +} + +ShiftMaskPromise* shiftMaskPromise(Context* c, + Promise* base, + unsigned shift, + int64_t mask) +{ + return new (c->zone) ShiftMaskPromise(base, shift, mask); +} + +void moveCM(Context* c, + unsigned srcSize, + lir::Constant* src, + unsigned dstSize, + lir::Memory* dst) +{ + lir::RegisterPair tmp(c->client->acquireTemporary(GPR_MASK)); + moveCR(c, srcSize, src, dstSize, &tmp); + moveRM(c, dstSize, &tmp, dstSize, dst); + c->client->releaseTemporary(tmp.low); +} + +void negateRR(Context* c, + unsigned srcSize, + lir::RegisterPair* src, + unsigned dstSize UNUSED, + lir::RegisterPair* dst) +{ + assertT(c, srcSize == dstSize); + + append(c, neg(dst->low, src->low, srcSize)); +} + +void callR(Context* c, unsigned size UNUSED, lir::RegisterPair* target) +{ + assertT(c, size == vm::TargetBytesPerWord); + append(c, blr(target->low)); +} + +void callC(Context* c, unsigned size UNUSED, lir::Constant* target) +{ + assertT(c, size == vm::TargetBytesPerWord); + + appendOffsetTask(c, target->value, offsetPromise(c)); + append(c, bl(0)); +} + +void longCallC(Context* c, unsigned size UNUSED, lir::Constant* target) +{ + assertT(c, size == vm::TargetBytesPerWord); + + lir::RegisterPair tmp( + Register(9)); // a non-arg reg that we don't mind clobbering + moveCR2(c, vm::TargetBytesPerWord, target, &tmp, offsetPromise(c)); + callR(c, vm::TargetBytesPerWord, &tmp); +} + +void longJumpC(Context* c, unsigned size UNUSED, lir::Constant* target) +{ + assertT(c, size == vm::TargetBytesPerWord); + + lir::RegisterPair tmp( + Register(9)); // a non-arg reg that we don't mind clobbering + moveCR2(c, vm::TargetBytesPerWord, target, &tmp, offsetPromise(c)); + jumpR(c, vm::TargetBytesPerWord, &tmp); +} + +void jumpC(Context* c, unsigned size UNUSED, lir::Constant* target) +{ + assertT(c, size == vm::TargetBytesPerWord); + + appendOffsetTask(c, target->value, offsetPromise(c)); + append(c, b(0)); +} + +void return_(Context* c) +{ + append(c, br(LinkRegister)); +} + +void trap(Context* c) +{ + append(c, brk(0)); +} + +// todo: determine the minimal operation types and domains needed to +// implement the following barriers (see +// http://community.arm.com/groups/processors/blog/2011/10/19/memory-access-ordering-part-3--memory-access-ordering-in-the-arm-architecture). +// For now, we just use DMB SY as a conservative but not necessarily +// performant choice. + +void memoryBarrier(Context* c) +{ + append(c, dmb(0xF)); +} + +void loadBarrier(Context* c) +{ + memoryBarrier(c); +} + +void storeStoreBarrier(Context* c) +{ + memoryBarrier(c); +} + +void storeLoadBarrier(Context* c) +{ + memoryBarrier(c); +} + +} // namespace arm +} // namespace codegen +} // namespace avian + +#endif // TARGET_BYTES_PER_WORD == 8 diff --git a/sgx-jvm/avian/src/codegen/target/arm/registers.h b/sgx-jvm/avian/src/codegen/target/arm/registers.h new file mode 100644 index 0000000000..07e377bd76 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/arm/registers.h @@ -0,0 +1,77 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_ARM_REGISTERS_H +#define AVIAN_CODEGEN_ASSEMBLER_ARM_REGISTERS_H + +#include +#include + +#include "avian/environment.h" + +namespace avian { +namespace codegen { +namespace arm { + +const uint64_t MASK_LO32 = 0xffffffff; +const unsigned MASK_LO8 = 0xff; + +#if TARGET_BYTES_PER_WORD == 8 +constexpr Register ThreadRegister(19); +constexpr Register StackRegister(31); +constexpr Register LinkRegister(30); +constexpr Register FrameRegister(29); +constexpr Register ProgramCounter(0xFE); // i.e. unaddressable + +const int N_GPRS = 32; +const int N_FPRS = 32; +const RegisterMask GPR_MASK = 0xffffffff; +const RegisterMask FPR_MASK = 0xffffffff00000000; + +#else +constexpr Register ThreadRegister(8); +constexpr Register StackRegister(13); +constexpr Register LinkRegister(14); +constexpr Register FrameRegister(0xFE); // i.e. there is none +constexpr Register ProgramCounter(15); + +const int N_GPRS = 16; +const int N_FPRS = 16; +const RegisterMask GPR_MASK = 0xffff; +const RegisterMask FPR_MASK = 0xffff0000; + +inline int fpr64(Register reg) +{ + return reg.index() - N_GPRS; +} +inline int fpr64(lir::RegisterPair* reg) +{ + return fpr64(reg->low); +} +inline int fpr32(Register reg) +{ + return fpr64(reg) << 1; +} +inline int fpr32(lir::RegisterPair* reg) +{ + return fpr64(reg) << 1; +} +#endif + +inline bool isFpr(lir::RegisterPair* reg) +{ + return reg->low.index() >= N_GPRS; +} + +} // namespace arm +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_ARM_REGISTERS_H diff --git a/sgx-jvm/avian/src/codegen/target/multimethod.h b/sgx-jvm/avian/src/codegen/target/multimethod.h new file mode 100644 index 0000000000..61f7a72318 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/multimethod.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_TARGET_MULTIMETHOD_H +#define AVIAN_CODEGEN_TARGET_MULTIMETHOD_H + +namespace avian { +namespace codegen { + +class Multimethod { + public: + inline static unsigned index(lir::UnaryOperation operation, + lir::Operand::Type operand) + { + return operation + (lir::UnaryOperationCount * (unsigned)operand); + } +}; + +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_TARGET_MULTIMETHOD_H diff --git a/sgx-jvm/avian/src/codegen/target/x86/CMakeLists.txt b/sgx-jvm/avian/src/codegen/target/x86/CMakeLists.txt new file mode 100644 index 0000000000..eb5d5b1554 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/x86/CMakeLists.txt @@ -0,0 +1,11 @@ +add_library(avian_codegen_x86 + assembler.cpp + block.cpp + context.cpp + detect.cpp + encode.cpp + fixup.cpp + multimethod.cpp + operations.cpp + padding.cpp +) diff --git a/sgx-jvm/avian/src/codegen/target/x86/assembler.cpp b/sgx-jvm/avian/src/codegen/target/x86/assembler.cpp new file mode 100644 index 0000000000..c8412dbc04 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/x86/assembler.cpp @@ -0,0 +1,1263 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include +#include + +#include "avian/environment.h" +#include "avian/target.h" +#include "avian/alloc-vector.h" +#include "avian/common.h" +#include "avian/util/allocator.h" +#include "avian/zone.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "context.h" +#include "block.h" +#include "fixup.h" +#include "padding.h" +#include "registers.h" +#include "operations.h" +#include "detect.h" +#include "multimethod.h" +#include "../multimethod.h" + +#define CAST1(x) reinterpret_cast(x) +#define CAST2(x) reinterpret_cast(x) +#define CAST_BRANCH(x) reinterpret_cast(x) + +using namespace vm; +using namespace avian::util; + +namespace avian { +namespace codegen { +namespace x86 { + +const unsigned FrameHeaderSize = (UseFramePointer ? 2 : 1); + +const unsigned StackAlignmentInBytes = 16; +const unsigned StackAlignmentInWords = StackAlignmentInBytes + / TargetBytesPerWord; + +unsigned argumentFootprint(unsigned footprint) +{ + return max(pad(footprint, StackAlignmentInWords), StackAlignmentInWords); +} + +uint32_t read4(uint8_t* p) +{ + uint32_t v; + memcpy(&v, p, 4); + return v; +} + +void nextFrame(ArchitectureContext* c UNUSED, + uint8_t* start, + unsigned size UNUSED, + unsigned footprint, + void*, + bool mostRecent, + int targetParameterFootprint, + void** ip, + void** stack) +{ + assertT(c, *ip >= start); + assertT(c, *ip <= start + size); + + uint8_t* instruction = static_cast(*ip); + + // skip stack overflow check, if present: + if (TargetBytesPerWord == 4) { + if (*start == 0x39) { + start += 12; + } + } else if (*start == 0x48 and start[1] == 0x39) { + start += 13; + } + + if (instruction <= start) { + assertT(c, mostRecent); + *ip = static_cast(*stack)[0]; + return; + } + + if (UseFramePointer) { + // skip preamble + start += (TargetBytesPerWord == 4 ? 3 : 4); + + if (instruction <= start or *instruction == 0x5d) { + assertT(c, mostRecent); + + *ip = static_cast(*stack)[1]; + *stack = static_cast(*stack) + 1; + return; + } + } + + if (*instruction == 0xc3) { // return + *ip = static_cast(*stack)[0]; + return; + } + + unsigned offset = footprint + FrameHeaderSize - (mostRecent ? 1 : 0); + + if (TailCalls and targetParameterFootprint >= 0) { + if (argumentFootprint(targetParameterFootprint) > StackAlignmentInWords) { + offset += argumentFootprint(targetParameterFootprint) + - StackAlignmentInWords; + } + + // check for post-non-tail-call stack adjustment of the form "sub + // $offset,%rsp": + if (TargetBytesPerWord == 4) { + if ((*instruction == 0x83 or *instruction == 0x81) + and instruction[1] == 0xec) { + offset + -= (*instruction == 0x83 ? instruction[2] : read4(instruction + 2)) + / TargetBytesPerWord; + } + } else if (*instruction == 0x48 + and (instruction[1] == 0x83 or instruction[1] == 0x81) + and instruction[2] == 0xec) { + offset + -= (instruction[1] == 0x83 ? instruction[3] : read4(instruction + 3)) + / TargetBytesPerWord; + } + + // todo: check for and handle tail calls + } + + if (UseFramePointer and not mostRecent) { + assertT(c, + static_cast(*stack)[-1] + 1 + == static_cast(*stack) + offset); + + assertT(c, + static_cast(*stack)[-1][1] + == static_cast(*stack)[offset]); + } + + *ip = static_cast(*stack)[offset]; + *stack = static_cast(*stack) + offset; +} + +class MyArchitecture : public Architecture { + public: + MyArchitecture(System* system, bool useNativeFeatures) + : c(system, useNativeFeatures), + referenceCount(0), + myRegisterFile(GeneralRegisterMask, useSSE(&c) ? FloatRegisterMask : 0) + { + populateTables(&c); + } + + virtual unsigned floatRegisterSize() + { + if (useSSE(&c)) { + return 8; + } else { + return 0; + } + } + + virtual const RegisterFile* registerFile() + { + return &myRegisterFile; + } + + virtual Register scratch() + { + return rax; + } + + virtual Register stack() + { + return rsp; + } + + virtual Register thread() + { + return rbx; + } + + virtual Register returnLow() + { + return rax; + } + + virtual Register returnHigh() + { + return (TargetBytesPerWord == 4 ? rdx : NoRegister); + } + + virtual Register virtualCallTarget() + { + return rax; + } + + virtual Register virtualCallIndex() + { + return rdx; + } + + virtual ir::TargetInfo targetInfo() + { + return ir::TargetInfo(TargetBytesPerWord); + } + + virtual bool bigEndian() + { + return false; + } + + virtual uintptr_t maximumImmediateJump() + { + return 0x7FFFFFFF; + } + + virtual bool reserved(Register register_) + { + switch (register_.index()) { + case rbp.index(): + return UseFramePointer; + + case rsp.index(): + case rbx.index(): + return true; + + default: + return false; + } + } + + virtual unsigned frameFootprint(unsigned footprint) + { +#if AVIAN_TARGET_FORMAT == AVIAN_FORMAT_PE + return max(footprint, StackAlignmentInWords); +#else + return max(footprint > argumentRegisterCount() + ? footprint - argumentRegisterCount() + : 0, + StackAlignmentInWords); +#endif + } + + virtual unsigned argumentFootprint(unsigned footprint) + { + return x86::argumentFootprint(footprint); + } + + virtual bool argumentAlignment() + { + return false; + } + + virtual bool argumentRegisterAlignment() + { + return false; + } + + virtual unsigned argumentRegisterCount() + { +#if AVIAN_TARGET_FORMAT == AVIAN_FORMAT_PE + if (TargetBytesPerWord == 8) + return 4; + else +#else + if (TargetBytesPerWord == 8) + return 6; + else +#endif + return 0; + } + + virtual Register argumentRegister(unsigned index) + { + assertT(&c, TargetBytesPerWord == 8); + switch (index) { +#if AVIAN_TARGET_FORMAT == AVIAN_FORMAT_PE + case 0: + return rcx; + case 1: + return rdx; + case 2: + return r8; + case 3: + return r9; +#else + case 0: + return rdi; + case 1: + return rsi; + case 2: + return rdx; + case 3: + return rcx; + case 4: + return r8; + case 5: + return r9; +#endif + default: + abort(&c); + } + } + + virtual bool hasLinkRegister() + { + return false; + } + + virtual unsigned stackAlignmentInWords() + { + return StackAlignmentInWords; + } + + virtual bool matchCall(void* returnAddress, void* target) + { + uint8_t* instruction = static_cast(returnAddress) - 5; + int32_t actualOffset; + memcpy(&actualOffset, instruction + 1, 4); + void* actualTarget = static_cast(returnAddress) + actualOffset; + + return *instruction == 0xE8 and actualTarget == target; + } + + virtual void updateCall(lir::UnaryOperation op, + void* returnAddress, + void* newTarget) + { + bool assertTAlignment UNUSED; + switch (op) { + case lir::AlignedCall: + op = lir::Call; + assertTAlignment = true; + break; + + case lir::AlignedJump: + op = lir::Jump; + assertTAlignment = true; + break; + + case lir::AlignedLongCall: + op = lir::LongCall; + assertTAlignment = true; + break; + + case lir::AlignedLongJump: + op = lir::LongJump; + assertTAlignment = true; + break; + + default: + assertTAlignment = false; + } + + if (TargetBytesPerWord == 4 or op == lir::Call or op == lir::Jump) { + uint8_t* instruction = static_cast(returnAddress) - 5; + + assertT( + &c, + ((op == lir::Call or op == lir::LongCall) and *instruction == 0xE8) + or ((op == lir::Jump or op == lir::LongJump) + and *instruction == 0xE9)); + + assertT(&c, + (not assertTAlignment) + or reinterpret_cast(instruction + 1) % 4 == 0); + + intptr_t v = static_cast(newTarget) + - static_cast(returnAddress); + + assertT(&c, vm::fitsInInt32(v)); + + int32_t v32 = v; + + memcpy(instruction + 1, &v32, 4); + } else { + uint8_t* instruction = static_cast(returnAddress) - 13; + + assertT(&c, instruction[0] == 0x49 and instruction[1] == 0xBA); + assertT(&c, instruction[10] == 0x41 and instruction[11] == 0xFF); + assertT(&c, + (op == lir::LongCall and instruction[12] == 0xD2) + or (op == lir::LongJump and instruction[12] == 0xE2)); + + assertT(&c, + (not assertTAlignment) + or reinterpret_cast(instruction + 2) % 8 == 0); + + memcpy(instruction + 2, &newTarget, 8); + } + } + + virtual void setConstant(void* dst, uint64_t constant) + { + target_uintptr_t v = targetVW(constant); + memcpy(dst, &v, TargetBytesPerWord); + } + + virtual unsigned alignFrameSize(unsigned sizeInWords) + { + return pad(sizeInWords + FrameHeaderSize, StackAlignmentInWords) + - FrameHeaderSize; + } + + virtual void nextFrame(void* start, + unsigned size, + unsigned footprint, + void* link, + bool mostRecent, + int targetParameterFootprint, + void** ip, + void** stack) + { + x86::nextFrame(&c, + static_cast(start), + size, + footprint, + link, + mostRecent, + targetParameterFootprint, + ip, + stack); + } + + virtual void* frameIp(void* stack) + { + return stack ? *static_cast(stack) : 0; + } + + virtual unsigned frameHeaderSize() + { + return FrameHeaderSize; + } + + virtual unsigned frameReturnAddressSize() + { + return 1; + } + + virtual unsigned frameFooterSize() + { + return 0; + } + + virtual bool alwaysCondensed(lir::BinaryOperation op) + { + switch (op) { + case lir::Float2Float: + case lir::Float2Int: + case lir::Int2Float: + case lir::FloatAbsolute: + case lir::FloatNegate: + case lir::FloatSquareRoot: + return false; + + case lir::Negate: + case lir::Absolute: + return true; + + default: + abort(&c); + } + } + + virtual bool alwaysCondensed(lir::TernaryOperation) + { + return true; + } + + virtual int returnAddressOffset() + { + return 0; + } + + virtual int framePointerOffset() + { + return UseFramePointer ? -1 : 0; + } + + virtual void plan(lir::UnaryOperation, + unsigned, + OperandMask& aMask, + bool* thunk) + { + aMask.typeMask = lir::Operand::RegisterPairMask | lir::Operand::MemoryMask + | lir::Operand::ConstantMask; + *thunk = false; + } + + virtual void planSource(lir::BinaryOperation op, + unsigned aSize, + OperandMask& aMask, + unsigned bSize, + bool* thunk) + { + aMask.setLowHighRegisterMasks(GeneralRegisterMask, GeneralRegisterMask); + + *thunk = false; + + switch (op) { + case lir::Negate: + aMask.typeMask = lir::Operand::RegisterPairMask; + aMask.setLowHighRegisterMasks(rax, rdx); + break; + + case lir::Absolute: + if (aSize <= TargetBytesPerWord) { + aMask.typeMask = lir::Operand::RegisterPairMask; + aMask.setLowHighRegisterMasks(rax, 0); + } else { + *thunk = true; + } + break; + + case lir::FloatAbsolute: + if (useSSE(&c)) { + aMask.typeMask = lir::Operand::RegisterPairMask; + aMask.setLowHighRegisterMasks(FloatRegisterMask, FloatRegisterMask); + } else { + *thunk = true; + } + break; + + case lir::FloatNegate: + // floatNegateRR does not support doubles + if (useSSE(&c) and aSize == 4 and bSize == 4) { + aMask.typeMask = lir::Operand::RegisterPairMask; + aMask.setLowHighRegisterMasks(FloatRegisterMask, 0); + } else { + *thunk = true; + } + break; + + case lir::FloatSquareRoot: + if (useSSE(&c)) { + aMask.typeMask = lir::Operand::RegisterPairMask + | lir::Operand::MemoryMask; + aMask.setLowHighRegisterMasks(FloatRegisterMask, FloatRegisterMask); + } else { + *thunk = true; + } + break; + + case lir::Float2Float: + if (useSSE(&c)) { + aMask.typeMask = lir::Operand::RegisterPairMask + | lir::Operand::MemoryMask; + aMask.setLowHighRegisterMasks(FloatRegisterMask, FloatRegisterMask); + } else { + *thunk = true; + } + break; + + case lir::Float2Int: + // todo: Java requires different semantics than SSE for + // converting floats to integers, we we need to either use + // thunks or produce inline machine code which handles edge + // cases properly. + if (false and useSSE(&c) and bSize <= TargetBytesPerWord) { + aMask.typeMask = lir::Operand::RegisterPairMask + | lir::Operand::MemoryMask; + aMask.setLowHighRegisterMasks(FloatRegisterMask, FloatRegisterMask); + } else { + *thunk = true; + } + break; + + case lir::Int2Float: + if (useSSE(&c) and aSize <= TargetBytesPerWord) { + aMask.typeMask = lir::Operand::RegisterPairMask + | lir::Operand::MemoryMask; + aMask.setLowHighRegisterMasks(GeneralRegisterMask, GeneralRegisterMask); + } else { + *thunk = true; + } + break; + + case lir::Move: + aMask.typeMask = ~0; + aMask.setLowHighRegisterMasks(AnyRegisterMask, AnyRegisterMask); + + if (TargetBytesPerWord == 4) { + if (aSize == 4 and bSize == 8) { + aMask.typeMask = lir::Operand::RegisterPairMask + | lir::Operand::MemoryMask; + const RegisterMask mask = GeneralRegisterMask + .excluding(rax).excluding(rdx); + aMask.setLowHighRegisterMasks(mask, mask); + } else if (aSize == 1 or bSize == 1) { + aMask.typeMask = lir::Operand::RegisterPairMask + | lir::Operand::MemoryMask; + const RegisterMask mask = rax | rcx | rdx | rbx; + aMask.setLowHighRegisterMasks(mask, mask); + } + } + break; + + default: + break; + } + } + + virtual void planDestination(lir::BinaryOperation op, + unsigned aSize, + const OperandMask& aMask, + unsigned bSize, + OperandMask& bMask) + { + bMask.typeMask = ~0; + bMask.setLowHighRegisterMasks(GeneralRegisterMask, GeneralRegisterMask); + + switch (op) { + case lir::Absolute: + bMask.typeMask = lir::Operand::RegisterPairMask; + bMask.setLowHighRegisterMasks(rax, 0); + break; + + case lir::FloatAbsolute: + bMask.typeMask = lir::Operand::RegisterPairMask; + bMask.lowRegisterMask = aMask.lowRegisterMask; + bMask.highRegisterMask = aMask.highRegisterMask; + break; + + case lir::Negate: + bMask.typeMask = lir::Operand::RegisterPairMask; + bMask.lowRegisterMask = aMask.lowRegisterMask; + bMask.highRegisterMask = aMask.highRegisterMask; + break; + + case lir::FloatNegate: + case lir::FloatSquareRoot: + case lir::Float2Float: + case lir::Int2Float: + bMask.typeMask = lir::Operand::RegisterPairMask; + bMask.setLowHighRegisterMasks(FloatRegisterMask, FloatRegisterMask); + break; + + case lir::Float2Int: + bMask.typeMask = lir::Operand::RegisterPairMask; + break; + + case lir::Move: + if (aMask.typeMask + & (lir::Operand::MemoryMask | lir::Operand::AddressMask)) { + bMask.typeMask = lir::Operand::RegisterPairMask; + bMask.setLowHighRegisterMasks(GeneralRegisterMask | FloatRegisterMask, GeneralRegisterMask); + } else if (aMask.typeMask & lir::Operand::RegisterPairMask) { + bMask.typeMask = lir::Operand::RegisterPairMask + | lir::Operand::MemoryMask; + if (aMask.lowRegisterMask & FloatRegisterMask) { + bMask.setLowHighRegisterMasks(FloatRegisterMask, 0); + } else { + bMask.setLowHighRegisterMasks(GeneralRegisterMask, GeneralRegisterMask); + } + } else { + bMask.typeMask = lir::Operand::RegisterPairMask + | lir::Operand::MemoryMask; + } + + if (TargetBytesPerWord == 4) { + if (aSize == 4 and bSize == 8) { + bMask.setLowHighRegisterMasks(rax, rdx); + } else if (aSize == 1 or bSize == 1) { + const RegisterMask mask = rax | rcx | rdx | rbx; + bMask.setLowHighRegisterMasks(mask, mask); + } + } + break; + + default: + break; + } + } + + virtual void planMove(unsigned size, + OperandMask& srcMask, + OperandMask& tmpMask, + const OperandMask& dstMask) + { + srcMask.typeMask = ~0; + srcMask.setLowHighRegisterMasks(AnyRegisterMask, AnyRegisterMask); + + tmpMask.typeMask = 0; + tmpMask.setLowHighRegisterMasks(0, 0); + + if (dstMask.typeMask & lir::Operand::MemoryMask) { + // can't move directly from memory to memory + srcMask.typeMask = lir::Operand::RegisterPairMask + | lir::Operand::ConstantMask; + tmpMask.typeMask = lir::Operand::RegisterPairMask; + tmpMask.setLowHighRegisterMasks(GeneralRegisterMask, GeneralRegisterMask); + } else if (dstMask.typeMask & lir::Operand::RegisterPairMask) { + if (size > TargetBytesPerWord) { + // can't move directly from FPR to GPR or vice-versa for + // values larger than the GPR size + if (dstMask.lowRegisterMask & FloatRegisterMask) { + srcMask.setLowHighRegisterMasks(FloatRegisterMask, FloatRegisterMask); + tmpMask.typeMask = lir::Operand::MemoryMask; + } else if (dstMask.lowRegisterMask & GeneralRegisterMask) { + srcMask.setLowHighRegisterMasks(GeneralRegisterMask, GeneralRegisterMask); + tmpMask.typeMask = lir::Operand::MemoryMask; + } + } + if (dstMask.lowRegisterMask & FloatRegisterMask) { + // can't move directly from constant to FPR + srcMask.typeMask &= ~lir::Operand::ConstantMask; + if (size > TargetBytesPerWord) { + tmpMask.typeMask = lir::Operand::MemoryMask; + } else { + tmpMask.typeMask = lir::Operand::RegisterPairMask + | lir::Operand::MemoryMask; + tmpMask.setLowHighRegisterMasks(GeneralRegisterMask, GeneralRegisterMask); + } + } + } + } + + virtual void planSource(lir::TernaryOperation op, + unsigned aSize, + OperandMask& aMask, + unsigned bSize, + OperandMask& bMask, + unsigned, + bool* thunk) + { + aMask.typeMask = lir::Operand::RegisterPairMask | lir::Operand::ConstantMask; + aMask.setLowHighRegisterMasks(GeneralRegisterMask, GeneralRegisterMask); + + bMask.typeMask = lir::Operand::RegisterPairMask; + bMask.setLowHighRegisterMasks(GeneralRegisterMask, GeneralRegisterMask); + + *thunk = false; + + switch (op) { + case lir::FloatAdd: + case lir::FloatSubtract: + case lir::FloatMultiply: + case lir::FloatDivide: + if (useSSE(&c)) { + aMask.typeMask = lir::Operand::RegisterPairMask + | lir::Operand::MemoryMask; + bMask.typeMask = lir::Operand::RegisterPairMask; + + aMask.setLowHighRegisterMasks(FloatRegisterMask, FloatRegisterMask); + bMask.setLowHighRegisterMasks(FloatRegisterMask, FloatRegisterMask); + } else { + *thunk = true; + } + break; + + case lir::FloatRemainder: + *thunk = true; + break; + + case lir::Multiply: + if (TargetBytesPerWord == 4 and aSize == 8) { + const RegisterMask mask = GeneralRegisterMask .excluding(rax).excluding(rdx); + aMask.setLowHighRegisterMasks(mask, mask); + bMask.setLowHighRegisterMasks(mask, rdx); + } else { + aMask.setLowHighRegisterMasks(GeneralRegisterMask, 0); + bMask.setLowHighRegisterMasks(GeneralRegisterMask, 0); + } + break; + + case lir::Divide: + if (TargetBytesPerWord == 4 and aSize == 8) { + *thunk = true; + } else { + aMask.typeMask = lir::Operand::RegisterPairMask; + aMask.setLowHighRegisterMasks(GeneralRegisterMask .excluding(rax).excluding(rdx), 0); + bMask.setLowHighRegisterMasks(rax, 0); + } + break; + + case lir::Remainder: + if (TargetBytesPerWord == 4 and aSize == 8) { + *thunk = true; + } else { + aMask.typeMask = lir::Operand::RegisterPairMask; + aMask.setLowHighRegisterMasks(GeneralRegisterMask .excluding(rax).excluding(rdx), 0); + bMask.setLowHighRegisterMasks(rax, 0); + } + break; + + case lir::ShiftLeft: + case lir::ShiftRight: + case lir::UnsignedShiftRight: { + if (TargetBytesPerWord == 4 and bSize == 8) { + const RegisterMask mask = GeneralRegisterMask.excluding(rcx); + aMask.setLowHighRegisterMasks(mask, mask); + bMask.setLowHighRegisterMasks(mask, mask); + } else { + aMask.setLowHighRegisterMasks(rcx, GeneralRegisterMask); + const RegisterMask mask = GeneralRegisterMask.excluding(rcx); + bMask.setLowHighRegisterMasks(mask, mask); + } + } break; + + case lir::JumpIfFloatEqual: + case lir::JumpIfFloatNotEqual: + case lir::JumpIfFloatLess: + case lir::JumpIfFloatGreater: + case lir::JumpIfFloatLessOrEqual: + case lir::JumpIfFloatGreaterOrEqual: + case lir::JumpIfFloatLessOrUnordered: + case lir::JumpIfFloatGreaterOrUnordered: + case lir::JumpIfFloatLessOrEqualOrUnordered: + case lir::JumpIfFloatGreaterOrEqualOrUnordered: + if (useSSE(&c)) { + aMask.typeMask = lir::Operand::RegisterPairMask; + aMask.setLowHighRegisterMasks(FloatRegisterMask, FloatRegisterMask); + bMask.typeMask = aMask.typeMask; + bMask.lowRegisterMask = aMask.lowRegisterMask; + bMask.highRegisterMask = aMask.highRegisterMask; + } else { + *thunk = true; + } + break; + + default: + break; + } + } + + virtual void planDestination(lir::TernaryOperation op, + unsigned, + const OperandMask&, + unsigned, + const OperandMask& bMask, + unsigned, + OperandMask& cMask) + { + if (isBranch(op)) { + cMask.typeMask = lir::Operand::ConstantMask; + cMask.setLowHighRegisterMasks(0, 0); + } else { + cMask.typeMask = lir::Operand::RegisterPairMask; + cMask.lowRegisterMask = bMask.lowRegisterMask; + cMask.highRegisterMask = bMask.highRegisterMask; + } + } + + virtual Assembler* makeAssembler(util::Alloc* allocator, Zone* zone); + + virtual void acquire() + { + ++referenceCount; + } + + virtual void release() + { + if (--referenceCount == 0) { + c.s->free(this); + } + } + + ArchitectureContext c; + unsigned referenceCount; + const RegisterFile myRegisterFile; +}; + +class MyAssembler : public Assembler { + public: + MyAssembler(System* s, util::Alloc* a, Zone* zone, MyArchitecture* arch) + : c(s, a, zone, &(arch->c)), arch_(arch) + { + } + + virtual void setClient(Client* client) + { + assertT(&c, c.client == 0); + c.client = client; + } + + virtual Architecture* arch() + { + return arch_; + } + + virtual void checkStackOverflow(uintptr_t handler, + unsigned stackLimitOffsetFromThread) + { + lir::RegisterPair stack(rsp); + lir::Memory stackLimit(rbx, stackLimitOffsetFromThread); + lir::Constant handlerConstant(resolvedPromise(&c, handler)); + branchRM(&c, + lir::JumpIfGreaterOrEqual, + TargetBytesPerWord, + &stack, + &stackLimit, + &handlerConstant); + } + + virtual void saveFrame(unsigned stackOffset, unsigned) + { + lir::RegisterPair stack(rsp); + lir::Memory stackDst(rbx, stackOffset); + apply(lir::Move, + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &stack), + OperandInfo(TargetBytesPerWord, lir::Operand::Type::Memory, &stackDst)); + } + + virtual void pushFrame(unsigned argumentCount, ...) + { + // TODO: Argument should be replaced by OperandInfo... + struct Argument { + unsigned size; + lir::Operand::Type type; + lir::Operand* operand; + }; + RUNTIME_ARRAY(Argument, arguments, argumentCount); + va_list a; + va_start(a, argumentCount); + unsigned footprint = 0; + for (unsigned i = 0; i < argumentCount; ++i) { + RUNTIME_ARRAY_BODY(arguments)[i].size = va_arg(a, unsigned); + RUNTIME_ARRAY_BODY(arguments)[i].type + = static_cast(va_arg(a, int)); + RUNTIME_ARRAY_BODY(arguments)[i].operand = va_arg(a, lir::Operand*); + footprint += ceilingDivide(RUNTIME_ARRAY_BODY(arguments)[i].size, + TargetBytesPerWord); + } + va_end(a); + + allocateFrame(arch_->alignFrameSize(footprint)); + + unsigned offset = 0; + for (unsigned i = 0; i < argumentCount; ++i) { + if (i < arch_->argumentRegisterCount()) { + lir::RegisterPair dst(arch_->argumentRegister(i)); + apply(lir::Move, + OperandInfo(RUNTIME_ARRAY_BODY(arguments)[i].size, + RUNTIME_ARRAY_BODY(arguments)[i].type, + RUNTIME_ARRAY_BODY(arguments)[i].operand), + OperandInfo(pad(RUNTIME_ARRAY_BODY(arguments)[i].size, + TargetBytesPerWord), + lir::Operand::Type::RegisterPair, + &dst)); + } else { + lir::Memory dst(rsp, offset * TargetBytesPerWord); + apply(lir::Move, + OperandInfo(RUNTIME_ARRAY_BODY(arguments)[i].size, + RUNTIME_ARRAY_BODY(arguments)[i].type, + RUNTIME_ARRAY_BODY(arguments)[i].operand), + OperandInfo(pad(RUNTIME_ARRAY_BODY(arguments)[i].size, + TargetBytesPerWord), + lir::Operand::Type::Memory, + &dst)); + offset += ceilingDivide(RUNTIME_ARRAY_BODY(arguments)[i].size, + TargetBytesPerWord); + } + } + } + + virtual void allocateFrame(unsigned footprint) + { + lir::RegisterPair stack(rsp); + + if (UseFramePointer) { + lir::RegisterPair base(rbp); + pushR(&c, TargetBytesPerWord, &base); + + apply(lir::Move, + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &stack), + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &base)); + } + + lir::Constant footprintConstant( + resolvedPromise(&c, footprint * TargetBytesPerWord)); + apply(lir::Subtract, + OperandInfo( + TargetBytesPerWord, lir::Operand::Type::Constant, &footprintConstant), + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &stack), + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &stack)); + } + + virtual void adjustFrame(unsigned difference) + { + lir::RegisterPair stack(rsp); + lir::Constant differenceConstant( + resolvedPromise(&c, difference * TargetBytesPerWord)); + apply(lir::Subtract, + OperandInfo( + TargetBytesPerWord, lir::Operand::Type::Constant, &differenceConstant), + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &stack), + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &stack)); + } + + virtual void popFrame(unsigned frameFootprint) + { + if (UseFramePointer) { + lir::RegisterPair base(rbp); + lir::RegisterPair stack(rsp); + apply(lir::Move, + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &base), + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &stack)); + + popR(&c, TargetBytesPerWord, &base); + } else { + lir::RegisterPair stack(rsp); + lir::Constant footprint( + resolvedPromise(&c, frameFootprint * TargetBytesPerWord)); + apply(lir::Add, + OperandInfo(TargetBytesPerWord, lir::Operand::Type::Constant, &footprint), + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &stack), + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &stack)); + } + } + + virtual void popFrameForTailCall(unsigned frameFootprint, + int offset, + Register returnAddressSurrogate, + Register framePointerSurrogate) + { + if (TailCalls) { + if (offset) { + lir::RegisterPair tmp(c.client->acquireTemporary()); + + unsigned baseSize = UseFramePointer ? 1 : 0; + + lir::Memory returnAddressSrc( + rsp, (frameFootprint + baseSize) * TargetBytesPerWord); + moveMR(&c, + TargetBytesPerWord, + &returnAddressSrc, + TargetBytesPerWord, + &tmp); + + lir::Memory returnAddressDst( + rsp, (frameFootprint - offset + baseSize) * TargetBytesPerWord); + moveRM(&c, + TargetBytesPerWord, + &tmp, + TargetBytesPerWord, + &returnAddressDst); + + c.client->releaseTemporary(tmp.low); + + if (UseFramePointer) { + lir::Memory baseSrc(rsp, frameFootprint * TargetBytesPerWord); + lir::RegisterPair base(rbp); + moveMR(&c, TargetBytesPerWord, &baseSrc, TargetBytesPerWord, &base); + } + + lir::RegisterPair stack(rsp); + lir::Constant footprint(resolvedPromise( + &c, (frameFootprint - offset + baseSize) * TargetBytesPerWord)); + + addCR(&c, TargetBytesPerWord, &footprint, TargetBytesPerWord, &stack); + + if (returnAddressSurrogate != NoRegister) { + assertT(&c, offset > 0); + + lir::RegisterPair ras(returnAddressSurrogate); + lir::Memory dst(rsp, offset * TargetBytesPerWord); + moveRM(&c, TargetBytesPerWord, &ras, TargetBytesPerWord, &dst); + } + + if (framePointerSurrogate != NoRegister) { + assertT(&c, offset > 0); + + lir::RegisterPair fps(framePointerSurrogate); + lir::Memory dst(rsp, (offset - 1) * TargetBytesPerWord); + moveRM(&c, TargetBytesPerWord, &fps, TargetBytesPerWord, &dst); + } + } else { + popFrame(frameFootprint); + } + } else { + abort(&c); + } + } + + virtual void popFrameAndPopArgumentsAndReturn(unsigned frameFootprint, + unsigned argumentFootprint) + { + popFrame(frameFootprint); + + assertT(&c, argumentFootprint >= StackAlignmentInWords); + assertT(&c, (argumentFootprint % StackAlignmentInWords) == 0); + + if (TailCalls and argumentFootprint > StackAlignmentInWords) { + lir::RegisterPair returnAddress(rcx); + popR(&c, TargetBytesPerWord, &returnAddress); + + lir::RegisterPair stack(rsp); + lir::Constant adjustment(resolvedPromise( + &c, + (argumentFootprint - StackAlignmentInWords) * TargetBytesPerWord)); + addCR(&c, TargetBytesPerWord, &adjustment, TargetBytesPerWord, &stack); + + jumpR(&c, TargetBytesPerWord, &returnAddress); + } else { + return_(&c); + } + } + + virtual void popFrameAndUpdateStackAndReturn(unsigned frameFootprint, + unsigned stackOffsetFromThread) + { + popFrame(frameFootprint); + + lir::RegisterPair returnAddress(rcx); + popR(&c, TargetBytesPerWord, &returnAddress); + + lir::RegisterPair stack(rsp); + lir::Memory stackSrc(rbx, stackOffsetFromThread); + moveMR(&c, TargetBytesPerWord, &stackSrc, TargetBytesPerWord, &stack); + + jumpR(&c, TargetBytesPerWord, &returnAddress); + } + + virtual void apply(lir::Operation op) + { + arch_->c.operations[op](&c); + } + + virtual void apply(lir::UnaryOperation op, OperandInfo a) + { + arch_->c.unaryOperations[Multimethod::index(op, a.type)]( + &c, a.size, a.operand); + } + + virtual void apply(lir::BinaryOperation op, OperandInfo a, OperandInfo b) + { + arch_->c.binaryOperations[index(&(arch_->c), op, a.type, b.type)]( + &c, a.size, a.operand, b.size, b.operand); + } + + virtual void apply(lir::TernaryOperation op, + OperandInfo a, + OperandInfo b, + OperandInfo c) + { + if (isBranch(op)) { + assertT(&this->c, a.size == b.size); + assertT(&this->c, c.size == TargetBytesPerWord); + assertT(&this->c, c.type == lir::Operand::Type::Constant); + + arch_->c.branchOperations[branchIndex(&(arch_->c), a.type, b.type)]( + &this->c, op, a.size, a.operand, b.operand, c.operand); + } else { + assertT(&this->c, b.size == c.size); + assertT(&this->c, b.type == c.type); + + arch_->c.binaryOperations[index(&(arch_->c), op, a.type, b.type)]( + &this->c, a.size, a.operand, b.size, b.operand); + } + } + + virtual void setDestination(uint8_t* dst) + { + c.result = dst; + } + + virtual void write() + { + uint8_t* dst = c.result; + for (MyBlock* b = c.firstBlock; b; b = b->next) { + unsigned index = 0; + unsigned padding = 0; + for (AlignmentPadding* p = b->firstPadding; p; p = p->next) { + unsigned size = p->offset - b->offset - index; + + memcpy(dst + b->start + index + padding, + c.code.data.begin() + b->offset + index, + size); + + index += size; + + while ((b->start + index + padding + p->instructionOffset) + % p->alignment) { + *(dst + b->start + index + padding) = 0x90; + ++padding; + } + } + + memcpy(dst + b->start + index + padding, + c.code.data.begin() + b->offset + index, + b->size - index); + } + + for (Task* t = c.tasks; t; t = t->next) { + t->run(&c); + } + } + + virtual Promise* offset(bool) + { + return x86::offsetPromise(&c); + } + + virtual Block* endBlock(bool startNew) + { + MyBlock* b = c.lastBlock; + b->size = c.code.length() - b->offset; + if (startNew) { + c.lastBlock = new (c.zone) MyBlock(c.code.length()); + } else { + c.lastBlock = 0; + } + return b; + } + + virtual void endEvent() + { + // ignore + } + + virtual unsigned length() + { + return c.code.length(); + } + + virtual unsigned footerSize() + { + return 0; + } + + virtual void dispose() + { + c.code.dispose(); + } + + Context c; + MyArchitecture* arch_; +}; + +Assembler* MyArchitecture::makeAssembler(util::Alloc* allocator, Zone* zone) +{ + return new (zone) MyAssembler(c.s, allocator, zone, this); +} + +} // namespace x86 + +Architecture* makeArchitectureX86(System* system, bool useNativeFeatures) +{ + return new (allocate(system, sizeof(x86::MyArchitecture))) + x86::MyArchitecture(system, useNativeFeatures); +} + +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/target/x86/block.cpp b/sgx-jvm/avian/src/codegen/target/x86/block.cpp new file mode 100644 index 0000000000..5d2d46d6fe --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/x86/block.cpp @@ -0,0 +1,44 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "block.h" + +#include + +namespace avian { +namespace codegen { +namespace x86 { + +unsigned padding(AlignmentPadding* p, + unsigned index, + unsigned offset, + AlignmentPadding* limit); + +MyBlock::MyBlock(unsigned offset) + : next(0), + firstPadding(0), + lastPadding(0), + offset(offset), + start(~0), + size(0) +{ +} + +unsigned MyBlock::resolve(unsigned start, Assembler::Block* next) +{ + this->start = start; + this->next = static_cast(next); + + return start + size + padding(firstPadding, start, offset, lastPadding); +} + +} // namespace x86 +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/target/x86/block.h b/sgx-jvm/avian/src/codegen/target/x86/block.h new file mode 100644 index 0000000000..6841f5d577 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/x86/block.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_X86_BLOCK_H +#define AVIAN_CODEGEN_ASSEMBLER_X86_BLOCK_H + +#include + +namespace avian { +namespace codegen { +namespace x86 { + +class AlignmentPadding; + +class MyBlock : public Assembler::Block { + public: + MyBlock(unsigned offset); + + virtual unsigned resolve(unsigned start, Assembler::Block* next); + + MyBlock* next; + AlignmentPadding* firstPadding; + AlignmentPadding* lastPadding; + unsigned offset; + unsigned start; + unsigned size; +}; + +} // namespace x86 +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_X86_BLOCK_H diff --git a/sgx-jvm/avian/src/codegen/target/x86/context.cpp b/sgx-jvm/avian/src/codegen/target/x86/context.cpp new file mode 100644 index 0000000000..37d742c47c --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/x86/context.cpp @@ -0,0 +1,44 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/util/allocator.h" +#include "avian/zone.h" + +#include "context.h" +#include "block.h" + +namespace avian { +namespace codegen { +namespace x86 { + +ArchitectureContext::ArchitectureContext(vm::System* s, bool useNativeFeatures) + : s(s), useNativeFeatures(useNativeFeatures) +{ +} + +Context::Context(vm::System* s, + util::Alloc* a, + vm::Zone* zone, + ArchitectureContext* ac) + : s(s), + zone(zone), + client(0), + code(s, a, 1024), + tasks(0), + result(0), + firstBlock(new (zone) MyBlock(0)), + lastBlock(firstBlock), + ac(ac) +{ +} + +} // namespace x86 +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/target/x86/context.h b/sgx-jvm/avian/src/codegen/target/x86/context.h new file mode 100644 index 0000000000..ac8d267012 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/x86/context.h @@ -0,0 +1,112 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_X86_CONTEXT_H +#define AVIAN_CODEGEN_ASSEMBLER_X86_CONTEXT_H + +#define CAST1(x) reinterpret_cast(x) +#define CAST2(x) reinterpret_cast(x) +#define CAST_BRANCH(x) reinterpret_cast(x) + +#include + +#include "avian/alloc-vector.h" + +#include +#include + +#include + +namespace vm { +class System; +class Alloc; +class Zone; +} // namespace vm + +namespace avian { + +namespace util { +class Aborter; +} // namespace util + +namespace codegen { +namespace x86 { + +class Context; +class MyBlock; +class Task; + +typedef void (*OperationType)(Context*); + +typedef void (*UnaryOperationType)(Context*, unsigned, lir::Operand*); + +typedef void (*BinaryOperationType)(Context*, + unsigned, + lir::Operand*, + unsigned, + lir::Operand*); + +typedef void (*BranchOperationType)(Context*, + lir::TernaryOperation, + unsigned, + lir::Operand*, + lir::Operand*, + lir::Operand*); + +class ArchitectureContext { + public: + ArchitectureContext(vm::System* s, bool useNativeFeatures); + + vm::System* s; + bool useNativeFeatures; + OperationType operations[lir::OperationCount]; + UnaryOperationType + unaryOperations[lir::UnaryOperationCount * lir::Operand::TypeCount]; + BinaryOperationType binaryOperations + [(lir::BinaryOperationCount + lir::NonBranchTernaryOperationCount) + * lir::Operand::TypeCount * lir::Operand::TypeCount]; + BranchOperationType branchOperations[lir::BranchOperationCount + * lir::Operand::TypeCount + * lir::Operand::TypeCount]; +}; + +class Context { + public: + Context(vm::System* s, + util::Alloc* a, + vm::Zone* zone, + ArchitectureContext* ac); + + vm::System* s; + vm::Zone* zone; + Assembler::Client* client; + vm::Vector code; + Task* tasks; + uint8_t* result; + MyBlock* firstBlock; + MyBlock* lastBlock; + ArchitectureContext* ac; +}; + +inline avian::util::Aborter* getAborter(Context* c) +{ + return c->s; +} + +inline avian::util::Aborter* getAborter(ArchitectureContext* c) +{ + return c->s; +} + +} // namespace x86 +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_X86_CONTEXT_H diff --git a/sgx-jvm/avian/src/codegen/target/x86/detect.cpp b/sgx-jvm/avian/src/codegen/target/x86/detect.cpp new file mode 100644 index 0000000000..52ecc2f293 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/x86/detect.cpp @@ -0,0 +1,83 @@ + +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/target.h" + +#include "context.h" + +// Note: this is so that we can build the x86 backend(s) on an arm machine. +// This way, we could (in theory) do a bootimage cross-compile from arm to x86 +#ifndef __arm__ + +#ifndef _MSC_VER +#include +#else +// MSVC implementation: +static int __get_cpuid(unsigned int __level, + unsigned int* __eax, + unsigned int* __ebx, + unsigned int* __ecx, + unsigned int* __edx) +{ + _asm + { + mov eax, __level; + cpuid; + mov[__eax], eax; + mov[__ebx], ebx; + mov[__ecx], ecx; + mov[__edx], edx; + } + return 1; +} +#define bit_SSE (1 << 25) +#define bit_SSE2 (1 << 26) + +#endif // ndef _MSC_VER + +#endif // ndef __arm__ + +namespace avian { +namespace codegen { +namespace x86 { + +// TODO: this should be moved such that it's called by the client (e.g. whatever +// allocates the Archivecture). That way, we can link the x86 code generator on +// another architecture (e.g. arm). + +bool useSSE(ArchitectureContext* c) +{ +#ifdef __arm__ + // We can't link in the detection code on arm (DUH!) + return vm::TargetBytesPerWord == 8; +#else + if (vm::TargetBytesPerWord == 8) { + // amd64 implies SSE2 support + return true; + } else if (c->useNativeFeatures) { + static int supported = -1; + if (supported == -1) { + unsigned eax; + unsigned ebx; + unsigned ecx; + unsigned edx; + supported = __get_cpuid(1, &eax, &ebx, &ecx, &edx) && (edx & bit_SSE) && (edx & bit_SSE2); + } + return supported; + } else { + return false; + } +#endif +} + +} // namespace x86 +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/target/x86/detect.h b/sgx-jvm/avian/src/codegen/target/x86/detect.h new file mode 100644 index 0000000000..f8014ab1da --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/x86/detect.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_X86_DETECT_H +#define AVIAN_CODEGEN_ASSEMBLER_X86_DETECT_H + +#include + +namespace avian { +namespace codegen { +namespace x86 { + +class ArchitectureContext; + +bool useSSE(ArchitectureContext* c); + +} // namespace x86 +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_X86_DETECT_H diff --git a/sgx-jvm/avian/src/codegen/target/x86/encode.cpp b/sgx-jvm/avian/src/codegen/target/x86/encode.cpp new file mode 100644 index 0000000000..38313660f8 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/x86/encode.cpp @@ -0,0 +1,432 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/target.h" +#include "avian/alloc-vector.h" + +#include +#include + +#include +#include + +#include "context.h" +#include "encode.h" +#include "registers.h" +#include "fixup.h" + +using namespace avian::util; + +namespace { + +int64_t signExtend(unsigned size, int64_t v) +{ + if (size == 4) { + return static_cast(v); + } else if (size == 2) { + return static_cast(v); + } else if (size == 1) { + return static_cast(v); + } else { + return v; + } +} + +} // namespace + +namespace avian { +namespace codegen { +namespace x86 { + +#define REX_W 0x48 +#define REX_R 0x44 +#define REX_X 0x42 +#define REX_B 0x41 +#define REX_NONE 0x40 + +void maybeRex(Context* c, + unsigned size, + Register a, + Register index, + Register base, + bool always) +{ + if (vm::TargetBytesPerWord == 8) { + uint8_t byte; + if (size == 8) { + byte = REX_W; + } else { + byte = REX_NONE; + } + if (a != NoRegister and (a.index() & 8)) + byte |= REX_R; + if (index != NoRegister and (index.index() & 8)) + byte |= REX_X; + if (base != NoRegister and (base.index() & 8)) + byte |= REX_B; + if (always or byte != REX_NONE) + c->code.append(byte); + } +} + +void maybeRex(Context* c, unsigned size, lir::RegisterPair* a, lir::RegisterPair* b) +{ + maybeRex(c, size, a->low, NoRegister, b->low, false); +} + +void alwaysRex(Context* c, unsigned size, lir::RegisterPair* a, lir::RegisterPair* b) +{ + maybeRex(c, size, a->low, NoRegister, b->low, true); +} + +void maybeRex(Context* c, unsigned size, lir::RegisterPair* a) +{ + maybeRex(c, size, NoRegister, NoRegister, a->low, false); +} + +void maybeRex(Context* c, unsigned size, lir::RegisterPair* a, lir::Memory* b) +{ + maybeRex(c, size, a->low, b->index, b->base, size == 1 and (a->low.index() & 4)); +} + +void maybeRex(Context* c, unsigned size, lir::Memory* a) +{ + maybeRex(c, size, NoRegister, a->index, a->base, false); +} + +void modrm(Context* c, uint8_t mod, Register a, Register b) +{ + c->code.append(mod | (regCode(b) << 3) | regCode(a)); +} + +void modrm(Context* c, uint8_t mod, lir::RegisterPair* a, lir::RegisterPair* b) +{ + modrm(c, mod, a->low, b->low); +} + +void sib(Context* c, unsigned scale, Register index, Register base) +{ + c->code.append((util::log(scale) << 6) | (regCode(index) << 3) + | regCode(base)); +} + +void modrmSib(Context* c, int width, Register a, int scale, Register index, Register base) +{ + if (index == NoRegister) { + modrm(c, width, base, a); + if (regCode(base) == rsp.index()) { + sib(c, 0x00, rsp, rsp); + } + } else { + modrm(c, width, rsp, a); + sib(c, scale, index, base); + } +} + +void modrmSibImm(Context* c, Register a, int scale, Register index, Register base, int offset) +{ + if (offset == 0 and regCode(base) != rbp.index()) { + modrmSib(c, 0x00, a, scale, index, base); + } else if (vm::fitsInInt8(offset)) { + modrmSib(c, 0x40, a, scale, index, base); + c->code.append(offset); + } else { + modrmSib(c, 0x80, a, scale, index, base); + c->code.append4(offset); + } +} + +void modrmSibImm(Context* c, lir::RegisterPair* a, lir::Memory* b) +{ + modrmSibImm(c, a->low, b->scale, b->index, b->base, b->offset); +} + +void opcode(Context* c, uint8_t op) +{ + c->code.append(op); +} + +void opcode(Context* c, uint8_t op1, uint8_t op2) +{ + c->code.append(op1); + c->code.append(op2); +} + +void unconditional(Context* c, unsigned jump, lir::Constant* a) +{ + appendOffsetTask(c, a->value, offsetPromise(c), 5); + + opcode(c, jump); + c->code.append4(0); +} + +void conditional(Context* c, unsigned condition, lir::Constant* a) +{ + appendOffsetTask(c, a->value, offsetPromise(c), 6); + + opcode(c, 0x0f, condition); + c->code.append4(0); +} + +void sseMoveRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + assertT(c, aSize >= 4); + assertT(c, aSize == bSize); + + if (isFloatReg(a) and isFloatReg(b)) { + if (aSize == 4) { + opcode(c, 0xf3); + maybeRex(c, 4, a, b); + opcode(c, 0x0f, 0x10); + modrm(c, 0xc0, a, b); + } else { + opcode(c, 0xf2); + maybeRex(c, 4, b, a); + opcode(c, 0x0f, 0x10); + modrm(c, 0xc0, a, b); + } + } else if (isFloatReg(a)) { + opcode(c, 0x66); + maybeRex(c, aSize, a, b); + opcode(c, 0x0f, 0x7e); + modrm(c, 0xc0, b, a); + } else { + opcode(c, 0x66); + maybeRex(c, aSize, b, a); + opcode(c, 0x0f, 0x6e); + modrm(c, 0xc0, a, b); + } +} + +void sseMoveCR(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b) +{ + assertT(c, aSize <= vm::TargetBytesPerWord); + lir::RegisterPair tmp(c->client->acquireTemporary(GeneralRegisterMask)); + moveCR2(c, aSize, a, aSize, &tmp, 0); + sseMoveRR(c, aSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); +} + +void sseMoveMR(Context* c, + unsigned aSize, + lir::Memory* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + assertT(c, aSize >= 4); + + if (vm::TargetBytesPerWord == 4 and aSize == 8) { + opcode(c, 0xf3); + opcode(c, 0x0f, 0x7e); + modrmSibImm(c, b, a); + } else { + opcode(c, 0x66); + maybeRex(c, aSize, b, a); + opcode(c, 0x0f, 0x6e); + modrmSibImm(c, b, a); + } +} + +void sseMoveRM(Context* c, + unsigned aSize, + lir::RegisterPair* a, + UNUSED unsigned bSize, + lir::Memory* b) +{ + assertT(c, aSize >= 4); + assertT(c, aSize == bSize); + + if (vm::TargetBytesPerWord == 4 and aSize == 8) { + opcode(c, 0x66); + opcode(c, 0x0f, 0xd6); + modrmSibImm(c, a, b); + } else { + opcode(c, 0x66); + maybeRex(c, aSize, a, b); + opcode(c, 0x0f, 0x7e); + modrmSibImm(c, a, b); + } +} + +void branch(Context* c, lir::TernaryOperation op, lir::Constant* target) +{ + switch (op) { + case lir::JumpIfEqual: + conditional(c, 0x84, target); + break; + + case lir::JumpIfNotEqual: + conditional(c, 0x85, target); + break; + + case lir::JumpIfLess: + conditional(c, 0x8c, target); + break; + + case lir::JumpIfGreater: + conditional(c, 0x8f, target); + break; + + case lir::JumpIfLessOrEqual: + conditional(c, 0x8e, target); + break; + + case lir::JumpIfGreaterOrEqual: + conditional(c, 0x8d, target); + break; + + default: + abort(c); + } +} + +void branchFloat(Context* c, lir::TernaryOperation op, lir::Constant* target) +{ + switch (op) { + case lir::JumpIfFloatEqual: + // jp past the je so we don't jump to the target if unordered: + c->code.append(0x7a); + c->code.append(6); + conditional(c, 0x84, target); + break; + + case lir::JumpIfFloatNotEqual: + conditional(c, 0x85, target); + conditional(c, 0x8a, target); + break; + + case lir::JumpIfFloatLess: + conditional(c, 0x82, target); + break; + + case lir::JumpIfFloatGreater: + conditional(c, 0x87, target); + break; + + case lir::JumpIfFloatLessOrEqual: + conditional(c, 0x86, target); + break; + + case lir::JumpIfFloatGreaterOrEqual: + conditional(c, 0x83, target); + break; + + case lir::JumpIfFloatLessOrUnordered: + conditional(c, 0x82, target); + conditional(c, 0x8a, target); + break; + + case lir::JumpIfFloatGreaterOrUnordered: + conditional(c, 0x87, target); + conditional(c, 0x8a, target); + break; + + case lir::JumpIfFloatLessOrEqualOrUnordered: + conditional(c, 0x86, target); + conditional(c, 0x8a, target); + break; + + case lir::JumpIfFloatGreaterOrEqualOrUnordered: + conditional(c, 0x83, target); + conditional(c, 0x8a, target); + break; + + default: + abort(c); + } +} + +void floatRegOp(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize, + lir::RegisterPair* b, + uint8_t op, + uint8_t mod) +{ + if (aSize == 4) { + opcode(c, 0xf3); + } else { + opcode(c, 0xf2); + } + maybeRex(c, bSize, b, a); + opcode(c, 0x0f, op); + modrm(c, mod, a, b); +} + +void floatMemOp(Context* c, + unsigned aSize, + lir::Memory* a, + unsigned bSize, + lir::RegisterPair* b, + uint8_t op) +{ + if (aSize == 4) { + opcode(c, 0xf3); + } else { + opcode(c, 0xf2); + } + maybeRex(c, bSize, b, a); + opcode(c, 0x0f, op); + modrmSibImm(c, b, a); +} + +void moveCR(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b); + +void moveCR2(Context* c, + UNUSED unsigned aSize, + lir::Constant* a, + UNUSED unsigned bSize, + lir::RegisterPair* b, + unsigned promiseOffset) +{ + if (vm::TargetBytesPerWord == 4 and bSize == 8) { + int64_t v = signExtend(aSize, a->value->value()); + + ResolvedPromise high((v >> 32) & 0xFFFFFFFF); + lir::Constant ah(&high); + + ResolvedPromise low(v & 0xFFFFFFFF); + lir::Constant al(&low); + + lir::RegisterPair bh(b->high); + + moveCR(c, 4, &al, 4, b); + moveCR(c, 4, &ah, 4, &bh); + } else { + maybeRex(c, vm::TargetBytesPerWord, b); + opcode(c, 0xb8 + regCode(b)); + if (a->value->resolved()) { + c->code.appendTargetAddress(signExtend(aSize, a->value->value())); + } else { + expect(c, aSize == vm::TargetBytesPerWord); + + appendImmediateTask( + c, a->value, offsetPromise(c), vm::TargetBytesPerWord, promiseOffset); + c->code.appendTargetAddress(static_cast(0)); + } + } +} + +} // namespace x86 +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/target/x86/encode.h b/sgx-jvm/avian/src/codegen/target/x86/encode.h new file mode 100644 index 0000000000..cb107cbbcc --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/x86/encode.h @@ -0,0 +1,134 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_X86_ENCODE_H +#define AVIAN_CODEGEN_ASSEMBLER_X86_ENCODE_H + +#include + +#include "avian/common.h" + +#include + +#include "registers.h" + +namespace avian { +namespace codegen { +namespace x86 { + +class Context; + +void maybeRex(Context* c, + unsigned size, + int a, + int index, + int base, + bool always); + +void maybeRex(Context* c, unsigned size, lir::RegisterPair* a, lir::RegisterPair* b); + +void alwaysRex(Context* c, unsigned size, lir::RegisterPair* a, lir::RegisterPair* b); + +void maybeRex(Context* c, unsigned size, lir::RegisterPair* a); + +void maybeRex(Context* c, unsigned size, lir::RegisterPair* a, lir::Memory* b); + +void maybeRex(Context* c, unsigned size, lir::Memory* a); + +inline int regCode(Register a) +{ + return a.index() & 7; +} + +inline int regCode(lir::RegisterPair* a) +{ + return regCode(a->low); +} + +inline bool isFloatReg(lir::RegisterPair* a) +{ + return a->low >= xmm0; +} + +void modrm(Context* c, uint8_t mod, Register a, Register b); + +void modrm(Context* c, uint8_t mod, lir::RegisterPair* a, lir::RegisterPair* b); + +void sib(Context* c, unsigned scale, Register index, Register base); + +void modrmSib(Context* c, int width, Register a, int scale, Register index, Register base); + +void modrmSibImm(Context* c, Register a, int scale, Register index, Register base, int offset); + +void modrmSibImm(Context* c, lir::RegisterPair* a, lir::Memory* b); + +void opcode(Context* c, uint8_t op); + +void opcode(Context* c, uint8_t op1, uint8_t op2); + +void unconditional(Context* c, unsigned jump, lir::Constant* a); + +void conditional(Context* c, unsigned condition, lir::Constant* a); + +void sseMoveRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void sseMoveCR(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b); + +void sseMoveMR(Context* c, + unsigned aSize, + lir::Memory* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void sseMoveRM(Context* c, + unsigned aSize, + lir::RegisterPair* a, + UNUSED unsigned bSize, + lir::Memory* b); + +void branch(Context* c, lir::TernaryOperation op, lir::Constant* target); + +void branchFloat(Context* c, lir::TernaryOperation op, lir::Constant* target); + +void floatRegOp(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize, + lir::RegisterPair* b, + uint8_t op, + uint8_t mod = 0xc0); + +void floatMemOp(Context* c, + unsigned aSize, + lir::Memory* a, + unsigned bSize, + lir::RegisterPair* b, + uint8_t op); + +void moveCR2(Context* c, + UNUSED unsigned aSize, + lir::Constant* a, + UNUSED unsigned bSize, + lir::RegisterPair* b, + unsigned promiseOffset); + +} // namespace x86 +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_X86_ENCODE_H diff --git a/sgx-jvm/avian/src/codegen/target/x86/fixup.cpp b/sgx-jvm/avian/src/codegen/target/x86/fixup.cpp new file mode 100644 index 0000000000..788d773afe --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/x86/fixup.cpp @@ -0,0 +1,209 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include + +#include "avian/util/allocator.h" +#include "avian/alloc-vector.h" +#include "avian/common.h" +#include "avian/zone.h" + +#include +#include + +#include "context.h" +#include "fixup.h" +#include "padding.h" +#include "block.h" + +namespace avian { +namespace codegen { +namespace x86 { + +using namespace util; + +ResolvedPromise* resolvedPromise(Context* c, int64_t value) +{ + return new (c->zone) ResolvedPromise(value); +} + +OffsetPromise::OffsetPromise(Context* c, + MyBlock* block, + unsigned offset, + AlignmentPadding* limit) + : c(c), block(block), offset(offset), limit(limit), value_(-1) +{ +} + +bool OffsetPromise::resolved() +{ + return block->start != static_cast(~0); +} + +int64_t OffsetPromise::value() +{ + assertT(c, resolved()); + + if (value_ == -1) { + value_ = block->start + (offset - block->offset) + + padding(block->firstPadding, block->start, block->offset, limit); + } + + return value_; +} +Promise* offsetPromise(Context* c) +{ + return new (c->zone) OffsetPromise( + c, c->lastBlock, c->code.length(), c->lastBlock->lastPadding); +} + +void* resolveOffset(vm::System* s, + uint8_t* instruction, + unsigned instructionSize, + int64_t value) +{ + intptr_t v = reinterpret_cast(value) - instruction + - instructionSize; + + expect(s, vm::fitsInInt32(v)); + + int32_t v4 = v; + memcpy(instruction + instructionSize - 4, &v4, 4); + return instruction + instructionSize; +} + +OffsetListener::OffsetListener(vm::System* s, + uint8_t* instruction, + unsigned instructionSize) + : s(s), instruction(instruction), instructionSize(instructionSize) +{ +} + +bool OffsetListener::resolve(int64_t value, void** location) +{ + void* p = resolveOffset(s, instruction, instructionSize, value); + if (location) + *location = p; + return false; +} + +OffsetTask::OffsetTask(Task* next, + Promise* promise, + Promise* instructionOffset, + unsigned instructionSize) + : Task(next), + promise(promise), + instructionOffset(instructionOffset), + instructionSize(instructionSize) +{ +} + +void OffsetTask::run(Context* c) +{ + if (promise->resolved()) { + resolveOffset(c->s, + c->result + instructionOffset->value(), + instructionSize, + promise->value()); + } else { + new (promise->listen(sizeof(OffsetListener))) OffsetListener( + c->s, c->result + instructionOffset->value(), instructionSize); + } +} + +void appendOffsetTask(Context* c, + Promise* promise, + Promise* instructionOffset, + unsigned instructionSize) +{ + OffsetTask* task = new (c->zone) + OffsetTask(c->tasks, promise, instructionOffset, instructionSize); + + c->tasks = task; +} + +ImmediateListener::ImmediateListener(vm::System* s, + void* dst, + unsigned size, + unsigned offset) + : s(s), dst(dst), size(size), offset(offset) +{ +} + +void copy(vm::System* s, void* dst, int64_t src, unsigned size) +{ + switch (size) { + case 4: { + int32_t v = src; + memcpy(dst, &v, 4); + } break; + + case 8: { + int64_t v = src; + memcpy(dst, &v, 8); + } break; + + default: + abort(s); + } +} + +bool ImmediateListener::resolve(int64_t value, void** location) +{ + copy(s, dst, value, size); + if (location) + *location = static_cast(dst) + offset; + return offset == 0; +} + +ImmediateTask::ImmediateTask(Task* next, + Promise* promise, + Promise* offset, + unsigned size, + unsigned promiseOffset) + : Task(next), + promise(promise), + offset(offset), + size(size), + promiseOffset(promiseOffset) +{ +} + +void ImmediateTask::run(Context* c) +{ + if (promise->resolved()) { + copy(c->s, c->result + offset->value(), promise->value(), size); + } else { + new (promise->listen(sizeof(ImmediateListener))) ImmediateListener( + c->s, c->result + offset->value(), size, promiseOffset); + } +} + +void appendImmediateTask(Context* c, + Promise* promise, + Promise* offset, + unsigned size, + unsigned promiseOffset) +{ + c->tasks = new (c->zone) + ImmediateTask(c->tasks, promise, offset, size, promiseOffset); +} + +ShiftMaskPromise* shiftMaskPromise(Context* c, + Promise* base, + unsigned shift, + int64_t mask) +{ + return new (c->zone) ShiftMaskPromise(base, shift, mask); +} + +} // namespace x86 +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/target/x86/fixup.h b/sgx-jvm/avian/src/codegen/target/x86/fixup.h new file mode 100644 index 0000000000..68c6d1fe0d --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/x86/fixup.h @@ -0,0 +1,141 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_X86_FIXUP_H +#define AVIAN_CODEGEN_ASSEMBLER_X86_FIXUP_H + +#include + +#include + +namespace vm { +class System; +} + +namespace avian { +namespace codegen { +namespace x86 { + +class Context; +class MyBlock; +class AlignmentPadding; + +ResolvedPromise* resolvedPromise(Context* c, int64_t value); + +class Task { + public: + Task(Task* next) : next(next) + { + } + + virtual void run(Context* c) = 0; + + Task* next; +}; + +class OffsetPromise : public Promise { + public: + OffsetPromise(Context* c, + MyBlock* block, + unsigned offset, + AlignmentPadding* limit); + + virtual bool resolved(); + + virtual int64_t value(); + + Context* c; + MyBlock* block; + unsigned offset; + AlignmentPadding* limit; + int value_; +}; + +Promise* offsetPromise(Context* c); + +void* resolveOffset(vm::System* s, + uint8_t* instruction, + unsigned instructionSize, + int64_t value); + +class OffsetListener : public Promise::Listener { + public: + OffsetListener(vm::System* s, uint8_t* instruction, unsigned instructionSize); + + virtual bool resolve(int64_t value, void** location); + + vm::System* s; + uint8_t* instruction; + unsigned instructionSize; +}; + +class OffsetTask : public Task { + public: + OffsetTask(Task* next, + Promise* promise, + Promise* instructionOffset, + unsigned instructionSize); + + virtual void run(Context* c); + + Promise* promise; + Promise* instructionOffset; + unsigned instructionSize; +}; + +void appendOffsetTask(Context* c, + Promise* promise, + Promise* instructionOffset, + unsigned instructionSize); + +class ImmediateListener : public Promise::Listener { + public: + ImmediateListener(vm::System* s, void* dst, unsigned size, unsigned offset); + + virtual bool resolve(int64_t value, void** location); + + vm::System* s; + void* dst; + unsigned size; + unsigned offset; +}; + +class ImmediateTask : public Task { + public: + ImmediateTask(Task* next, + Promise* promise, + Promise* offset, + unsigned size, + unsigned promiseOffset); + + virtual void run(Context* c); + + Promise* promise; + Promise* offset; + unsigned size; + unsigned promiseOffset; +}; + +void appendImmediateTask(Context* c, + Promise* promise, + Promise* offset, + unsigned size, + unsigned promiseOffset = 0); + +ShiftMaskPromise* shiftMaskPromise(Context* c, + Promise* base, + unsigned shift, + int64_t mask); + +} // namespace x86 +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_X86_FIXUP_H diff --git a/sgx-jvm/avian/src/codegen/target/x86/multimethod.cpp b/sgx-jvm/avian/src/codegen/target/x86/multimethod.cpp new file mode 100644 index 0000000000..9eae71bada --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/x86/multimethod.cpp @@ -0,0 +1,180 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/common.h" + +#include + +#include + +#include "context.h" +#include "operations.h" + +#include "multimethod.h" +#include "../multimethod.h" + +namespace avian { +namespace codegen { +namespace x86 { + +using namespace util; + +unsigned index(ArchitectureContext*, + lir::BinaryOperation operation, + lir::Operand::Type operand1, + lir::Operand::Type operand2) +{ + return operation + ((lir::BinaryOperationCount + + lir::NonBranchTernaryOperationCount) * (unsigned)operand1) + + ((lir::BinaryOperationCount + lir::NonBranchTernaryOperationCount) + * lir::Operand::TypeCount * (unsigned)operand2); +} + +unsigned index(ArchitectureContext* c UNUSED, + lir::TernaryOperation operation, + lir::Operand::Type operand1, + lir::Operand::Type operand2) +{ + assertT(c, not isBranch(operation)); + + return lir::BinaryOperationCount + operation + + ((lir::BinaryOperationCount + lir::NonBranchTernaryOperationCount) + * (unsigned)operand1) + + ((lir::BinaryOperationCount + lir::NonBranchTernaryOperationCount) + * lir::Operand::TypeCount * (unsigned)operand2); +} + +unsigned branchIndex(ArchitectureContext* c UNUSED, + lir::Operand::Type operand1, + lir::Operand::Type operand2) +{ + return (unsigned)operand1 + (lir::Operand::TypeCount * (unsigned)operand2); +} + +void populateTables(ArchitectureContext* c) +{ + const lir::Operand::Type C = lir::Operand::Type::Constant; + const lir::Operand::Type A = lir::Operand::Type::Address; + const lir::Operand::Type R = lir::Operand::Type::RegisterPair; + const lir::Operand::Type M = lir::Operand::Type::Memory; + + OperationType* zo = c->operations; + UnaryOperationType* uo = c->unaryOperations; + BinaryOperationType* bo = c->binaryOperations; + BranchOperationType* bro = c->branchOperations; + + zo[lir::Return] = return_; + zo[lir::LoadBarrier] = ignore; + zo[lir::StoreStoreBarrier] = ignore; + zo[lir::StoreLoadBarrier] = storeLoadBarrier; + zo[lir::Trap] = trap; + + uo[Multimethod::index(lir::Call, C)] = CAST1(callC); + uo[Multimethod::index(lir::Call, R)] = CAST1(callR); + uo[Multimethod::index(lir::Call, M)] = CAST1(callM); + + uo[Multimethod::index(lir::AlignedCall, C)] = CAST1(alignedCallC); + + uo[Multimethod::index(lir::LongCall, C)] = CAST1(longCallC); + + uo[Multimethod::index(lir::AlignedLongCall, C)] = CAST1(alignedLongCallC); + + uo[Multimethod::index(lir::Jump, R)] = CAST1(jumpR); + uo[Multimethod::index(lir::Jump, C)] = CAST1(jumpC); + uo[Multimethod::index(lir::Jump, M)] = CAST1(jumpM); + + uo[Multimethod::index(lir::AlignedJump, C)] = CAST1(alignedJumpC); + + uo[Multimethod::index(lir::LongJump, C)] = CAST1(longJumpC); + + uo[Multimethod::index(lir::AlignedLongJump, C)] = CAST1(alignedLongJumpC); + + bo[index(c, lir::Negate, R, R)] = CAST2(negateRR); + + bo[index(c, lir::FloatNegate, R, R)] = CAST2(floatNegateRR); + + bo[index(c, lir::Move, R, R)] = CAST2(moveRR); + bo[index(c, lir::Move, C, R)] = CAST2(moveCR); + bo[index(c, lir::Move, M, R)] = CAST2(moveMR); + bo[index(c, lir::Move, R, M)] = CAST2(moveRM); + bo[index(c, lir::Move, C, M)] = CAST2(moveCM); + bo[index(c, lir::Move, A, R)] = CAST2(moveAR); + + bo[index(c, lir::FloatSquareRoot, R, R)] = CAST2(floatSqrtRR); + bo[index(c, lir::FloatSquareRoot, M, R)] = CAST2(floatSqrtMR); + + bo[index(c, lir::MoveZ, R, R)] = CAST2(moveZRR); + bo[index(c, lir::MoveZ, M, R)] = CAST2(moveZMR); + bo[index(c, lir::MoveZ, C, R)] = CAST2(moveZCR); + + bo[index(c, lir::Add, R, R)] = CAST2(addRR); + bo[index(c, lir::Add, C, R)] = CAST2(addCR); + + bo[index(c, lir::Subtract, C, R)] = CAST2(subtractCR); + bo[index(c, lir::Subtract, R, R)] = CAST2(subtractRR); + + bo[index(c, lir::FloatAdd, R, R)] = CAST2(floatAddRR); + bo[index(c, lir::FloatAdd, M, R)] = CAST2(floatAddMR); + + bo[index(c, lir::FloatSubtract, R, R)] = CAST2(floatSubtractRR); + bo[index(c, lir::FloatSubtract, M, R)] = CAST2(floatSubtractMR); + + bo[index(c, lir::And, R, R)] = CAST2(andRR); + bo[index(c, lir::And, C, R)] = CAST2(andCR); + + bo[index(c, lir::Or, R, R)] = CAST2(orRR); + bo[index(c, lir::Or, C, R)] = CAST2(orCR); + + bo[index(c, lir::Xor, R, R)] = CAST2(xorRR); + bo[index(c, lir::Xor, C, R)] = CAST2(xorCR); + + bo[index(c, lir::Multiply, R, R)] = CAST2(multiplyRR); + bo[index(c, lir::Multiply, C, R)] = CAST2(multiplyCR); + + bo[index(c, lir::Divide, R, R)] = CAST2(divideRR); + + bo[index(c, lir::FloatMultiply, R, R)] = CAST2(floatMultiplyRR); + bo[index(c, lir::FloatMultiply, M, R)] = CAST2(floatMultiplyMR); + + bo[index(c, lir::FloatDivide, R, R)] = CAST2(floatDivideRR); + bo[index(c, lir::FloatDivide, M, R)] = CAST2(floatDivideMR); + + bo[index(c, lir::Remainder, R, R)] = CAST2(remainderRR); + + bo[index(c, lir::ShiftLeft, R, R)] = CAST2(shiftLeftRR); + bo[index(c, lir::ShiftLeft, C, R)] = CAST2(shiftLeftCR); + + bo[index(c, lir::ShiftRight, R, R)] = CAST2(shiftRightRR); + bo[index(c, lir::ShiftRight, C, R)] = CAST2(shiftRightCR); + + bo[index(c, lir::UnsignedShiftRight, R, R)] = CAST2(unsignedShiftRightRR); + bo[index(c, lir::UnsignedShiftRight, C, R)] = CAST2(unsignedShiftRightCR); + + bo[index(c, lir::Float2Float, R, R)] = CAST2(float2FloatRR); + bo[index(c, lir::Float2Float, M, R)] = CAST2(float2FloatMR); + + bo[index(c, lir::Float2Int, R, R)] = CAST2(float2IntRR); + bo[index(c, lir::Float2Int, M, R)] = CAST2(float2IntMR); + + bo[index(c, lir::Int2Float, R, R)] = CAST2(int2FloatRR); + bo[index(c, lir::Int2Float, M, R)] = CAST2(int2FloatMR); + + bo[index(c, lir::Absolute, R, R)] = CAST2(absoluteRR); + bo[index(c, lir::FloatAbsolute, R, R)] = CAST2(floatAbsoluteRR); + + bro[branchIndex(c, R, R)] = CAST_BRANCH(branchRR); + bro[branchIndex(c, C, R)] = CAST_BRANCH(branchCR); + bro[branchIndex(c, C, M)] = CAST_BRANCH(branchCM); + bro[branchIndex(c, R, M)] = CAST_BRANCH(branchRM); +} + +} // namespace x86 +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/target/x86/multimethod.h b/sgx-jvm/avian/src/codegen/target/x86/multimethod.h new file mode 100644 index 0000000000..46b62300c5 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/x86/multimethod.h @@ -0,0 +1,44 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_X86_MULTIMETHOD_H +#define AVIAN_CODEGEN_ASSEMBLER_X86_MULTIMETHOD_H + +#include "avian/common.h" + +#include + +namespace avian { +namespace codegen { +namespace x86 { + +class ArchitectureContext; + +unsigned index(ArchitectureContext*, + lir::BinaryOperation operation, + lir::Operand::Type operand1, + lir::Operand::Type operand2); + +unsigned index(ArchitectureContext* c UNUSED, + lir::TernaryOperation operation, + lir::Operand::Type operand1, + lir::Operand::Type operand2); + +unsigned branchIndex(ArchitectureContext* c UNUSED, + lir::Operand::Type operand1, + lir::Operand::Type operand2); + +void populateTables(ArchitectureContext* c); + +} // namespace x86 +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_X86_MULTIMETHOD_H diff --git a/sgx-jvm/avian/src/codegen/target/x86/operations.cpp b/sgx-jvm/avian/src/codegen/target/x86/operations.cpp new file mode 100644 index 0000000000..d9cbde1c04 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/x86/operations.cpp @@ -0,0 +1,1759 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/target.h" +#include "avian/alloc-vector.h" +#include "avian/util/allocator.h" +#include "avian/zone.h" + +#include + +#include +#include + +#include "context.h" +#include "encode.h" +#include "registers.h" +#include "detect.h" +#include "operations.h" +#include "padding.h" +#include "fixup.h" + +using namespace avian::util; + +namespace avian { +namespace codegen { +namespace x86 { + +void return_(Context* c) +{ + opcode(c, 0xc3); +} + +void trap(Context* c) +{ + opcode(c, 0xcc); +} + +void ignore(Context*) +{ +} + +void storeLoadBarrier(Context* c) +{ + if (useSSE(c->ac)) { + // mfence: + c->code.append(0x0f); + c->code.append(0xae); + c->code.append(0xf0); + } else { + // lock addq $0x0,(%rsp): + c->code.append(0xf0); + if (vm::TargetBytesPerWord == 8) { + c->code.append(0x48); + } + c->code.append(0x83); + c->code.append(0x04); + c->code.append(0x24); + c->code.append(0x00); + } +} + +void callC(Context* c, unsigned size UNUSED, lir::Constant* a) +{ + assertT(c, size == vm::TargetBytesPerWord); + + unconditional(c, 0xe8, a); +} + +void longCallC(Context* c, unsigned size, lir::Constant* a) +{ + assertT(c, size == vm::TargetBytesPerWord); + + if (vm::TargetBytesPerWord == 8) { + lir::RegisterPair r(LongJumpRegister); + moveCR2(c, size, a, size, &r, 11); + callR(c, size, &r); + } else { + callC(c, size, a); + } +} + +void jumpR(Context* c, unsigned size UNUSED, lir::RegisterPair* a) +{ + assertT(c, size == vm::TargetBytesPerWord); + + maybeRex(c, 4, a); + opcode(c, 0xff, 0xe0 + regCode(a)); +} + +void jumpC(Context* c, unsigned size UNUSED, lir::Constant* a) +{ + assertT(c, size == vm::TargetBytesPerWord); + + unconditional(c, 0xe9, a); +} + +void jumpM(Context* c, unsigned size UNUSED, lir::Memory* a) +{ + assertT(c, size == vm::TargetBytesPerWord); + + maybeRex(c, 4, a); + opcode(c, 0xff); + modrmSibImm(c, rsp, a->scale, a->index, a->base, a->offset); +} + +void longJumpC(Context* c, unsigned size, lir::Constant* a) +{ + assertT(c, size == vm::TargetBytesPerWord); + + if (vm::TargetBytesPerWord == 8) { + lir::RegisterPair r(LongJumpRegister); + moveCR2(c, size, a, size, &r, 11); + jumpR(c, size, &r); + } else { + jumpC(c, size, a); + } +} + +void callR(Context* c, unsigned size UNUSED, lir::RegisterPair* a) +{ + assertT(c, size == vm::TargetBytesPerWord); + + // maybeRex.W has no meaning here so we disable it + maybeRex(c, 4, a); + opcode(c, 0xff, 0xd0 + regCode(a)); +} + +void callM(Context* c, unsigned size UNUSED, lir::Memory* a) +{ + assertT(c, size == vm::TargetBytesPerWord); + + maybeRex(c, 4, a); + opcode(c, 0xff); + modrmSibImm(c, rdx, a->scale, a->index, a->base, a->offset); +} + +void alignedCallC(Context* c, unsigned size, lir::Constant* a) +{ + new (c->zone) AlignmentPadding(c, 1, 4); + callC(c, size, a); +} + +void alignedLongCallC(Context* c, unsigned size, lir::Constant* a) +{ + assertT(c, size == vm::TargetBytesPerWord); + + if (vm::TargetBytesPerWord == 8) { + new (c->zone) AlignmentPadding(c, 2, 8); + longCallC(c, size, a); + } else { + alignedCallC(c, size, a); + } +} + +void alignedJumpC(Context* c, unsigned size, lir::Constant* a) +{ + new (c->zone) AlignmentPadding(c, 1, 4); + jumpC(c, size, a); +} + +void alignedLongJumpC(Context* c, unsigned size, lir::Constant* a) +{ + assertT(c, size == vm::TargetBytesPerWord); + + if (vm::TargetBytesPerWord == 8) { + new (c->zone) AlignmentPadding(c, 2, 8); + longJumpC(c, size, a); + } else { + alignedJumpC(c, size, a); + } +} + +void pushR(Context* c, unsigned size, lir::RegisterPair* a) +{ + if (vm::TargetBytesPerWord == 4 and size == 8) { + lir::RegisterPair ah(a->high); + + pushR(c, 4, &ah); + pushR(c, 4, a); + } else { + maybeRex(c, 4, a); + opcode(c, 0x50 + regCode(a)); + } +} + +void popR(Context* c, unsigned size, lir::RegisterPair* a) +{ + if (vm::TargetBytesPerWord == 4 and size == 8) { + lir::RegisterPair ah(a->high); + + popR(c, 4, a); + popR(c, 4, &ah); + } else { + maybeRex(c, 4, a); + opcode(c, 0x58 + regCode(a)); + if (vm::TargetBytesPerWord == 8 and size == 4) { + moveRR(c, 4, a, 8, a); + } + } +} + +void negateR(Context* c, unsigned size, lir::RegisterPair* a) +{ + if (vm::TargetBytesPerWord == 4 and size == 8) { + assertT(c, a->low == rax and a->high == rdx); + + ResolvedPromise zeroPromise(0); + lir::Constant zero(&zeroPromise); + + lir::RegisterPair ah(a->high); + + negateR(c, 4, a); + addCarryCR(c, 4, &zero, &ah); + negateR(c, 4, &ah); + } else { + maybeRex(c, size, a); + opcode(c, 0xf7, 0xd8 + regCode(a)); + } +} + +void negateRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b UNUSED) +{ + assertT(c, aSize == bSize); + + negateR(c, aSize, a); +} + +void moveCR(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b) +{ + if (isFloatReg(b)) { + sseMoveCR(c, aSize, a, bSize, b); + } else { + moveCR2(c, aSize, a, bSize, b, 0); + } +} + +void moveZCR(Context* c, + unsigned aSize UNUSED, + lir::Constant* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + assertT(c, not isFloatReg(b)); + assertT(c, aSize == 2); + assertT(c, bSize == vm::TargetBytesPerWord); + assertT(c, a->value->resolved()); + + maybeRex(c, vm::TargetBytesPerWord, b); + opcode(c, 0xb8 + regCode(b)); + c->code.appendTargetAddress(static_cast(a->value->value())); +} + +void swapRR(Context* c, + unsigned aSize UNUSED, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + assertT(c, aSize == bSize); + assertT(c, aSize == vm::TargetBytesPerWord); + + alwaysRex(c, aSize, a, b); + opcode(c, 0x87); + modrm(c, 0xc0, b, a); +} + +void moveRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + UNUSED unsigned bSize, + lir::RegisterPair* b) +{ + if (isFloatReg(a) or isFloatReg(b)) { + sseMoveRR(c, aSize, a, bSize, b); + return; + } + + if (vm::TargetBytesPerWord == 4 and aSize == 8 and bSize == 8) { + lir::RegisterPair ah(a->high); + lir::RegisterPair bh(b->high); + + if (a->high == b->low) { + if (a->low == b->high) { + swapRR(c, 4, a, 4, b); + } else { + moveRR(c, 4, &ah, 4, &bh); + moveRR(c, 4, a, 4, b); + } + } else { + moveRR(c, 4, a, 4, b); + moveRR(c, 4, &ah, 4, &bh); + } + } else { + switch (aSize) { + case 1: + if (vm::TargetBytesPerWord == 4 and a->low > rbx) { + assertT(c, b->low <= rbx); + + moveRR(c, vm::TargetBytesPerWord, a, vm::TargetBytesPerWord, b); + moveRR(c, 1, b, vm::TargetBytesPerWord, b); + } else { + alwaysRex(c, aSize, b, a); + opcode(c, 0x0f, 0xbe); + modrm(c, 0xc0, a, b); + } + break; + + case 2: + alwaysRex(c, aSize, b, a); + opcode(c, 0x0f, 0xbf); + modrm(c, 0xc0, a, b); + break; + + case 4: + if (bSize == 8) { + if (vm::TargetBytesPerWord == 8) { + alwaysRex(c, bSize, b, a); + opcode(c, 0x63); + modrm(c, 0xc0, a, b); + } else { + if (a->low == rax and b->low == rax and b->high == rdx) { + opcode(c, 0x99); // cdq + } else { + assertT(c, b->low == rax and b->high == rdx); + + moveRR(c, 4, a, 4, b); + moveRR(c, 4, b, 8, b); + } + } + } else { + if (a->low != b->low) { + alwaysRex(c, aSize, a, b); + opcode(c, 0x89); + modrm(c, 0xc0, b, a); + } + } + break; + + case 8: + if (a->low != b->low) { + maybeRex(c, aSize, a, b); + opcode(c, 0x89); + modrm(c, 0xc0, b, a); + } + break; + } + } +} + +void moveMR(Context* c, + unsigned aSize, + lir::Memory* a, + unsigned bSize, + lir::RegisterPair* b) +{ + if (isFloatReg(b)) { + sseMoveMR(c, aSize, a, bSize, b); + return; + } + + switch (aSize) { + case 1: + maybeRex(c, bSize, b, a); + opcode(c, 0x0f, 0xbe); + modrmSibImm(c, b, a); + break; + + case 2: + maybeRex(c, bSize, b, a); + opcode(c, 0x0f, 0xbf); + modrmSibImm(c, b, a); + break; + + case 4: + if (vm::TargetBytesPerWord == 8) { + maybeRex(c, bSize, b, a); + opcode(c, 0x63); + modrmSibImm(c, b, a); + } else { + if (bSize == 8) { + assertT(c, b->low == rax and b->high == rdx); + + moveMR(c, 4, a, 4, b); + moveRR(c, 4, b, 8, b); + } else { + maybeRex(c, bSize, b, a); + opcode(c, 0x8b); + modrmSibImm(c, b, a); + } + } + break; + + case 8: + if (vm::TargetBytesPerWord == 4 and bSize == 8) { + lir::Memory ah(a->base, a->offset + 4, a->index, a->scale); + lir::RegisterPair bh(b->high); + + moveMR(c, 4, a, 4, b); + moveMR(c, 4, &ah, 4, &bh); + } else { + maybeRex(c, bSize, b, a); + opcode(c, 0x8b); + modrmSibImm(c, b, a); + } + break; + + default: + abort(c); + } +} + +void moveRM(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::Memory* b) +{ + assertT(c, aSize == bSize); + + if (isFloatReg(a)) { + sseMoveRM(c, aSize, a, bSize, b); + return; + } + + switch (aSize) { + case 1: + maybeRex(c, bSize, a, b); + opcode(c, 0x88); + modrmSibImm(c, a, b); + break; + + case 2: + opcode(c, 0x66); + maybeRex(c, bSize, a, b); + opcode(c, 0x89); + modrmSibImm(c, a, b); + break; + + case 4: + if (vm::TargetBytesPerWord == 8) { + maybeRex(c, bSize, a, b); + opcode(c, 0x89); + modrmSibImm(c, a, b); + break; + } else { + opcode(c, 0x89); + modrmSibImm(c, a, b); + } + break; + + case 8: + if (vm::TargetBytesPerWord == 8) { + maybeRex(c, bSize, a, b); + opcode(c, 0x89); + modrmSibImm(c, a, b); + } else { + lir::RegisterPair ah(a->high); + lir::Memory bh(b->base, b->offset + 4, b->index, b->scale); + + moveRM(c, 4, a, 4, b); + moveRM(c, 4, &ah, 4, &bh); + } + break; + + default: + abort(c); + } +} + +void moveAR(Context* c, + unsigned aSize, + lir::Address* a, + unsigned bSize, + lir::RegisterPair* b) +{ + assertT(c, vm::TargetBytesPerWord == 8 or (aSize == 4 and bSize == 4)); + + lir::Constant constant(a->address); + lir::Memory memory(b->low, 0, NoRegister, 0); + + moveCR(c, aSize, &constant, bSize, b); + moveMR(c, bSize, &memory, bSize, b); +} + +void moveCM(Context* c, + unsigned aSize UNUSED, + lir::Constant* a, + unsigned bSize, + lir::Memory* b) +{ + switch (bSize) { + case 1: + maybeRex(c, bSize, b); + opcode(c, 0xc6); + modrmSibImm(c, rax, b->scale, b->index, b->base, b->offset); + c->code.append(a->value->value()); + break; + + case 2: + opcode(c, 0x66); + maybeRex(c, bSize, b); + opcode(c, 0xc7); + modrmSibImm(c, rax, b->scale, b->index, b->base, b->offset); + c->code.append2(a->value->value()); + break; + + case 4: + maybeRex(c, bSize, b); + opcode(c, 0xc7); + modrmSibImm(c, rax, b->scale, b->index, b->base, b->offset); + if (a->value->resolved()) { + c->code.append4(a->value->value()); + } else { + appendImmediateTask(c, a->value, offsetPromise(c), 4); + c->code.append4(0); + } + break; + + case 8: { + if (vm::TargetBytesPerWord == 8) { + if (a->value->resolved() and vm::fitsInInt32(a->value->value())) { + maybeRex(c, bSize, b); + opcode(c, 0xc7); + modrmSibImm(c, rax, b->scale, b->index, b->base, b->offset); + c->code.append4(a->value->value()); + } else { + lir::RegisterPair tmp(c->client->acquireTemporary(GeneralRegisterMask)); + moveCR(c, 8, a, 8, &tmp); + moveRM(c, 8, &tmp, 8, b); + c->client->releaseTemporary(tmp.low); + } + } else { + lir::Constant ah(shiftMaskPromise(c, a->value, 32, 0xFFFFFFFF)); + lir::Constant al(shiftMaskPromise(c, a->value, 0, 0xFFFFFFFF)); + + lir::Memory bh(b->base, b->offset + 4, b->index, b->scale); + + moveCM(c, 4, &al, 4, b); + moveCM(c, 4, &ah, 4, &bh); + } + } break; + + default: + abort(c); + } +} + +void moveZRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + switch (aSize) { + case 2: + alwaysRex(c, aSize, b, a); + opcode(c, 0x0f, 0xb7); + modrm(c, 0xc0, a, b); + break; + + default: + abort(c); + } +} + +void moveZMR(Context* c, + unsigned aSize UNUSED, + lir::Memory* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + assertT(c, bSize == vm::TargetBytesPerWord); + assertT(c, aSize == 2); + + maybeRex(c, bSize, b, a); + opcode(c, 0x0f, 0xb7); + modrmSibImm(c, b->low, a->scale, a->index, a->base, a->offset); +} + +void addCarryRR(Context* c, unsigned size, lir::RegisterPair* a, lir::RegisterPair* b) +{ + assertT(c, vm::TargetBytesPerWord == 8 or size == 4); + + maybeRex(c, size, a, b); + opcode(c, 0x11); + modrm(c, 0xc0, b, a); +} + +void addRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + assertT(c, aSize == bSize); + + if (vm::TargetBytesPerWord == 4 and aSize == 8) { + lir::RegisterPair ah(a->high); + lir::RegisterPair bh(b->high); + + addRR(c, 4, a, 4, b); + addCarryRR(c, 4, &ah, &bh); + } else { + maybeRex(c, aSize, a, b); + opcode(c, 0x01); + modrm(c, 0xc0, b, a); + } +} + +void addCarryCR(Context* c, unsigned size, lir::Constant* a, lir::RegisterPair* b) +{ + int64_t v = a->value->value(); + maybeRex(c, size, b); + if (vm::fitsInInt8(v)) { + opcode(c, 0x83, 0xd0 + regCode(b)); + c->code.append(v); + } else { + opcode(c, 0x81, 0xd0 + regCode(b)); + c->code.append4(v); + } +} + +void addCR(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b) +{ + assertT(c, aSize == bSize); + + int64_t v = a->value->value(); + if (v) { + if (vm::TargetBytesPerWord == 4 and bSize == 8) { + ResolvedPromise high((v >> 32) & 0xFFFFFFFF); + lir::Constant ah(&high); + + ResolvedPromise low(v & 0xFFFFFFFF); + lir::Constant al(&low); + + lir::RegisterPair bh(b->high); + + addCR(c, 4, &al, 4, b); + addCarryCR(c, 4, &ah, &bh); + } else { + if (vm::fitsInInt32(v)) { + maybeRex(c, aSize, b); + if (vm::fitsInInt8(v)) { + opcode(c, 0x83, 0xc0 + regCode(b)); + c->code.append(v); + } else { + opcode(c, 0x81, 0xc0 + regCode(b)); + c->code.append4(v); + } + } else { + lir::RegisterPair tmp(c->client->acquireTemporary(GeneralRegisterMask)); + moveCR(c, aSize, a, aSize, &tmp); + addRR(c, aSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); + } + } + } +} + +void subtractBorrowCR(Context* c, + unsigned size UNUSED, + lir::Constant* a, + lir::RegisterPair* b) +{ + assertT(c, vm::TargetBytesPerWord == 8 or size == 4); + + int64_t v = a->value->value(); + if (vm::fitsInInt8(v)) { + opcode(c, 0x83, 0xd8 + regCode(b)); + c->code.append(v); + } else { + opcode(c, 0x81, 0xd8 + regCode(b)); + c->code.append4(v); + } +} + +void subtractCR(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b) +{ + assertT(c, aSize == bSize); + + int64_t v = a->value->value(); + if (v) { + if (vm::TargetBytesPerWord == 4 and bSize == 8) { + ResolvedPromise high((v >> 32) & 0xFFFFFFFF); + lir::Constant ah(&high); + + ResolvedPromise low(v & 0xFFFFFFFF); + lir::Constant al(&low); + + lir::RegisterPair bh(b->high); + + subtractCR(c, 4, &al, 4, b); + subtractBorrowCR(c, 4, &ah, &bh); + } else { + if (vm::fitsInInt32(v)) { + maybeRex(c, aSize, b); + if (vm::fitsInInt8(v)) { + opcode(c, 0x83, 0xe8 + regCode(b)); + c->code.append(v); + } else { + opcode(c, 0x81, 0xe8 + regCode(b)); + c->code.append4(v); + } + } else { + lir::RegisterPair tmp(c->client->acquireTemporary(GeneralRegisterMask)); + moveCR(c, aSize, a, aSize, &tmp); + subtractRR(c, aSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); + } + } + } +} + +void subtractBorrowRR(Context* c, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b) +{ + assertT(c, vm::TargetBytesPerWord == 8 or size == 4); + + maybeRex(c, size, a, b); + opcode(c, 0x19); + modrm(c, 0xc0, b, a); +} + +void subtractRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + assertT(c, aSize == bSize); + + if (vm::TargetBytesPerWord == 4 and aSize == 8) { + lir::RegisterPair ah(a->high); + lir::RegisterPair bh(b->high); + + subtractRR(c, 4, a, 4, b); + subtractBorrowRR(c, 4, &ah, &bh); + } else { + maybeRex(c, aSize, a, b); + opcode(c, 0x29); + modrm(c, 0xc0, b, a); + } +} + +void andRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + assertT(c, aSize == bSize); + + if (vm::TargetBytesPerWord == 4 and aSize == 8) { + lir::RegisterPair ah(a->high); + lir::RegisterPair bh(b->high); + + andRR(c, 4, a, 4, b); + andRR(c, 4, &ah, 4, &bh); + } else { + maybeRex(c, aSize, a, b); + opcode(c, 0x21); + modrm(c, 0xc0, b, a); + } +} + +void andCR(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b) +{ + assertT(c, aSize == bSize); + + int64_t v = a->value->value(); + + if (vm::TargetBytesPerWord == 4 and bSize == 8) { + ResolvedPromise high((v >> 32) & 0xFFFFFFFF); + lir::Constant ah(&high); + + ResolvedPromise low(v & 0xFFFFFFFF); + lir::Constant al(&low); + + lir::RegisterPair bh(b->high); + + andCR(c, 4, &al, 4, b); + andCR(c, 4, &ah, 4, &bh); + } else { + if (vm::fitsInInt32(v)) { + maybeRex(c, aSize, b); + if (vm::fitsInInt8(v)) { + opcode(c, 0x83, 0xe0 + regCode(b)); + c->code.append(v); + } else { + opcode(c, 0x81, 0xe0 + regCode(b)); + c->code.append4(v); + } + } else { + lir::RegisterPair tmp(c->client->acquireTemporary(GeneralRegisterMask)); + moveCR(c, aSize, a, aSize, &tmp); + andRR(c, aSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); + } + } +} + +void orRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + assertT(c, aSize == bSize); + + if (vm::TargetBytesPerWord == 4 and aSize == 8) { + lir::RegisterPair ah(a->high); + lir::RegisterPair bh(b->high); + + orRR(c, 4, a, 4, b); + orRR(c, 4, &ah, 4, &bh); + } else { + maybeRex(c, aSize, a, b); + opcode(c, 0x09); + modrm(c, 0xc0, b, a); + } +} + +void orCR(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b) +{ + assertT(c, aSize == bSize); + + int64_t v = a->value->value(); + if (v) { + if (vm::TargetBytesPerWord == 4 and bSize == 8) { + ResolvedPromise high((v >> 32) & 0xFFFFFFFF); + lir::Constant ah(&high); + + ResolvedPromise low(v & 0xFFFFFFFF); + lir::Constant al(&low); + + lir::RegisterPair bh(b->high); + + orCR(c, 4, &al, 4, b); + orCR(c, 4, &ah, 4, &bh); + } else { + if (vm::fitsInInt32(v)) { + maybeRex(c, aSize, b); + if (vm::fitsInInt8(v)) { + opcode(c, 0x83, 0xc8 + regCode(b)); + c->code.append(v); + } else { + opcode(c, 0x81, 0xc8 + regCode(b)); + c->code.append4(v); + } + } else { + lir::RegisterPair tmp(c->client->acquireTemporary(GeneralRegisterMask)); + moveCR(c, aSize, a, aSize, &tmp); + orRR(c, aSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); + } + } + } +} + +void xorRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + if (vm::TargetBytesPerWord == 4 and aSize == 8) { + lir::RegisterPair ah(a->high); + lir::RegisterPair bh(b->high); + + xorRR(c, 4, a, 4, b); + xorRR(c, 4, &ah, 4, &bh); + } else { + maybeRex(c, aSize, a, b); + opcode(c, 0x31); + modrm(c, 0xc0, b, a); + } +} + +void xorCR(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b) +{ + assertT(c, aSize == bSize); + + int64_t v = a->value->value(); + if (v) { + if (vm::TargetBytesPerWord == 4 and bSize == 8) { + ResolvedPromise high((v >> 32) & 0xFFFFFFFF); + lir::Constant ah(&high); + + ResolvedPromise low(v & 0xFFFFFFFF); + lir::Constant al(&low); + + lir::RegisterPair bh(b->high); + + xorCR(c, 4, &al, 4, b); + xorCR(c, 4, &ah, 4, &bh); + } else { + if (vm::fitsInInt32(v)) { + maybeRex(c, aSize, b); + if (vm::fitsInInt8(v)) { + opcode(c, 0x83, 0xf0 + regCode(b)); + c->code.append(v); + } else { + opcode(c, 0x81, 0xf0 + regCode(b)); + c->code.append4(v); + } + } else { + lir::RegisterPair tmp(c->client->acquireTemporary(GeneralRegisterMask)); + moveCR(c, aSize, a, aSize, &tmp); + xorRR(c, aSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); + } + } + } +} + +void multiplyRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + assertT(c, aSize == bSize); + + if (vm::TargetBytesPerWord == 4 and aSize == 8) { + assertT(c, b->high == rdx); + assertT(c, b->low != rax); + assertT(c, a->low != rax); + assertT(c, a->high != rax); + + c->client->save(rax); + + lir::RegisterPair axdx(rax, rdx); + lir::RegisterPair ah(a->high); + lir::RegisterPair bh(b->high); + + lir::RegisterPair tmp(NoRegister); + lir::RegisterPair* scratch; + if (a->low == b->low) { + tmp.low = c->client->acquireTemporary(GeneralRegisterMask.excluding(rax)); + scratch = &tmp; + moveRR(c, 4, b, 4, scratch); + } else { + scratch = b; + } + + moveRR(c, 4, b, 4, &axdx); + multiplyRR(c, 4, &ah, 4, scratch); + multiplyRR(c, 4, a, 4, &bh); + addRR(c, 4, &bh, 4, scratch); + + // mul a->low,%eax%edx + opcode(c, 0xf7, 0xe0 + a->low.index()); + + addRR(c, 4, scratch, 4, &bh); + moveRR(c, 4, &axdx, 4, b); + + if (tmp.low != NoRegister) { + c->client->releaseTemporary(tmp.low); + } + } else { + maybeRex(c, aSize, b, a); + opcode(c, 0x0f, 0xaf); + modrm(c, 0xc0, a, b); + } +} + +void compareRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + assertT(c, aSize == bSize); + assertT(c, aSize <= vm::TargetBytesPerWord); + + maybeRex(c, aSize, a, b); + opcode(c, 0x39); + modrm(c, 0xc0, b, a); +} + +void compareCR(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b) +{ + assertT(c, aSize == bSize); + assertT(c, vm::TargetBytesPerWord == 8 or aSize == 4); + + if (a->value->resolved() and vm::fitsInInt32(a->value->value())) { + int64_t v = a->value->value(); + maybeRex(c, aSize, b); + if (vm::fitsInInt8(v)) { + opcode(c, 0x83, 0xf8 + regCode(b)); + c->code.append(v); + } else { + opcode(c, 0x81, 0xf8 + regCode(b)); + c->code.append4(v); + } + } else { + lir::RegisterPair tmp(c->client->acquireTemporary(GeneralRegisterMask)); + moveCR(c, aSize, a, aSize, &tmp); + compareRR(c, aSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); + } +} + +void compareRM(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::Memory* b) +{ + assertT(c, aSize == bSize); + assertT(c, vm::TargetBytesPerWord == 8 or aSize == 4); + + if (vm::TargetBytesPerWord == 8 and aSize == 4) { + moveRR(c, 4, a, 8, a); + } + maybeRex(c, bSize, a, b); + opcode(c, 0x39); + modrmSibImm(c, a, b); +} + +void compareCM(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::Memory* b) +{ + assertT(c, aSize == bSize); + assertT(c, vm::TargetBytesPerWord == 8 or aSize == 4); + + if (a->value->resolved()) { + int64_t v = a->value->value(); + maybeRex(c, aSize, b); + opcode(c, vm::fitsInInt8(v) ? 0x83 : 0x81); + modrmSibImm(c, rdi, b->scale, b->index, b->base, b->offset); + + if (vm::fitsInInt8(v)) { + c->code.append(v); + } else if (vm::fitsInInt32(v)) { + c->code.append4(v); + } else { + abort(c); + } + } else { + lir::RegisterPair tmp(c->client->acquireTemporary(GeneralRegisterMask)); + moveCR(c, aSize, a, bSize, &tmp); + compareRM(c, bSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); + } +} + +void compareFloatRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + assertT(c, aSize == bSize); + + if (aSize == 8) { + opcode(c, 0x66); + } + maybeRex(c, 4, a, b); + opcode(c, 0x0f, 0x2e); + modrm(c, 0xc0, a, b); +} + +void branchLong(Context* c, + lir::TernaryOperation op, + lir::Operand* al, + lir::Operand* ah, + lir::Operand* bl, + lir::Operand* bh, + lir::Constant* target, + BinaryOperationType compare) +{ + compare(c, 4, ah, 4, bh); + + unsigned next = 0; + + switch (op) { + case lir::JumpIfEqual: + opcode(c, 0x75); // jne + next = c->code.length(); + c->code.append(0); + + compare(c, 4, al, 4, bl); + conditional(c, 0x84, target); // je + break; + + case lir::JumpIfNotEqual: + conditional(c, 0x85, target); // jne + + compare(c, 4, al, 4, bl); + conditional(c, 0x85, target); // jne + break; + + case lir::JumpIfLess: + conditional(c, 0x8c, target); // jl + + opcode(c, 0x7f); // jg + next = c->code.length(); + c->code.append(0); + + compare(c, 4, al, 4, bl); + conditional(c, 0x82, target); // jb + break; + + case lir::JumpIfGreater: + conditional(c, 0x8f, target); // jg + + opcode(c, 0x7c); // jl + next = c->code.length(); + c->code.append(0); + + compare(c, 4, al, 4, bl); + conditional(c, 0x87, target); // ja + break; + + case lir::JumpIfLessOrEqual: + conditional(c, 0x8c, target); // jl + + opcode(c, 0x7f); // jg + next = c->code.length(); + c->code.append(0); + + compare(c, 4, al, 4, bl); + conditional(c, 0x86, target); // jbe + break; + + case lir::JumpIfGreaterOrEqual: + conditional(c, 0x8f, target); // jg + + opcode(c, 0x7c); // jl + next = c->code.length(); + c->code.append(0); + + compare(c, 4, al, 4, bl); + conditional(c, 0x83, target); // jae + break; + + default: + abort(c); + } + + if (next) { + int8_t nextOffset = c->code.length() - next - 1; + c->code.set(next, &nextOffset, 1); + } +} + +void branchRR(Context* c, + lir::TernaryOperation op, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::Constant* target) +{ + if (isFloatBranch(op)) { + compareFloatRR(c, size, a, size, b); + branchFloat(c, op, target); + } else if (size > vm::TargetBytesPerWord) { + lir::RegisterPair ah(a->high); + lir::RegisterPair bh(b->high); + + branchLong(c, op, a, &ah, b, &bh, target, CAST2(compareRR)); + } else { + compareRR(c, size, a, size, b); + branch(c, op, target); + } +} + +void branchCR(Context* c, + lir::TernaryOperation op, + unsigned size, + lir::Constant* a, + lir::RegisterPair* b, + lir::Constant* target) +{ + assertT(c, not isFloatBranch(op)); + + if (size > vm::TargetBytesPerWord) { + int64_t v = a->value->value(); + + ResolvedPromise low(v & ~static_cast(0)); + lir::Constant al(&low); + + ResolvedPromise high((v >> 32) & ~static_cast(0)); + lir::Constant ah(&high); + + lir::RegisterPair bh(b->high); + + branchLong(c, op, &al, &ah, b, &bh, target, CAST2(compareCR)); + } else { + compareCR(c, size, a, size, b); + branch(c, op, target); + } +} + +void branchRM(Context* c, + lir::TernaryOperation op, + unsigned size, + lir::RegisterPair* a, + lir::Memory* b, + lir::Constant* target) +{ + assertT(c, not isFloatBranch(op)); + assertT(c, size <= vm::TargetBytesPerWord); + + compareRM(c, size, a, size, b); + branch(c, op, target); +} + +void branchCM(Context* c, + lir::TernaryOperation op, + unsigned size, + lir::Constant* a, + lir::Memory* b, + lir::Constant* target) +{ + assertT(c, not isFloatBranch(op)); + assertT(c, size <= vm::TargetBytesPerWord); + + compareCM(c, size, a, size, b); + branch(c, op, target); +} + +void multiplyCR(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b) +{ + assertT(c, aSize == bSize); + + if (vm::TargetBytesPerWord == 4 and aSize == 8) { + const RegisterMask mask = GeneralRegisterMask.excluding(rax).excluding(rdx); + lir::RegisterPair tmp(c->client->acquireTemporary(mask), + c->client->acquireTemporary(mask)); + + moveCR(c, aSize, a, aSize, &tmp); + multiplyRR(c, aSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); + c->client->releaseTemporary(tmp.high); + } else { + int64_t v = a->value->value(); + if (v != 1) { + if (vm::fitsInInt32(v)) { + maybeRex(c, bSize, b, b); + if (vm::fitsInInt8(v)) { + opcode(c, 0x6b); + modrm(c, 0xc0, b, b); + c->code.append(v); + } else { + opcode(c, 0x69); + modrm(c, 0xc0, b, b); + c->code.append4(v); + } + } else { + lir::RegisterPair tmp(c->client->acquireTemporary(GeneralRegisterMask)); + moveCR(c, aSize, a, aSize, &tmp); + multiplyRR(c, aSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); + } + } + } +} + +void divideRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b UNUSED) +{ + assertT(c, aSize == bSize); + + assertT(c, b->low == rax); + assertT(c, a->low != rdx); + + c->client->save(rdx); + + maybeRex(c, aSize, a, b); + opcode(c, 0x99); // cdq + maybeRex(c, aSize, b, a); + opcode(c, 0xf7, 0xf8 + regCode(a)); +} + +void remainderRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + assertT(c, aSize == bSize); + + assertT(c, b->low == rax); + assertT(c, a->low != rdx); + + c->client->save(rdx); + + maybeRex(c, aSize, a, b); + opcode(c, 0x99); // cdq + maybeRex(c, aSize, b, a); + opcode(c, 0xf7, 0xf8 + regCode(a)); + + lir::RegisterPair dx(rdx); + moveRR(c, vm::TargetBytesPerWord, &dx, vm::TargetBytesPerWord, b); +} + +void doShift(Context* c, + UNUSED void (*shift)(Context*, + unsigned, + lir::RegisterPair*, + unsigned, + lir::RegisterPair*), + int type, + UNUSED unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b) +{ + int64_t v = a->value->value(); + + if (vm::TargetBytesPerWord == 4 and bSize == 8) { + c->client->save(rcx); + + lir::RegisterPair cx(rcx); + ResolvedPromise promise(v & 0x3F); + lir::Constant masked(&promise); + moveCR(c, 4, &masked, 4, &cx); + shift(c, aSize, &cx, bSize, b); + } else { + maybeRex(c, bSize, b); + if (v == 1) { + opcode(c, 0xd1, type + regCode(b)); + } else if (vm::fitsInInt8(v)) { + opcode(c, 0xc1, type + regCode(b)); + c->code.append(v); + } else { + abort(c); + } + } +} + +void shiftLeftRR(Context* c, + UNUSED unsigned aSize, + lir::RegisterPair* a, + unsigned bSize, + lir::RegisterPair* b) +{ + if (vm::TargetBytesPerWord == 4 and bSize == 8) { + lir::RegisterPair cx(rcx); + if (a->low != rcx) { + c->client->save(rcx); + ResolvedPromise promise(0x3F); + lir::Constant mask(&promise); + moveRR(c, 4, a, 4, &cx); + andCR(c, 4, &mask, 4, &cx); + } + + // shld + opcode(c, 0x0f, 0xa5); + modrm(c, 0xc0, b->high, b->low); + + // shl + opcode(c, 0xd3, 0xe0 + b->low.index()); + + ResolvedPromise promise(32); + lir::Constant constant(&promise); + compareCR( + c, vm::TargetBytesPerWord, &constant, vm::TargetBytesPerWord, &cx); + + opcode(c, 0x7c); // jl + c->code.append(2 + 2); + + lir::RegisterPair bh(b->high); + moveRR(c, 4, b, 4, &bh); // 2 bytes + xorRR(c, 4, b, 4, b); // 2 bytes + } else { + assertT(c, a->low == rcx); + + maybeRex(c, bSize, a, b); + opcode(c, 0xd3, 0xe0 + regCode(b)); + } +} + +void shiftLeftCR(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b) +{ + doShift(c, shiftLeftRR, 0xe0, aSize, a, bSize, b); +} + +void shiftRightRR(Context* c, + UNUSED unsigned aSize, + lir::RegisterPair* a, + unsigned bSize, + lir::RegisterPair* b) +{ + if (vm::TargetBytesPerWord == 4 and bSize == 8) { + lir::RegisterPair cx(rcx); + if (a->low != rcx) { + c->client->save(rcx); + ResolvedPromise promise(0x3F); + lir::Constant mask(&promise); + moveRR(c, 4, a, 4, &cx); + andCR(c, 4, &mask, 4, &cx); + } + + // shrd + opcode(c, 0x0f, 0xad); + modrm(c, 0xc0, b->low, b->high); + + // sar + opcode(c, 0xd3, 0xf8 + b->high.index()); + + ResolvedPromise promise(32); + lir::Constant constant(&promise); + compareCR( + c, vm::TargetBytesPerWord, &constant, vm::TargetBytesPerWord, &cx); + + opcode(c, 0x7c); // jl + c->code.append(2 + 3); + + lir::RegisterPair bh(b->high); + moveRR(c, 4, &bh, 4, b); // 2 bytes + + // sar 31,high + opcode(c, 0xc1, 0xf8 + b->high.index()); + c->code.append(31); + } else { + assertT(c, a->low == rcx); + + maybeRex(c, bSize, a, b); + opcode(c, 0xd3, 0xf8 + regCode(b)); + } +} + +void shiftRightCR(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b) +{ + doShift(c, shiftRightRR, 0xf8, aSize, a, bSize, b); +} + +void unsignedShiftRightRR(Context* c, + UNUSED unsigned aSize, + lir::RegisterPair* a, + unsigned bSize, + lir::RegisterPair* b) +{ + if (vm::TargetBytesPerWord == 4 and bSize == 8) { + lir::RegisterPair cx(rcx); + if (a->low != rcx) { + c->client->save(rcx); + ResolvedPromise promise(0x3F); + lir::Constant mask(&promise); + moveRR(c, 4, a, 4, &cx); + andCR(c, 4, &mask, 4, &cx); + } + + // shrd + opcode(c, 0x0f, 0xad); + modrm(c, 0xc0, b->low, b->high); + + // shr + opcode(c, 0xd3, 0xe8 + b->high.index()); + + ResolvedPromise promise(32); + lir::Constant constant(&promise); + compareCR( + c, vm::TargetBytesPerWord, &constant, vm::TargetBytesPerWord, &cx); + + opcode(c, 0x7c); // jl + c->code.append(2 + 2); + + lir::RegisterPair bh(b->high); + moveRR(c, 4, &bh, 4, b); // 2 bytes + xorRR(c, 4, &bh, 4, &bh); // 2 bytes + } else { + assertT(c, a->low == rcx); + + maybeRex(c, bSize, a, b); + opcode(c, 0xd3, 0xe8 + regCode(b)); + } +} + +void unsignedShiftRightCR(Context* c, + unsigned aSize UNUSED, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b) +{ + doShift(c, unsignedShiftRightRR, 0xe8, aSize, a, bSize, b); +} + +void floatSqrtRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + floatRegOp(c, aSize, a, 4, b, 0x51); +} + +void floatSqrtMR(Context* c, + unsigned aSize, + lir::Memory* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + floatMemOp(c, aSize, a, 4, b, 0x51); +} + +void floatAddRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + floatRegOp(c, aSize, a, 4, b, 0x58); +} + +void floatAddMR(Context* c, + unsigned aSize, + lir::Memory* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + floatMemOp(c, aSize, a, 4, b, 0x58); +} + +void floatSubtractRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + floatRegOp(c, aSize, a, 4, b, 0x5c); +} + +void floatSubtractMR(Context* c, + unsigned aSize, + lir::Memory* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + floatMemOp(c, aSize, a, 4, b, 0x5c); +} + +void floatMultiplyRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + floatRegOp(c, aSize, a, 4, b, 0x59); +} + +void floatMultiplyMR(Context* c, + unsigned aSize, + lir::Memory* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + floatMemOp(c, aSize, a, 4, b, 0x59); +} + +void floatDivideRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + floatRegOp(c, aSize, a, 4, b, 0x5e); +} + +void floatDivideMR(Context* c, + unsigned aSize, + lir::Memory* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + floatMemOp(c, aSize, a, 4, b, 0x5e); +} + +void float2FloatRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + floatRegOp(c, aSize, a, 4, b, 0x5a); +} + +void float2FloatMR(Context* c, + unsigned aSize, + lir::Memory* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + floatMemOp(c, aSize, a, 4, b, 0x5a); +} + +void float2IntRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize, + lir::RegisterPair* b) +{ + assertT(c, not isFloatReg(b)); + floatRegOp(c, aSize, a, bSize, b, 0x2c); +} + +void float2IntMR(Context* c, + unsigned aSize, + lir::Memory* a, + unsigned bSize, + lir::RegisterPair* b) +{ + floatMemOp(c, aSize, a, bSize, b, 0x2c); +} + +void int2FloatRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize, + lir::RegisterPair* b) +{ + floatRegOp(c, bSize, a, aSize, b, 0x2a); +} + +void int2FloatMR(Context* c, + unsigned aSize, + lir::Memory* a, + unsigned bSize, + lir::RegisterPair* b) +{ + floatMemOp(c, bSize, a, aSize, b, 0x2a); +} + +void floatNegateRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + assertT(c, isFloatReg(a) and isFloatReg(b)); + // unlike most of the other floating point code, this does NOT + // support doubles: + assertT(c, aSize == 4); + ResolvedPromise pcon(0x80000000); + lir::Constant con(&pcon); + if (a->low == b->low) { + lir::RegisterPair tmp(c->client->acquireTemporary(FloatRegisterMask)); + moveCR(c, 4, &con, 4, &tmp); + maybeRex(c, 4, a, &tmp); + opcode(c, 0x0f, 0x57); + modrm(c, 0xc0, &tmp, a); + c->client->releaseTemporary(tmp.low); + } else { + moveCR(c, 4, &con, 4, b); + if (aSize == 8) + opcode(c, 0x66); + maybeRex(c, 4, a, b); + opcode(c, 0x0f, 0x57); + modrm(c, 0xc0, a, b); + } +} + +void floatAbsoluteRR(Context* c, + unsigned aSize UNUSED, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b) +{ + assertT(c, isFloatReg(a) and isFloatReg(b)); + // unlike most of the other floating point code, this does NOT + // support doubles: + assertT(c, aSize == 4); + ResolvedPromise pcon(0x7fffffff); + lir::Constant con(&pcon); + if (a->low == b->low) { + lir::RegisterPair tmp(c->client->acquireTemporary(FloatRegisterMask)); + moveCR(c, 4, &con, 4, &tmp); + maybeRex(c, 4, a, &tmp); + opcode(c, 0x0f, 0x54); + modrm(c, 0xc0, &tmp, a); + c->client->releaseTemporary(tmp.low); + } else { + moveCR(c, 4, &con, 4, b); + maybeRex(c, 4, a, b); + opcode(c, 0x0f, 0x54); + modrm(c, 0xc0, a, b); + } +} + +void absoluteRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b UNUSED) +{ + assertT(c, aSize == bSize and a->low == rax and b->low == rax); + lir::RegisterPair d(c->client->acquireTemporary(rdx)); + maybeRex(c, aSize, a, b); + opcode(c, 0x99); + xorRR(c, aSize, &d, aSize, a); + subtractRR(c, aSize, &d, aSize, a); + c->client->releaseTemporary(rdx); +} + +} // namespace x86 +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/target/x86/operations.h b/sgx-jvm/avian/src/codegen/target/x86/operations.h new file mode 100644 index 0000000000..2101231e05 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/x86/operations.h @@ -0,0 +1,459 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_X86_OPERATIONS_H +#define AVIAN_CODEGEN_ASSEMBLER_X86_OPERATIONS_H + +#include "avian/common.h" + +#include + +#include "context.h" + +namespace avian { +namespace codegen { +namespace x86 { + +void return_(Context* c); + +void trap(Context* c); + +void ignore(Context*); + +void storeLoadBarrier(Context* c); + +void callC(Context* c, unsigned size UNUSED, lir::Constant* a); + +void longCallC(Context* c, unsigned size, lir::Constant* a); + +void jumpR(Context* c, unsigned size UNUSED, lir::RegisterPair* a); + +void jumpC(Context* c, unsigned size UNUSED, lir::Constant* a); + +void jumpM(Context* c, unsigned size UNUSED, lir::Memory* a); + +void longJumpC(Context* c, unsigned size, lir::Constant* a); + +void callR(Context* c, unsigned size UNUSED, lir::RegisterPair* a); + +void callM(Context* c, unsigned size UNUSED, lir::Memory* a); + +void alignedCallC(Context* c, unsigned size, lir::Constant* a); + +void alignedLongCallC(Context* c, unsigned size, lir::Constant* a); + +void alignedJumpC(Context* c, unsigned size, lir::Constant* a); + +void alignedLongJumpC(Context* c, unsigned size, lir::Constant* a); + +void pushR(Context* c, unsigned size, lir::RegisterPair* a); + +void popR(Context* c, unsigned size, lir::RegisterPair* a); + +void negateR(Context* c, unsigned size, lir::RegisterPair* a); + +void negateRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b UNUSED); + +void moveCR(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b); + +void moveZCR(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b); + +void swapRR(Context* c, + unsigned aSize UNUSED, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void moveRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + UNUSED unsigned bSize, + lir::RegisterPair* b); + +void moveMR(Context* c, + unsigned aSize, + lir::Memory* a, + unsigned bSize, + lir::RegisterPair* b); + +void moveRM(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::Memory* b); + +void moveAR(Context* c, + unsigned aSize, + lir::Address* a, + unsigned bSize, + lir::RegisterPair* b); + +void moveCM(Context* c, + unsigned aSize UNUSED, + lir::Constant* a, + unsigned bSize, + lir::Memory* b); + +void moveZRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void moveZMR(Context* c, + unsigned aSize UNUSED, + lir::Memory* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void addCarryRR(Context* c, unsigned size, lir::RegisterPair* a, lir::RegisterPair* b); + +void addRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void addCarryCR(Context* c, unsigned size, lir::Constant* a, lir::RegisterPair* b); + +void addCR(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b); + +void subtractBorrowCR(Context* c, + unsigned size UNUSED, + lir::Constant* a, + lir::RegisterPair* b); + +void subtractCR(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b); + +void subtractBorrowRR(Context* c, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b); + +void subtractRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void andRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void andCR(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b); + +void orRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void orCR(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b); + +void xorRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void xorCR(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b); + +void multiplyRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void compareRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void compareCR(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b); + +void compareRM(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::Memory* b); + +void compareCM(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::Memory* b); + +void compareFloatRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void branchLong(Context* c, + lir::TernaryOperation op, + lir::Operand* al, + lir::Operand* ah, + lir::Operand* bl, + lir::Operand* bh, + lir::Constant* target, + BinaryOperationType compare); + +void branchRR(Context* c, + lir::TernaryOperation op, + unsigned size, + lir::RegisterPair* a, + lir::RegisterPair* b, + lir::Constant* target); + +void branchCR(Context* c, + lir::TernaryOperation op, + unsigned size, + lir::Constant* a, + lir::RegisterPair* b, + lir::Constant* target); + +void branchRM(Context* c, + lir::TernaryOperation op, + unsigned size, + lir::RegisterPair* a, + lir::Memory* b, + lir::Constant* target); + +void branchCM(Context* c, + lir::TernaryOperation op, + unsigned size, + lir::Constant* a, + lir::Memory* b, + lir::Constant* target); + +void multiplyCR(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b); + +void divideRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b UNUSED); + +void remainderRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void doShift(Context* c, + UNUSED void (*shift)(Context*, + unsigned, + lir::RegisterPair*, + unsigned, + lir::RegisterPair*), + int type, + UNUSED unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b); + +void shiftLeftRR(Context* c, + UNUSED unsigned aSize, + lir::RegisterPair* a, + unsigned bSize, + lir::RegisterPair* b); + +void shiftLeftCR(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b); + +void shiftRightRR(Context* c, + UNUSED unsigned aSize, + lir::RegisterPair* a, + unsigned bSize, + lir::RegisterPair* b); + +void shiftRightCR(Context* c, + unsigned aSize, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b); + +void unsignedShiftRightRR(Context* c, + UNUSED unsigned aSize, + lir::RegisterPair* a, + unsigned bSize, + lir::RegisterPair* b); + +void unsignedShiftRightCR(Context* c, + unsigned aSize UNUSED, + lir::Constant* a, + unsigned bSize, + lir::RegisterPair* b); + +void floatSqrtRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void floatSqrtMR(Context* c, + unsigned aSize, + lir::Memory* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void floatAddRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void floatAddMR(Context* c, + unsigned aSize, + lir::Memory* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void floatSubtractRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void floatSubtractMR(Context* c, + unsigned aSize, + lir::Memory* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void floatMultiplyRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void floatMultiplyMR(Context* c, + unsigned aSize, + lir::Memory* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void floatDivideRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void floatDivideMR(Context* c, + unsigned aSize, + lir::Memory* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void float2FloatRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void float2FloatMR(Context* c, + unsigned aSize, + lir::Memory* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void float2IntRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize, + lir::RegisterPair* b); + +void float2IntMR(Context* c, + unsigned aSize, + lir::Memory* a, + unsigned bSize, + lir::RegisterPair* b); + +void int2FloatRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize, + lir::RegisterPair* b); + +void int2FloatMR(Context* c, + unsigned aSize, + lir::Memory* a, + unsigned bSize, + lir::RegisterPair* b); + +void floatNegateRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void floatAbsoluteRR(Context* c, + unsigned aSize UNUSED, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b); + +void absoluteRR(Context* c, + unsigned aSize, + lir::RegisterPair* a, + unsigned bSize UNUSED, + lir::RegisterPair* b UNUSED); + +} // namespace x86 +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_X86_OPERATIONS_H diff --git a/sgx-jvm/avian/src/codegen/target/x86/padding.cpp b/sgx-jvm/avian/src/codegen/target/x86/padding.cpp new file mode 100644 index 0000000000..930d05903e --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/x86/padding.cpp @@ -0,0 +1,71 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/alloc-vector.h" + +#include "context.h" +#include "padding.h" +#include "block.h" + +namespace avian { +namespace codegen { +namespace x86 { + +AlignmentPadding::AlignmentPadding(Context* c, + unsigned instructionOffset, + unsigned alignment) + : offset(c->code.length()), + instructionOffset(instructionOffset), + alignment(alignment), + next(0), + padding(-1) +{ + if (c->lastBlock->firstPadding) { + c->lastBlock->lastPadding->next = this; + } else { + c->lastBlock->firstPadding = this; + } + c->lastBlock->lastPadding = this; +} + +unsigned padding(AlignmentPadding* p, + unsigned start, + unsigned offset, + AlignmentPadding* limit) +{ + unsigned padding = 0; + if (limit) { + if (limit->padding == -1) { + for (; p; p = p->next) { + if (p->padding == -1) { + unsigned index = p->offset - offset; + while ((start + index + padding + p->instructionOffset) + % p->alignment) { + ++padding; + } + + p->padding = padding; + + if (p == limit) + break; + } else { + padding = p->padding; + } + } + } else { + padding = limit->padding; + } + } + return padding; +} + +} // namespace x86 +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/codegen/target/x86/padding.h b/sgx-jvm/avian/src/codegen/target/x86/padding.h new file mode 100644 index 0000000000..51fde450d7 --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/x86/padding.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_X86_PADDING_H +#define AVIAN_CODEGEN_ASSEMBLER_X86_PADDING_H + +namespace avian { +namespace codegen { +namespace x86 { + +class Context; + +class AlignmentPadding { + public: + AlignmentPadding(Context* c, unsigned instructionOffset, unsigned alignment); + + unsigned offset; + unsigned instructionOffset; + unsigned alignment; + AlignmentPadding* next; + int padding; +}; + +unsigned padding(AlignmentPadding* p, + unsigned start, + unsigned offset, + AlignmentPadding* limit); + +} // namespace x86 +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_X86_PADDING_H diff --git a/sgx-jvm/avian/src/codegen/target/x86/registers.h b/sgx-jvm/avian/src/codegen/target/x86/registers.h new file mode 100644 index 0000000000..3e614a552f --- /dev/null +++ b/sgx-jvm/avian/src/codegen/target/x86/registers.h @@ -0,0 +1,63 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_X86_REGISTERS_H +#define AVIAN_CODEGEN_ASSEMBLER_X86_REGISTERS_H + +namespace avian { +namespace codegen { +namespace x86 { + +constexpr Register rax((int)0); +constexpr Register rcx(1); +constexpr Register rdx(2); +constexpr Register rbx(3); +constexpr Register rsp(4); +constexpr Register rbp(5); +constexpr Register rsi(6); +constexpr Register rdi(7); +constexpr Register r8(8); +constexpr Register r9(9); +constexpr Register r10(10); +constexpr Register r11(11); +constexpr Register r12(12); +constexpr Register r13(13); +constexpr Register r14(14); +constexpr Register r15(15); +constexpr Register xmm0(16); +constexpr Register xmm1(16 + 1); +constexpr Register xmm2(16 + 2); +constexpr Register xmm3(16 + 3); +constexpr Register xmm4(16 + 4); +constexpr Register xmm5(16 + 5); +constexpr Register xmm6(16 + 6); +constexpr Register xmm7(16 + 7); +constexpr Register xmm8(16 + 8); +constexpr Register xmm9(16 + 9); +constexpr Register xmm10(16 + 10); +constexpr Register xmm11(16 + 11); +constexpr Register xmm12(16 + 12); +constexpr Register xmm13(16 + 13); +constexpr Register xmm14(16 + 14); +constexpr Register xmm15(16 + 15); + +constexpr Register LongJumpRegister = r10; + +constexpr RegisterMask GeneralRegisterMask = vm::TargetBytesPerWord == 4 ? 0x000000ff + : 0x0000ffff; + +constexpr RegisterMask FloatRegisterMask = vm::TargetBytesPerWord == 4 ? 0x00ff0000 + : 0xffff0000; + +} // namespace x86 +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_X86_REGISTERS_H diff --git a/sgx-jvm/avian/src/codegen/targets.cpp b/sgx-jvm/avian/src/codegen/targets.cpp new file mode 100644 index 0000000000..b4eabba5be --- /dev/null +++ b/sgx-jvm/avian/src/codegen/targets.cpp @@ -0,0 +1,42 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/common.h" + +#include + +#include "avian/environment.h" + +namespace avian { +namespace codegen { + +Architecture* makeArchitectureNative(vm::System* system, + bool useNativeFeatures UNUSED) +{ +#ifndef AVIAN_TARGET_ARCH +#error "Must specify native target!" +#endif + +#if AVIAN_TARGET_ARCH == AVIAN_ARCH_UNKNOWN + system->abort(); + return 0; +#elif(AVIAN_TARGET_ARCH == AVIAN_ARCH_X86) \ + || (AVIAN_TARGET_ARCH == AVIAN_ARCH_X86_64) + return makeArchitectureX86(system, useNativeFeatures); +#elif (AVIAN_TARGET_ARCH == AVIAN_ARCH_ARM) \ + || (AVIAN_TARGET_ARCH == AVIAN_ARCH_ARM64) + return makeArchitectureArm(system, useNativeFeatures); +#else +#error "Unsupported codegen target" +#endif +} + +} // namespace codegen +} // namespace avian diff --git a/sgx-jvm/avian/src/compile-arm.S b/sgx-jvm/avian/src/compile-arm.S new file mode 100644 index 0000000000..cd44d243a6 --- /dev/null +++ b/sgx-jvm/avian/src/compile-arm.S @@ -0,0 +1,255 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/types.h" +#include "avian/target-fields.h" + +.text + +#define BYTES_PER_WORD 4 + +#define LOCAL(x) .L##x + +#ifdef __APPLE__ +# define GLOBAL(x) _##x +#else +# define GLOBAL(x) x +#endif + +#define CONTINUATION_NEXT 4 +#define CONTINUATION_ADDRESS 16 +#define CONTINUATION_RETURN_ADDRESS_OFFSET 20 +#define CONTINUATION_FRAME_POINTER_OFFSET 24 +#define CONTINUATION_LENGTH 28 +#define CONTINUATION_BODY 32 + +.globl GLOBAL(vmInvoke) +.align 2 +GLOBAL(vmInvoke): + /* + arguments + r0 : thread + r1 : function + r2 : arguments + r3 : argumentFootprint + [sp, #0] : frameSize (not used) + [sp, #4] : returnType + */ + + // save all non-volatile registers + stmfd sp!, {r4-r11, lr} + + // save return type + ldr r4, [sp, #4] + str r4, [sp, #-4]! + + str sp, [r0, #TARGET_THREAD_SCRATCH] + + // align stack, if necessary + eor r4, sp, r3 + tst r4, #4 + subne sp, sp, #4 + + // copy arguments into place + sub sp, r3 + mov r4, #0 + b LOCAL(vmInvoke_argumentTest) + +LOCAL(vmInvoke_argumentLoop): + ldr r5, [r2, r4] + str r5, [sp, r4] + add r4, r4, #BYTES_PER_WORD + +LOCAL(vmInvoke_argumentTest): + cmp r4, r3 + blt LOCAL(vmInvoke_argumentLoop) + + // we use r8 to hold the thread pointer, by convention + mov r8, r0 + + // load and call function address +#if defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__) + mov lr, pc + bx r1 +#else + blx r1 +#endif + +.globl GLOBAL(vmInvoke_returnAddress) +.align 2 +GLOBAL(vmInvoke_returnAddress): + // restore stack pointer + ldr sp, [r8, #TARGET_THREAD_SCRATCH] + + // clear MyThread::stack to avoid confusing another thread calling + // java.lang.Thread.getStackTrace on this one. See + // MyProcess::getStackTrace in compile.cpp for details on how we get + // a reliable stack trace from a thread that might be interrupted at + // any point in its execution. + mov r5, #0 + str r5, [r8, #TARGET_THREAD_STACK] + +.globl GLOBAL(vmInvoke_safeStack) +.align 2 +GLOBAL(vmInvoke_safeStack): + +#ifdef AVIAN_CONTINUATIONS + // call the next continuation, if any + ldr r5,[r8,#TARGET_THREAD_CONTINUATION] + cmp r5,#0 + beq LOCAL(vmInvoke_exit) + + ldr r6,[r5,#CONTINUATION_LENGTH] + lsl r6,r6,#2 + neg r7,r6 + add r7,r7,#-80 // 80 bytes for callee-saved register values + mov r4,sp + str r4,[sp,r7]! + + add r7,r5,#CONTINUATION_BODY + + mov r11,#0 + b LOCAL(vmInvoke_continuationTest) + +LOCAL(vmInvoke_continuationLoop): + ldr r9,[r7,r11] + str r9,[sp,r11] + add r11,r11,#4 + +LOCAL(vmInvoke_continuationTest): + cmp r11,r6 + ble LOCAL(vmInvoke_continuationLoop) + + ldr r7,[r5,#CONTINUATION_RETURN_ADDRESS_OFFSET] +#ifdef __APPLE__ + movw r11, :lower16:(GLOBAL(vmInvoke_returnAddress)-(LOCAL(vmInvoke_getAddress)+8)) + movt r11, :upper16:(GLOBAL(vmInvoke_returnAddress)-(LOCAL(vmInvoke_getAddress)+8)) +LOCAL(vmInvoke_getAddress): + add r11, pc, r11 +#else // not __APPLE__ + ldr r10,LOCAL(vmInvoke_returnAddress_word) + ldr r11,LOCAL(vmInvoke_getAddress_word) +LOCAL(vmInvoke_getAddress): + add r11,pc,r11 + ldr r11,[r11,r10] +#endif // not __APPLE__ + str r11,[sp,r7] + + ldr r7,[r5,#CONTINUATION_NEXT] + str r7,[r8,#TARGET_THREAD_CONTINUATION] + + // call the continuation unless we're handling an exception + ldr r7,[r8,#TARGET_THREAD_EXCEPTION] + cmp r7,#0 + bne LOCAL(vmInvoke_handleException) + ldr r7,[r5,#CONTINUATION_ADDRESS] + bx r7 + +LOCAL(vmInvoke_handleException): + // we're handling an exception - call the exception handler instead + mov r11,#0 + str r11,[r8,#TARGET_THREAD_EXCEPTION] + ldr r11,[r8,#TARGET_THREAD_EXCEPTIONSTACKADJUSTMENT] + ldr r9,[sp] + neg r11,r11 + str r9,[sp,r11]! + ldr r11,[r8,#TARGET_THREAD_EXCEPTIONOFFSET] + str r7,[sp,r11] + + ldr r7,[r8,#TARGET_THREAD_EXCEPTIONHANDLER] + bx r7 + +LOCAL(vmInvoke_exit): + + mov ip, #0 + str ip, [r8, #TARGET_THREAD_STACK] +#endif // AVIAN_CONTINUATIONS + + // restore return type + ldr ip, [sp], #4 + + // restore callee-saved registers + ldmfd sp!, {r4-r11, lr} + +LOCAL(vmInvoke_return): + bx lr + +.globl GLOBAL(vmJumpAndInvoke) +.align 2 +GLOBAL(vmJumpAndInvoke): +#ifdef AVIAN_CONTINUATIONS + // r0: thread + // r1: address + // r2: stack + // r3: argumentFootprint + // [sp,#0]: arguments + // [sp,#4]: frameSize + + ldr r5,[sp,#0] + ldr r6,[sp,#4] + + // allocate new frame, adding room for callee-saved registers, plus + // 4 bytes of padding since the calculation of frameSize assumes 4 + // bytes have already been allocated to save the return address, + // which is not true in this case + sub r2,r2,r6 + sub r2,r2,#84 + + mov r8,r0 + + // copy arguments into place + mov r6,#0 + b LOCAL(vmJumpAndInvoke_argumentTest) + +LOCAL(vmJumpAndInvoke_argumentLoop): + ldr r12,[r5,r6] + str r12,[r2,r6] + add r6,r6,#4 + +LOCAL(vmJumpAndInvoke_argumentTest): + cmp r6,r3 + ble LOCAL(vmJumpAndInvoke_argumentLoop) + + // the arguments have been copied, so we can set the real stack + // pointer now + mov sp,r2 + + // set return address to vmInvoke_returnAddress +#ifdef __APPLE__ + movw r11, :lower16:(GLOBAL(vmInvoke_returnAddress)-(LOCAL(vmJumpAndInvoke_getAddress)+8)) + movt r11, :upper16:(GLOBAL(vmInvoke_returnAddress)-(LOCAL(vmJumpAndInvoke_getAddress)+8)) +LOCAL(vmJumpAndInvoke_getAddress): + add r11, pc, r11 +#else // not __APPLE__ + + ldr r10,LOCAL(vmInvoke_returnAddress_word) + ldr r11,LOCAL(vmJumpAndInvoke_getAddress_word) +LOCAL(vmJumpAndInvoke_getAddress): + add r11,pc,r11 +#endif // not __APPLE__ + ldr lr,[r11,r10] + + bx r1 + +#ifndef __APPLE__ +LOCAL(vmInvoke_returnAddress_word): + .word GLOBAL(vmInvoke_returnAddress)(GOT) +LOCAL(vmInvoke_getAddress_word): + .word _GLOBAL_OFFSET_TABLE_-(LOCAL(vmInvoke_getAddress)+8) +LOCAL(vmJumpAndInvoke_getAddress_word): + .word _GLOBAL_OFFSET_TABLE_-(LOCAL(vmJumpAndInvoke_getAddress)+8) +#endif // not __APPLE__ + +#else // not AVIAN_CONTINUATIONS + // vmJumpAndInvoke should only be called when continuations are + // enabled, so we force a crash if we reach here: + mov r1,#0 + ldr r1,[r1] +#endif // not AVIAN_CONTINUATIONS diff --git a/sgx-jvm/avian/src/compile-arm.masm b/sgx-jvm/avian/src/compile-arm.masm new file mode 100644 index 0000000000..ac02048372 --- /dev/null +++ b/sgx-jvm/avian/src/compile-arm.masm @@ -0,0 +1,252 @@ +; Copyright (c) 2008-2015, Avian Contributors +; +; Permission to use, copy, modify, and/or distribute this software +; for any purpose with or without fee is hereby granted, provided +; that the above copyright notice and this permission notice appear +; in all copies. +; +; There is NO WARRANTY for this software. See license.txt for +; details. +; +; ORIGIN: https://github.com/gkvas/avian/tree/wince + +; types.inc +VOID_TYPE equ 0 +INT8_TYPE equ 1 +INT16_TYPE equ 2 +INT32_TYPE equ 3 +INT64_TYPE equ 4 +FLOAT_TYPE equ 5 +DOUBLE_TYPE equ 6 +POINTER_TYPE equ 7 + +; target-fields.inc +;TARGET_BYTES_PER_WORD = 4 + +TARGET_THREAD_EXCEPTION equ 44 +TARGET_THREAD_EXCEPTIONSTACKADJUSTMENT equ 2164 +TARGET_THREAD_EXCEPTIONOFFSET equ 2168 +TARGET_THREAD_EXCEPTIONHANDLER equ 2172 + +TARGET_THREAD_IP equ 2144 +TARGET_THREAD_STACK equ 2148 +TARGET_THREAD_NEWSTACK equ 2152 +TARGET_THREAD_SCRATCH equ 2156 +TARGET_THREAD_CONTINUATION equ 2160 +TARGET_THREAD_TAILADDRESS equ 2176 +TARGET_THREAD_VIRTUALCALLTARGET equ 2180 +TARGET_THREAD_VIRTUALCALLINDEX equ 2184 +TARGET_THREAD_HEAPIMAGE equ 2188 +TARGET_THREAD_CODEIMAGE equ 2192 +TARGET_THREAD_THUNKTABLE equ 2196 +TARGET_THREAD_STACKLIMIT equ 2220 + + AREA text, CODE, ARM + +BYTES_PER_WORD equ 4 + +CONTINUATION_NEXT equ 4 +CONTINUATION_ADDRESS equ 16 +CONTINUATION_RETURN_ADDRESS_OFFSET equ 20 +CONTINUATION_FRAME_POINTER_OFFSET equ 24 +CONTINUATION_LENGTH equ 28 +CONTINUATION_BODY equ 32 + + EXPORT vmInvoke +vmInvoke + + ; arguments + ; r0 : thread + ; r1 : function + ; r2 : arguments + ; r3 : argumentFootprint + ; [sp, #0] : frameSize (not used) + ; [sp, #4] : returnType + + + ; save all non-volatile registers + stmfd sp!, {r4-r11, lr} + + ; save return type + ldr r4, [sp, #4] + str r4, [sp, #-4]! + + str sp, [r0, #TARGET_THREAD_SCRATCH] + + ; align stack, if necessary + eor r4, sp, r3 + tst r4, #4 + subne sp, sp, #4 + + ; copy arguments into place + sub sp, sp, r3 + mov r4, #0 + b vmInvoke_argumentTest + +vmInvoke_argumentLoop + ldr r5, [r2, r4] + str r5, [sp, r4] + add r4, r4, #BYTES_PER_WORD + +vmInvoke_argumentTest + cmp r4, r3 + blt vmInvoke_argumentLoop + + ; we use r8 to hold the thread pointer, by convention + mov r8, r0 + + ; load and call function address + blx r1 + + EXPORT vmInvoke_returnAddress +vmInvoke_returnAddress + ; restore stack pointer + ldr sp, [r8, #TARGET_THREAD_SCRATCH] + + ; clear MyThread::stack to avoid confusing another thread calling + ; java.lang.Thread.getStackTrace on this one. See + ; MyProcess::getStackTrace in compile.cpp for details on how we get + ; a reliable stack trace from a thread that might be interrupted at + ; any point in its execution. + mov r5, #0 + str r5, [r8, #TARGET_THREAD_STACK] + + EXPORT vmInvoke_safeStack +vmInvoke_safeStack + +;if AVIAN_CONTINUATIONS +; ; call the next continuation, if any +; ldr r5,[r8,#TARGET_THREAD_CONTINUATION] +; cmp r5,#0 +; beq vmInvoke_exit) +; +; ldr r6,[r5,#CONTINUATION_LENGTH] +; lsl r6,r6,#2 +; neg r7,r6 +; add r7,r7,#-80 +; mov r4,sp +; str r4,[sp,r7]! +; +; add r7,r5,#CONTINUATION_BODY +; +; mov r11,#0 +; b vmInvoke_continuationTest +; +;vmInvoke_continuationLoop +; ldr r9,[r7,r11] +; str r9,[sp,r11] +; add r11,r11,#4 +; +;vmInvoke_continuationTest +; cmp r11,r6 +; ble vmInvoke_continuationLoop) +; +; ldr r7,[r5,#CONTINUATION_RETURN_ADDRESS_OFFSET] +; ldr r10,vmInvoke_returnAddress_word +; ldr r11,vmInvoke_getAddress_word +;vmInvoke_getAddress +; add r11,pc,r11 +; ldr r11,[r11,r10] +; str r11,[sp,r7] +; +; ldr r7,[r5,#CONTINUATION_NEXT] +; str r7,[r8,#TARGET_THREAD_CONTINUATION] +; +; ; call the continuation unless we're handling an exception +; ldr r7,[r8,#TARGET_THREAD_EXCEPTION] +; cmp r7,#0 +; bne vmInvoke_handleException) +; ldr r7,[r5,#CONTINUATION_ADDRESS] +; bx r7 +; +;vmInvoke_handleException +; ; we're handling an exception - call the exception handler instead +; mov r11,#0 +; str r11,[r8,#TARGET_THREAD_EXCEPTION] +; ldr r11,[r8,#TARGET_THREAD_EXCEPTIONSTACKADJUSTMENT] +; ldr r9,[sp] +; neg r11,r11 +; str r9,[sp,r11]! +; ldr r11,[r8,#TARGET_THREAD_EXCEPTIONOFFSET] +; str r7,[sp,r11] +; +; ldr r7,[r8,#TARGET_THREAD_EXCEPTIONHANDLER] +; bx r7 +; +;vmInvoke_exit +;endif ; AVIAN_CONTINUATIONS + + mov ip, #0 + str ip, [r8, #TARGET_THREAD_STACK] + + ; restore return type + ldr ip, [sp], #4 + + ; restore callee-saved registers + ldmfd sp!, {r4-r11, lr} + +vmInvoke_return + bx lr + + EXPORT vmJumpAndInvoke +vmJumpAndInvoke +;if :DEF:AVIAN_CONTINUATIONS +; ; r0: thread +; ; r1: address +; ; r2: stack +; ; r3: argumentFootprint +; ; [sp,#0]: arguments +; ; [sp,#4]: frameSize +; +; ldr r5,[sp,#0] +; ldr r6,[sp,#4] +; +; ; allocate new frame, adding room for callee-saved registers, plus +; ; 4 bytes of padding since the calculation of frameSize assumes 4 +; ; bytes have already been allocated to save the return address, +; ; which is not true in this case +; sub r2,r2,r6 +; sub r2,r2,#84 +; +; mov r8,r0 +; +; ; copy arguments into place +; mov r6,#0 +; b vmJumpAndInvoke_argumentTest +; +;vmJumpAndInvoke_argumentLoop +; ldr r12,[r5,r6] +; str r12,[r2,r6] +; add r6,r6,#4 +; +;vmJumpAndInvoke_argumentTest +; cmp r6,r3 +; ble vmJumpAndInvoke_argumentLoop +; +; ; the arguments have been copied, so we can set the real stack +; ; pointer now +; mov sp,r2 +; +; ; set return address to vmInvoke_returnAddress +; ldr r10,vmInvoke_returnAddress_word) +; ldr r11,vmJumpAndInvoke_getAddress_word) +;vmJumpAndInvoke_getAddress +; add r11,pc,r11 +; ldr lr,[r11,r10] +; +; bx r1 +; +;vmInvoke_returnAddress_word +; .word GLOBAL(vmInvoke_returnAddress)(GOT) +;vmInvoke_getAddress_word +; .word _GLOBAL_OFFSET_TABLE_-(vmInvoke_getAddress)+8) +;vmJumpAndInvoke_getAddress_word +; .word _GLOBAL_OFFSET_TABLE_-(vmJumpAndInvoke_getAddress)+8) +; +;else ; not AVIAN_CONTINUATIONS + ; vmJumpAndInvoke should only be called when continuations are + ; enabled + bkpt 0 +;endif ; not AVIAN_CONTINUATIONS + + END \ No newline at end of file diff --git a/sgx-jvm/avian/src/compile-arm64.S b/sgx-jvm/avian/src/compile-arm64.S new file mode 100644 index 0000000000..f1b022e860 --- /dev/null +++ b/sgx-jvm/avian/src/compile-arm64.S @@ -0,0 +1,222 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/types.h" +#include "avian/target-fields.h" + +.text + +#define BYTES_PER_WORD 8 + +#define LOCAL(x) .L##x + +#ifdef __APPLE__ +# define GLOBAL(x) _##x +#else +# define GLOBAL(x) x +#endif + +#define CONTINUATION_NEXT 8 +#define CONTINUATION_ADDRESS 32 +#define CONTINUATION_RETURN_ADDRESS_OFFSET 40 +#define CONTINUATION_FRAME_POINTER_OFFSET 48 +#define CONTINUATION_LENGTH 56 +#define CONTINUATION_BODY 64 + +.globl GLOBAL(vmInvoke) +.align 2 +GLOBAL(vmInvoke): + // arguments: + // x0 : thread + // x1 : function + // x2 : arguments + // w3 : argumentFootprint + // w4 : frameSize (not used) + // w5 : returnType + + // allocate frame + stp x29, x30, [sp,#-96]! + mov x29, sp + + // save callee-saved register values + stp x19, x20, [sp,#16] + stp x21, x22, [sp,#32] + stp x23, x24, [sp,#48] + stp x25, x26, [sp,#64] + stp x27, x28, [sp,#80] + + // save return type + str w5, [sp,#-16]! + + mov x5, sp + str x5, [x0,#TARGET_THREAD_SCRATCH] + + // copy arguments into place, reserving enough space for them, plus + // alignment padding + sub x5, sp, w3, uxtw + and sp, x5, #-16 + + mov x4, #0 + b LOCAL(vmInvoke_argumentTest) + +LOCAL(vmInvoke_argumentLoop): + ldr x5, [x2, x4] + str x5, [sp, x4] + add x4, x4, #BYTES_PER_WORD + +LOCAL(vmInvoke_argumentTest): + cmp x4, x3 + b.lt LOCAL(vmInvoke_argumentLoop) + + // we use x19 to hold the thread pointer, by convention + mov x19, x0 + + // load and call function address + blr x1 + +.globl GLOBAL(vmInvoke_returnAddress) +.align 2 +GLOBAL(vmInvoke_returnAddress): + // restore stack pointer + ldr x5, [x19, #TARGET_THREAD_SCRATCH] + mov sp, x5 + + // clear MyThread::stack to avoid confusing another thread calling + // java.lang.Thread.getStackTrace on this one. See + // MyProcess::getStackTrace in compile.cpp for details on how we get + // a reliable stack trace from a thread that might be interrupted at + // any point in its execution. + str xzr, [x19, #TARGET_THREAD_STACK] + +.globl GLOBAL(vmInvoke_safeStack) +.align 2 +GLOBAL(vmInvoke_safeStack): + +#ifdef AVIAN_CONTINUATIONS + // call the next continuation, if any + ldr x5, [x19,#TARGET_THREAD_CONTINUATION] + cmp x5, xzr + b.eq LOCAL(vmInvoke_exit) + + ldr x6, [x5,#CONTINUATION_LENGTH] + lsl x6, x6, #3 + neg x7, x6 + add x7, x7, #-128 // 128 bytes for callee-saved register values + mov x4, sp + add sp, sp, x7 + str x4, [sp] + + add x7, x5, #CONTINUATION_BODY + mov x11, xzr + b LOCAL(vmInvoke_continuationTest) + +LOCAL(vmInvoke_continuationLoop): + ldr x9, [x7,x11] + str x9, [sp,x11] + add x11, x11, #8 + +LOCAL(vmInvoke_continuationTest): + cmp x11, x6 + b.le LOCAL(vmInvoke_continuationLoop) + + ldr x7, [x5,#CONTINUATION_RETURN_ADDRESS_OFFSET] + adr x11, GLOBAL(vmInvoke_returnAddress) + str x11, [sp,x7] + + ldr x7, [x5,#CONTINUATION_NEXT] + str x7, [x19,#TARGET_THREAD_CONTINUATION] + + // call the continuation unless we're handling an exception + ldr x7, [x19,#TARGET_THREAD_EXCEPTION] + cmp x7, xzr + b.ne LOCAL(vmInvoke_handleException) + ldr x7, [x5,#CONTINUATION_ADDRESS] + br x7 + +LOCAL(vmInvoke_handleException): + // we're handling an exception - call the exception handler instead + str xzr, [x19,#TARGET_THREAD_EXCEPTION] + ldr x11, [x19,#TARGET_THREAD_EXCEPTIONSTACKADJUSTMENT] + ldr x9, [sp] + neg x11, x11 + add sp, sp, x11 + str x9, [sp] + ldr x11, [x19,#TARGET_THREAD_EXCEPTIONOFFSET] + str x7, [sp,x11] + + ldr x7, [x19,#TARGET_THREAD_EXCEPTIONHANDLER] + br x7 + +LOCAL(vmInvoke_exit): + str xzr, [x19, #TARGET_THREAD_STACK] + +#endif // AVIAN_CONTINUATIONS + + // restore return type + ldr w5, [sp],#16 + + // restore callee-saved register values + ldp x19, x20, [sp,#16] + ldp x21, x22, [sp,#32] + ldp x23, x24, [sp,#48] + ldp x25, x26, [sp,#64] + ldp x27, x28, [sp,#80] + ldp x29, x30, [sp],#96 + +LOCAL(vmInvoke_return): + br x30 + +.globl GLOBAL(vmJumpAndInvoke) +.align 2 +GLOBAL(vmJumpAndInvoke): +#ifdef AVIAN_CONTINUATIONS + // x0: thread + // x1: address + // x2: stack + // x3: argumentFootprint + // x4: arguments + // x5: frameSize + + // allocate new frame, adding room for callee-saved registers, plus + // 8 bytes of padding since the calculation of frameSize assumes 8 + // bytes have already been allocated to save the return address, + // which is not true in this case + sub x2, x2, x5 + sub x2, x2, #136 + + mov x19, x0 + + // copy arguments into place + mov x6, xzr + b LOCAL(vmJumpAndInvoke_argumentTest) + +LOCAL(vmJumpAndInvoke_argumentLoop): + ldr x12, [x4,x6] + str x12, [x2,x6] + add x6, x6, #4 + +LOCAL(vmJumpAndInvoke_argumentTest): + cmp x6, x3 + ble LOCAL(vmJumpAndInvoke_argumentLoop) + + // the arguments have been copied, so we can set the real stack + // pointer now + mov sp, x2 + + // set return address to vmInvoke_returnAddress + adr x30, GLOBAL(vmInvoke_returnAddress) + + br x1 + +#else // not AVIAN_CONTINUATIONS + // vmJumpAndInvoke should only be called when continuations are + // enabled, so we force a crash if we reach here: + brk 0 +#endif // not AVIAN_CONTINUATIONS diff --git a/sgx-jvm/avian/src/compile-i386.S b/sgx-jvm/avian/src/compile-i386.S new file mode 100644 index 0000000000..d6ddd2052f --- /dev/null +++ b/sgx-jvm/avian/src/compile-i386.S @@ -0,0 +1,455 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/types.h" +#include "avian/target-fields.h" + +#define LOCAL(x) .L##x + +#if defined __APPLE__ \ + || ((defined __MINGW32__ || defined __CYGWIN32__) && ! defined __x86_64__) +# define GLOBAL(x) _##x +#else +# define GLOBAL(x) x +#endif + +.text + +#ifdef __x86_64__ + +#ifdef AVIAN_USE_FRAME_POINTER +# define ALIGNMENT_ADJUSTMENT 0 +#else +# define ALIGNMENT_ADJUSTMENT 8 +#endif + +#if defined __MINGW32__ || defined __CYGWIN32__ + +#define CALLEE_SAVED_REGISTER_FOOTPRINT 64 + ALIGNMENT_ADJUSTMENT + +.globl GLOBAL(vmInvoke) +GLOBAL(vmInvoke): + pushq %rbp + movq %rsp,%rbp + + // %rcx: thread + // %rdx: function + // %r8 : arguments + // %r9 : argumentsFootprint + // 48(%rbp) : frameSize + // 56(%rbp) : returnType (ignored) + + // allocate stack space for callee-saved registers + subq $CALLEE_SAVED_REGISTER_FOOTPRINT,%rsp + + // remember this stack position, since we won't be able to rely on + // %rbp being restored when the call returns + movq %rsp,TARGET_THREAD_SCRATCH(%rcx) + + // save callee-saved registers + movq %rbx,0(%rsp) + movq %r12,8(%rsp) + movq %r13,16(%rsp) + movq %r14,24(%rsp) + movq %r15,32(%rsp) + movq %rsi,40(%rsp) + movq %rdi,48(%rsp) + + // allocate stack space for arguments + movl 48(%rbp),%eax + subq %rax,%rsp + + // we use rbx to hold the thread pointer, by convention + mov %rcx,%rbx + + // copy arguments into place + movq $0,%r11 + jmp LOCAL(vmInvoke_argumentTest) + +LOCAL(vmInvoke_argumentLoop): + movq (%r8,%r11,1),%rsi + movq %rsi,(%rsp,%r11,1) + addq $8,%r11 + +LOCAL(vmInvoke_argumentTest): + cmpq %r9,%r11 + jb LOCAL(vmInvoke_argumentLoop) + + // call function + call *%rdx + +.globl GLOBAL(vmInvoke_returnAddress) +GLOBAL(vmInvoke_returnAddress): + // restore stack pointer + movq TARGET_THREAD_SCRATCH(%rbx),%rsp + + // clear MyThread::stack to avoid confusing another thread calling + // java.lang.Thread.getStackTrace on this one. See + // MyProcess::getStackTrace in compile.cpp for details on how we get + // a reliable stack trace from a thread that might be interrupted at + // any point in its execution. + movq $0,TARGET_THREAD_STACK(%rbx) + +.globl GLOBAL(vmInvoke_safeStack) +GLOBAL(vmInvoke_safeStack): + +#ifdef AVIAN_CONTINUATIONS +# include "continuations-x86.S" +#endif // AVIAN_CONTINUATIONS + + // restore callee-saved registers + movq 0(%rsp),%rbx + movq 8(%rsp),%r12 + movq 16(%rsp),%r13 + movq 24(%rsp),%r14 + movq 32(%rsp),%r15 + movq 40(%rsp),%rsi + movq 48(%rsp),%rdi + + addq $CALLEE_SAVED_REGISTER_FOOTPRINT,%rsp + + // return + popq %rbp + ret + +.globl GLOBAL(vmJumpAndInvoke) +GLOBAL(vmJumpAndInvoke): +#ifdef AVIAN_CONTINUATIONS + // %rcx: thread + // %rdx: address + // %r8 : stack + // %r9 : argumentFootprint + // 40(%rsp): arguments + // 48(%rsp): frameSize + + // allocate new frame, adding room for callee-saved registers + movl 48(%rsp),%eax + subq %rax,%r8 + subq $CALLEE_SAVED_REGISTER_FOOTPRINT,%r8 + + movq %rcx,%rbx + + // set return address + leaq GLOBAL(vmInvoke_returnAddress)(%rip),%r10 + movq %r10,(%r8) + + // copy arguments into place + movq $0,%r11 + movl 40(%rsp),%eax + jmp LOCAL(vmJumpAndInvoke_argumentTest) + +LOCAL(vmJumpAndInvoke_argumentLoop): + movq (%rax,%r11,1),%r10 + movq %r10,8(%r8,%r11,1) + addq $8,%r11 + +LOCAL(vmJumpAndInvoke_argumentTest): + cmpq %r9,%r11 + jb LOCAL(vmJumpAndInvoke_argumentLoop) + + // the arguments have been copied, so we can set the real stack + // pointer now + movq %r8,%rsp + + jmp *%rdx +#else // not AVIAN_CONTINUATIONS + // vmJumpAndInvoke should only be called when continuations are + // enabled + int3 +#endif // not AVIAN_CONTINUATIONS + +#else // not __MINGW32__ || __CYGWIN32__ + +#define CALLEE_SAVED_REGISTER_FOOTPRINT 48 + ALIGNMENT_ADJUSTMENT + +.globl GLOBAL(vmInvoke) +GLOBAL(vmInvoke): + pushq %rbp + movq %rsp,%rbp + + // %rdi: thread + // %rsi: function + // %rdx: arguments + // %rcx: argumentFootprint + // %r8 : frameSize + // %r9 : returnType (ignored) + + // allocate stack space for callee-saved registers + subq $CALLEE_SAVED_REGISTER_FOOTPRINT,%rsp + + // remember this stack position, since we won't be able to rely on + // %rbp being restored when the call returns + movq %rsp,TARGET_THREAD_SCRATCH(%rdi) + + // save callee-saved registers + movq %rbx,0(%rsp) + movq %r12,8(%rsp) + movq %r13,16(%rsp) + movq %r14,24(%rsp) + movq %r15,32(%rsp) + + // allocate stack space for arguments + subq %r8,%rsp + + // we use rbx to hold the thread pointer, by convention + mov %rdi,%rbx + + // copy arguments into place + movq $0,%r9 + jmp LOCAL(vmInvoke_argumentTest) + +LOCAL(vmInvoke_argumentLoop): + movq (%rdx,%r9,1),%r8 + movq %r8,(%rsp,%r9,1) + addq $8,%r9 + +LOCAL(vmInvoke_argumentTest): + cmpq %rcx,%r9 + jb LOCAL(vmInvoke_argumentLoop) + + // call function + call *%rsi + +.globl GLOBAL(vmInvoke_returnAddress) +GLOBAL(vmInvoke_returnAddress): + // restore stack pointer + movq TARGET_THREAD_SCRATCH(%rbx),%rsp + + // clear MyThread::stack to avoid confusing another thread calling + // java.lang.Thread.getStackTrace on this one. See + // MyProcess::getStackTrace in compile.cpp for details on how we get + // a reliable stack trace from a thread that might be interrupted at + // any point in its execution. + movq $0,TARGET_THREAD_STACK(%rbx) + +.globl GLOBAL(vmInvoke_safeStack) +GLOBAL(vmInvoke_safeStack): + +#ifdef AVIAN_CONTINUATIONS +# include "continuations-x86.S" +#endif // AVIAN_CONTINUATIONS + + // restore callee-saved registers + movq 0(%rsp),%rbx + movq 8(%rsp),%r12 + movq 16(%rsp),%r13 + movq 24(%rsp),%r14 + movq 32(%rsp),%r15 + + addq $CALLEE_SAVED_REGISTER_FOOTPRINT,%rsp + + // return + popq %rbp + ret + +.globl GLOBAL(vmJumpAndInvoke) +GLOBAL(vmJumpAndInvoke): +#ifdef AVIAN_CONTINUATIONS + // %rdi: thread + // %rsi: address + // %rdx: stack + // %rcx: argumentFootprint + // %r8 : arguments + // %r9 : frameSize + + // allocate new frame, adding room for callee-saved registers + subq %r9,%rdx + subq $CALLEE_SAVED_REGISTER_FOOTPRINT,%rdx + + movq %rdi,%rbx + + // set return address + movq GLOBAL(vmInvoke_returnAddress)@GOTPCREL(%rip),%r10 + movq %r10,(%rdx) + + // copy arguments into place + movq $0,%r11 + jmp LOCAL(vmJumpAndInvoke_argumentTest) + +LOCAL(vmJumpAndInvoke_argumentLoop): + movq (%r8,%r11,1),%r10 + movq %r10,8(%rdx,%r11,1) + addq $8,%r11 + +LOCAL(vmJumpAndInvoke_argumentTest): + cmpq %rcx,%r11 + jb LOCAL(vmJumpAndInvoke_argumentLoop) + + // the arguments have been copied, so we can set the real stack + // pointer now + movq %rdx,%rsp + + jmp *%rsi +#else // not AVIAN_CONTINUATIONS + // vmJumpAndInvoke should only be called when continuations are + // enabled + int3 +#endif // not AVIAN_CONTINUATIONS + +#endif // not __MINGW32__ || __CYGWIN32__ + +#elif defined __i386__ + +#ifdef AVIAN_USE_FRAME_POINTER +# define ALIGNMENT_ADJUSTMENT 0 +#else +# define ALIGNMENT_ADJUSTMENT 12 +#endif + +#define CALLEE_SAVED_REGISTER_FOOTPRINT 16 + ALIGNMENT_ADJUSTMENT + +.globl GLOBAL(vmInvoke) +GLOBAL(vmInvoke): + pushl %ebp + movl %esp,%ebp + + // 8(%ebp): thread + // 12(%ebp): function + // 16(%ebp): arguments + // 20(%ebp): argumentFootprint + // 24(%ebp): frameSize + // 28(%ebp): returnType + + // allocate stack space for callee-saved registers + subl $CALLEE_SAVED_REGISTER_FOOTPRINT,%esp + + // remember this stack position, since we won't be able to rely on + // %rbp being restored when the call returns + movl 8(%ebp),%eax + movl %esp,TARGET_THREAD_SCRATCH(%eax) + + movl %ebx,0(%esp) + movl %esi,4(%esp) + movl %edi,8(%esp) + + // allocate stack space for arguments + subl 24(%ebp),%esp + + // we use ebx to hold the thread pointer, by convention + mov %eax,%ebx + + // copy arguments into place + movl $0,%ecx + movl 16(%ebp),%edx + jmp LOCAL(vmInvoke_argumentTest) + +LOCAL(vmInvoke_argumentLoop): + movl (%edx,%ecx,1),%eax + movl %eax,(%esp,%ecx,1) + addl $4,%ecx + +LOCAL(vmInvoke_argumentTest): + cmpl 20(%ebp),%ecx + jb LOCAL(vmInvoke_argumentLoop) + + // call function + call *12(%ebp) + +.globl GLOBAL(vmInvoke_returnAddress) +GLOBAL(vmInvoke_returnAddress): + // restore stack pointer + movl TARGET_THREAD_SCRATCH(%ebx),%esp + + // clear MyThread::stack to avoid confusing another thread calling + // java.lang.Thread.getStackTrace on this one. See + // MyProcess::getStackTrace in compile.cpp for details on how we get + // a reliable stack trace from a thread that might be interrupted at + // any point in its execution. + movl $0,TARGET_THREAD_STACK(%ebx) + +.globl GLOBAL(vmInvoke_safeStack) +GLOBAL(vmInvoke_safeStack): + +#ifdef AVIAN_CONTINUATIONS +# include "continuations-x86.S" +#endif // AVIAN_CONTINUATIONS + + // restore callee-saved registers + movl 0(%esp),%ebx + movl 4(%esp),%esi + movl 8(%esp),%edi + + addl $CALLEE_SAVED_REGISTER_FOOTPRINT,%esp + + // handle return value based on expected type + movl 28(%esp),%ecx + + popl %ebp + ret + +LOCAL(getPC): + movl (%esp),%esi + ret + +.globl GLOBAL(vmJumpAndInvoke) +GLOBAL(vmJumpAndInvoke): +#ifdef AVIAN_CONTINUATIONS + // 4(%esp): thread + // 8(%esp): address + // 12(%esp): stack + // 16(%esp): argumentFootprint + // 20(%esp): arguments + // 24(%esp): frameSize + + movl 12(%esp),%ecx + + // allocate new frame, adding room for callee-saved registers, + // return address, and frame pointer + subl 24(%esp),%ecx + subl $CALLEE_SAVED_REGISTER_FOOTPRINT+8,%ecx + + movl 4(%esp),%ebx + + // set return address +#if defined __MINGW32__ || defined __CYGWIN32__ + movl $GLOBAL(vmInvoke_returnAddress),%esi +#else + call LOCAL(getPC) +# if defined __APPLE__ +LOCAL(vmJumpAndInvoke_offset): + leal GLOBAL(vmInvoke_returnAddress)-LOCAL(vmJumpAndInvoke_offset)(%esi),%esi +# else + addl $_GLOBAL_OFFSET_TABLE_,%esi + movl GLOBAL(vmInvoke_returnAddress)@GOT(%esi),%esi +# endif +#endif + movl %esi,(%ecx) + + // copy arguments into place + movl $0,%esi + movl 16(%esp),%edx + movl 20(%esp),%eax + jmp LOCAL(vmJumpAndInvoke_argumentTest) + +LOCAL(vmJumpAndInvoke_argumentLoop): + movl (%eax,%esi,1),%edi + movl %edi,4(%ecx,%esi,1) + addl $4,%esi + +LOCAL(vmJumpAndInvoke_argumentTest): + cmpl %edx,%esi + jb LOCAL(vmJumpAndInvoke_argumentLoop) + + movl 8(%esp),%esi + + // the arguments have been copied, so we can set the real stack + // pointer now + movl %ecx,%esp + + jmp *%esi +#else // not AVIAN_CONTINUATIONS + // vmJumpAndInvoke should only be called when continuations are + // enabled + int3 +#endif // AVIAN_CONTINUATIONS + +#else +#error unsupported architecture +#endif //def __x86_64__ diff --git a/sgx-jvm/avian/src/compile-i386.masm b/sgx-jvm/avian/src/compile-i386.masm new file mode 100644 index 0000000000..aff4cabb94 --- /dev/null +++ b/sgx-jvm/avian/src/compile-i386.masm @@ -0,0 +1,173 @@ +comment # + Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. + + ORIGIN: https://github.com/gkvas/avian/tree/wince +# + +.586 +.MODEL FLAT, C + +comment # types.h # +VOID_TYPE equ 0 +INT8_TYPE equ 1 +INT16_TYPE equ 2 +INT32_TYPE equ 3 +INT64_TYPE equ 4 +FLOAT_TYPE equ 5 +DOUBLE_TYPE equ 6 +POINTER_TYPE equ 7 + +comment # target-fields.h # +ifdef TARGET_BYTES_PER_WORD + if TARGET_BYTES_PER_WORD eq 8 + +TARGET_THREAD_EXCEPTION equ 80 +TARGET_THREAD_EXCEPTIONSTACKADJUSTMENT equ 2256 +TARGET_THREAD_EXCEPTIONOFFSET equ 2264 +TARGET_THREAD_EXCEPTIONHANDLER equ 2272 + +TARGET_THREAD_IP equ 2216 +TARGET_THREAD_STACK equ 2224 +TARGET_THREAD_NEWSTACK equ 2232 +TARGET_THREAD_SCRATCH equ 2240 +TARGET_THREAD_CONTINUATION equ 2248 +TARGET_THREAD_TAILADDRESS equ 2280 +TARGET_THREAD_VIRTUALCALLTARGET equ 2288 +TARGET_THREAD_VIRTUALCALLINDEX equ 2296 +TARGET_THREAD_HEAPIMAGE equ 2304 +TARGET_THREAD_CODEIMAGE equ 2312 +TARGET_THREAD_THUNKTABLE equ 2320 +TARGET_THREAD_STACKLIMIT equ 2368 + + elseif TARGET_BYTES_PER_WORD eq 4 + +TARGET_THREAD_EXCEPTION equ 44 +TARGET_THREAD_EXCEPTIONSTACKADJUSTMENT equ 2164 +TARGET_THREAD_EXCEPTIONOFFSET equ 2168 +TARGET_THREAD_EXCEPTIONHANDLER equ 2172 + +TARGET_THREAD_IP equ 2144 +TARGET_THREAD_STACK equ 2148 +TARGET_THREAD_NEWSTACK equ 2152 +TARGET_THREAD_SCRATCH equ 2156 +TARGET_THREAD_CONTINUATION equ 2160 +TARGET_THREAD_TAILADDRESS equ 2176 +TARGET_THREAD_VIRTUALCALLTARGET equ 2180 +TARGET_THREAD_VIRTUALCALLINDEX equ 2184 +TARGET_THREAD_HEAPIMAGE equ 2188 +TARGET_THREAD_CODEIMAGE equ 2192 +TARGET_THREAD_THUNKTABLE equ 2196 +TARGET_THREAD_STACKLIMIT equ 2220 + + else + error + endif +else + error +endif + +ifdef AVIAN_USE_FRAME_POINTER + ALIGNMENT_ADJUSTMENT equ 0 +else + ALIGNMENT_ADJUSTMENT equ 12 +endif + +CALLEE_SAVED_REGISTER_FOOTPRINT equ 16 + ALIGNMENT_ADJUSTMENT + +_TEXT SEGMENT + +public C vmInvoke +vmInvoke: + push ebp + mov ebp,esp + + ; 8(%ebp): thread + ; 12(%ebp): function + ; 16(%ebp): arguments + ; 20(%ebp): argumentFootprint + ; 24(%ebp): frameSize + ; 28(%ebp): returnType + + ; allocate stack space for callee-saved registers + sub esp,offset CALLEE_SAVED_REGISTER_FOOTPRINT + + ; remember this stack position, since we won't be able to rely on + ; %rbp being restored when the call returns + mov eax,ds:dword ptr[8+ebp] + mov ds:dword ptr[TARGET_THREAD_SCRATCH+eax],esp + + mov ds:dword ptr[0+esp],ebx + mov ds:dword ptr[4+esp],esi + mov ds:dword ptr[8+esp],edi + + ; allocate stack space for arguments + sub esp,ds:dword ptr[24+ebp] + + ; we use ebx to hold the thread pointer, by convention + mov ebx,eax + + ; copy arguments into place + mov ecx,0 + mov edx,ds:dword ptr[16+ebp] + jmp LvmInvoke_argumentTest + +LvmInvoke_argumentLoop: + mov eax,ds:dword ptr[edx+ecx*1] + mov ds:dword ptr[esp+ecx*1],eax + add ecx,4 + +LvmInvoke_argumentTest: + cmp ecx,ds:dword ptr[20+ebp] + jb LvmInvoke_argumentLoop + + ; call function + call dword ptr[12+ebp] + +public vmInvoke_returnAddress +vmInvoke_returnAddress: + ; restore stack pointer + mov esp,ds:dword ptr[TARGET_THREAD_SCRATCH+ebx] + + ; clear MyThread::stack to avoid confusing another thread calling + ; java.lang.Thread.getStackTrace on this one. See + ; MyProcess::getStackTrace in compile.cpp for details on how we get + ; a reliable stack trace from a thread that might be interrupted at + ; any point in its execution. + mov ds:dword ptr[TARGET_THREAD_STACK+ebx],0 + +public vmInvoke_safeStack +vmInvoke_safeStack: + + ; restore callee-saved registers + mov ebx,ds:dword ptr[0+esp] + mov esi,ds:dword ptr[4+esp] + mov edi,ds:dword ptr[8+esp] + + add esp,offset CALLEE_SAVED_REGISTER_FOOTPRINT + + mov ecx,ds:dword ptr[28+esp] + + pop ebp + ret + +LgetPC: + mov esi,ds:dword ptr[esp] + ret + +public vmJumpAndInvoke +vmJumpAndInvoke: + ; vmJumpAndInvoke should only be called when continuations are + ; enabled + int 3 + +_TEXT ENDS + +END diff --git a/sgx-jvm/avian/src/compile-x86_64.S b/sgx-jvm/avian/src/compile-x86_64.S new file mode 100644 index 0000000000..d6ddd2052f --- /dev/null +++ b/sgx-jvm/avian/src/compile-x86_64.S @@ -0,0 +1,455 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/types.h" +#include "avian/target-fields.h" + +#define LOCAL(x) .L##x + +#if defined __APPLE__ \ + || ((defined __MINGW32__ || defined __CYGWIN32__) && ! defined __x86_64__) +# define GLOBAL(x) _##x +#else +# define GLOBAL(x) x +#endif + +.text + +#ifdef __x86_64__ + +#ifdef AVIAN_USE_FRAME_POINTER +# define ALIGNMENT_ADJUSTMENT 0 +#else +# define ALIGNMENT_ADJUSTMENT 8 +#endif + +#if defined __MINGW32__ || defined __CYGWIN32__ + +#define CALLEE_SAVED_REGISTER_FOOTPRINT 64 + ALIGNMENT_ADJUSTMENT + +.globl GLOBAL(vmInvoke) +GLOBAL(vmInvoke): + pushq %rbp + movq %rsp,%rbp + + // %rcx: thread + // %rdx: function + // %r8 : arguments + // %r9 : argumentsFootprint + // 48(%rbp) : frameSize + // 56(%rbp) : returnType (ignored) + + // allocate stack space for callee-saved registers + subq $CALLEE_SAVED_REGISTER_FOOTPRINT,%rsp + + // remember this stack position, since we won't be able to rely on + // %rbp being restored when the call returns + movq %rsp,TARGET_THREAD_SCRATCH(%rcx) + + // save callee-saved registers + movq %rbx,0(%rsp) + movq %r12,8(%rsp) + movq %r13,16(%rsp) + movq %r14,24(%rsp) + movq %r15,32(%rsp) + movq %rsi,40(%rsp) + movq %rdi,48(%rsp) + + // allocate stack space for arguments + movl 48(%rbp),%eax + subq %rax,%rsp + + // we use rbx to hold the thread pointer, by convention + mov %rcx,%rbx + + // copy arguments into place + movq $0,%r11 + jmp LOCAL(vmInvoke_argumentTest) + +LOCAL(vmInvoke_argumentLoop): + movq (%r8,%r11,1),%rsi + movq %rsi,(%rsp,%r11,1) + addq $8,%r11 + +LOCAL(vmInvoke_argumentTest): + cmpq %r9,%r11 + jb LOCAL(vmInvoke_argumentLoop) + + // call function + call *%rdx + +.globl GLOBAL(vmInvoke_returnAddress) +GLOBAL(vmInvoke_returnAddress): + // restore stack pointer + movq TARGET_THREAD_SCRATCH(%rbx),%rsp + + // clear MyThread::stack to avoid confusing another thread calling + // java.lang.Thread.getStackTrace on this one. See + // MyProcess::getStackTrace in compile.cpp for details on how we get + // a reliable stack trace from a thread that might be interrupted at + // any point in its execution. + movq $0,TARGET_THREAD_STACK(%rbx) + +.globl GLOBAL(vmInvoke_safeStack) +GLOBAL(vmInvoke_safeStack): + +#ifdef AVIAN_CONTINUATIONS +# include "continuations-x86.S" +#endif // AVIAN_CONTINUATIONS + + // restore callee-saved registers + movq 0(%rsp),%rbx + movq 8(%rsp),%r12 + movq 16(%rsp),%r13 + movq 24(%rsp),%r14 + movq 32(%rsp),%r15 + movq 40(%rsp),%rsi + movq 48(%rsp),%rdi + + addq $CALLEE_SAVED_REGISTER_FOOTPRINT,%rsp + + // return + popq %rbp + ret + +.globl GLOBAL(vmJumpAndInvoke) +GLOBAL(vmJumpAndInvoke): +#ifdef AVIAN_CONTINUATIONS + // %rcx: thread + // %rdx: address + // %r8 : stack + // %r9 : argumentFootprint + // 40(%rsp): arguments + // 48(%rsp): frameSize + + // allocate new frame, adding room for callee-saved registers + movl 48(%rsp),%eax + subq %rax,%r8 + subq $CALLEE_SAVED_REGISTER_FOOTPRINT,%r8 + + movq %rcx,%rbx + + // set return address + leaq GLOBAL(vmInvoke_returnAddress)(%rip),%r10 + movq %r10,(%r8) + + // copy arguments into place + movq $0,%r11 + movl 40(%rsp),%eax + jmp LOCAL(vmJumpAndInvoke_argumentTest) + +LOCAL(vmJumpAndInvoke_argumentLoop): + movq (%rax,%r11,1),%r10 + movq %r10,8(%r8,%r11,1) + addq $8,%r11 + +LOCAL(vmJumpAndInvoke_argumentTest): + cmpq %r9,%r11 + jb LOCAL(vmJumpAndInvoke_argumentLoop) + + // the arguments have been copied, so we can set the real stack + // pointer now + movq %r8,%rsp + + jmp *%rdx +#else // not AVIAN_CONTINUATIONS + // vmJumpAndInvoke should only be called when continuations are + // enabled + int3 +#endif // not AVIAN_CONTINUATIONS + +#else // not __MINGW32__ || __CYGWIN32__ + +#define CALLEE_SAVED_REGISTER_FOOTPRINT 48 + ALIGNMENT_ADJUSTMENT + +.globl GLOBAL(vmInvoke) +GLOBAL(vmInvoke): + pushq %rbp + movq %rsp,%rbp + + // %rdi: thread + // %rsi: function + // %rdx: arguments + // %rcx: argumentFootprint + // %r8 : frameSize + // %r9 : returnType (ignored) + + // allocate stack space for callee-saved registers + subq $CALLEE_SAVED_REGISTER_FOOTPRINT,%rsp + + // remember this stack position, since we won't be able to rely on + // %rbp being restored when the call returns + movq %rsp,TARGET_THREAD_SCRATCH(%rdi) + + // save callee-saved registers + movq %rbx,0(%rsp) + movq %r12,8(%rsp) + movq %r13,16(%rsp) + movq %r14,24(%rsp) + movq %r15,32(%rsp) + + // allocate stack space for arguments + subq %r8,%rsp + + // we use rbx to hold the thread pointer, by convention + mov %rdi,%rbx + + // copy arguments into place + movq $0,%r9 + jmp LOCAL(vmInvoke_argumentTest) + +LOCAL(vmInvoke_argumentLoop): + movq (%rdx,%r9,1),%r8 + movq %r8,(%rsp,%r9,1) + addq $8,%r9 + +LOCAL(vmInvoke_argumentTest): + cmpq %rcx,%r9 + jb LOCAL(vmInvoke_argumentLoop) + + // call function + call *%rsi + +.globl GLOBAL(vmInvoke_returnAddress) +GLOBAL(vmInvoke_returnAddress): + // restore stack pointer + movq TARGET_THREAD_SCRATCH(%rbx),%rsp + + // clear MyThread::stack to avoid confusing another thread calling + // java.lang.Thread.getStackTrace on this one. See + // MyProcess::getStackTrace in compile.cpp for details on how we get + // a reliable stack trace from a thread that might be interrupted at + // any point in its execution. + movq $0,TARGET_THREAD_STACK(%rbx) + +.globl GLOBAL(vmInvoke_safeStack) +GLOBAL(vmInvoke_safeStack): + +#ifdef AVIAN_CONTINUATIONS +# include "continuations-x86.S" +#endif // AVIAN_CONTINUATIONS + + // restore callee-saved registers + movq 0(%rsp),%rbx + movq 8(%rsp),%r12 + movq 16(%rsp),%r13 + movq 24(%rsp),%r14 + movq 32(%rsp),%r15 + + addq $CALLEE_SAVED_REGISTER_FOOTPRINT,%rsp + + // return + popq %rbp + ret + +.globl GLOBAL(vmJumpAndInvoke) +GLOBAL(vmJumpAndInvoke): +#ifdef AVIAN_CONTINUATIONS + // %rdi: thread + // %rsi: address + // %rdx: stack + // %rcx: argumentFootprint + // %r8 : arguments + // %r9 : frameSize + + // allocate new frame, adding room for callee-saved registers + subq %r9,%rdx + subq $CALLEE_SAVED_REGISTER_FOOTPRINT,%rdx + + movq %rdi,%rbx + + // set return address + movq GLOBAL(vmInvoke_returnAddress)@GOTPCREL(%rip),%r10 + movq %r10,(%rdx) + + // copy arguments into place + movq $0,%r11 + jmp LOCAL(vmJumpAndInvoke_argumentTest) + +LOCAL(vmJumpAndInvoke_argumentLoop): + movq (%r8,%r11,1),%r10 + movq %r10,8(%rdx,%r11,1) + addq $8,%r11 + +LOCAL(vmJumpAndInvoke_argumentTest): + cmpq %rcx,%r11 + jb LOCAL(vmJumpAndInvoke_argumentLoop) + + // the arguments have been copied, so we can set the real stack + // pointer now + movq %rdx,%rsp + + jmp *%rsi +#else // not AVIAN_CONTINUATIONS + // vmJumpAndInvoke should only be called when continuations are + // enabled + int3 +#endif // not AVIAN_CONTINUATIONS + +#endif // not __MINGW32__ || __CYGWIN32__ + +#elif defined __i386__ + +#ifdef AVIAN_USE_FRAME_POINTER +# define ALIGNMENT_ADJUSTMENT 0 +#else +# define ALIGNMENT_ADJUSTMENT 12 +#endif + +#define CALLEE_SAVED_REGISTER_FOOTPRINT 16 + ALIGNMENT_ADJUSTMENT + +.globl GLOBAL(vmInvoke) +GLOBAL(vmInvoke): + pushl %ebp + movl %esp,%ebp + + // 8(%ebp): thread + // 12(%ebp): function + // 16(%ebp): arguments + // 20(%ebp): argumentFootprint + // 24(%ebp): frameSize + // 28(%ebp): returnType + + // allocate stack space for callee-saved registers + subl $CALLEE_SAVED_REGISTER_FOOTPRINT,%esp + + // remember this stack position, since we won't be able to rely on + // %rbp being restored when the call returns + movl 8(%ebp),%eax + movl %esp,TARGET_THREAD_SCRATCH(%eax) + + movl %ebx,0(%esp) + movl %esi,4(%esp) + movl %edi,8(%esp) + + // allocate stack space for arguments + subl 24(%ebp),%esp + + // we use ebx to hold the thread pointer, by convention + mov %eax,%ebx + + // copy arguments into place + movl $0,%ecx + movl 16(%ebp),%edx + jmp LOCAL(vmInvoke_argumentTest) + +LOCAL(vmInvoke_argumentLoop): + movl (%edx,%ecx,1),%eax + movl %eax,(%esp,%ecx,1) + addl $4,%ecx + +LOCAL(vmInvoke_argumentTest): + cmpl 20(%ebp),%ecx + jb LOCAL(vmInvoke_argumentLoop) + + // call function + call *12(%ebp) + +.globl GLOBAL(vmInvoke_returnAddress) +GLOBAL(vmInvoke_returnAddress): + // restore stack pointer + movl TARGET_THREAD_SCRATCH(%ebx),%esp + + // clear MyThread::stack to avoid confusing another thread calling + // java.lang.Thread.getStackTrace on this one. See + // MyProcess::getStackTrace in compile.cpp for details on how we get + // a reliable stack trace from a thread that might be interrupted at + // any point in its execution. + movl $0,TARGET_THREAD_STACK(%ebx) + +.globl GLOBAL(vmInvoke_safeStack) +GLOBAL(vmInvoke_safeStack): + +#ifdef AVIAN_CONTINUATIONS +# include "continuations-x86.S" +#endif // AVIAN_CONTINUATIONS + + // restore callee-saved registers + movl 0(%esp),%ebx + movl 4(%esp),%esi + movl 8(%esp),%edi + + addl $CALLEE_SAVED_REGISTER_FOOTPRINT,%esp + + // handle return value based on expected type + movl 28(%esp),%ecx + + popl %ebp + ret + +LOCAL(getPC): + movl (%esp),%esi + ret + +.globl GLOBAL(vmJumpAndInvoke) +GLOBAL(vmJumpAndInvoke): +#ifdef AVIAN_CONTINUATIONS + // 4(%esp): thread + // 8(%esp): address + // 12(%esp): stack + // 16(%esp): argumentFootprint + // 20(%esp): arguments + // 24(%esp): frameSize + + movl 12(%esp),%ecx + + // allocate new frame, adding room for callee-saved registers, + // return address, and frame pointer + subl 24(%esp),%ecx + subl $CALLEE_SAVED_REGISTER_FOOTPRINT+8,%ecx + + movl 4(%esp),%ebx + + // set return address +#if defined __MINGW32__ || defined __CYGWIN32__ + movl $GLOBAL(vmInvoke_returnAddress),%esi +#else + call LOCAL(getPC) +# if defined __APPLE__ +LOCAL(vmJumpAndInvoke_offset): + leal GLOBAL(vmInvoke_returnAddress)-LOCAL(vmJumpAndInvoke_offset)(%esi),%esi +# else + addl $_GLOBAL_OFFSET_TABLE_,%esi + movl GLOBAL(vmInvoke_returnAddress)@GOT(%esi),%esi +# endif +#endif + movl %esi,(%ecx) + + // copy arguments into place + movl $0,%esi + movl 16(%esp),%edx + movl 20(%esp),%eax + jmp LOCAL(vmJumpAndInvoke_argumentTest) + +LOCAL(vmJumpAndInvoke_argumentLoop): + movl (%eax,%esi,1),%edi + movl %edi,4(%ecx,%esi,1) + addl $4,%esi + +LOCAL(vmJumpAndInvoke_argumentTest): + cmpl %edx,%esi + jb LOCAL(vmJumpAndInvoke_argumentLoop) + + movl 8(%esp),%esi + + // the arguments have been copied, so we can set the real stack + // pointer now + movl %ecx,%esp + + jmp *%esi +#else // not AVIAN_CONTINUATIONS + // vmJumpAndInvoke should only be called when continuations are + // enabled + int3 +#endif // AVIAN_CONTINUATIONS + +#else +#error unsupported architecture +#endif //def __x86_64__ diff --git a/sgx-jvm/avian/src/compile.cpp b/sgx-jvm/avian/src/compile.cpp new file mode 100644 index 0000000000..4b3969a9e5 --- /dev/null +++ b/sgx-jvm/avian/src/compile.cpp @@ -0,0 +1,10778 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/machine.h" +#include "avian/util.h" +#include "avian/alloc-vector.h" +#include "avian/process.h" +#include "avian/target.h" +#include "avian/arch.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "debug-util.h" + +using namespace vm; + +extern "C" uint64_t vmInvoke(void* thread, + void* function, + void* arguments, + unsigned argumentFootprint, + unsigned frameSize, + unsigned returnType); + +extern "C" void vmInvoke_returnAddress(); + +extern "C" void vmInvoke_safeStack(); + +extern "C" void vmJumpAndInvoke(void* thread, + void* function, + void* stack, + unsigned argumentFootprint, + uintptr_t* arguments, + unsigned frameSize); + +using namespace avian::codegen; +using namespace avian::system; + +namespace { + +namespace local { + +const bool DebugCompile = false; +const bool DebugNatives = false; +const bool DebugCallTable = false; +const bool DebugMethodTree = false; +const bool DebugInstructions = false; + +#ifndef AVIAN_AOT_ONLY +const bool DebugFrameMaps = false; +const bool CheckArrayBounds = true; +const unsigned ExecutableAreaSizeInBytes = 30 * 1024 * 1024; +#endif + +#ifdef AVIAN_CONTINUATIONS +const bool Continuations = true; +#else +const bool Continuations = false; +#endif + +const unsigned MaxNativeCallFootprint = TargetBytesPerWord == 8 ? 4 : 5; + +const unsigned InitialZoneCapacityInBytes = 64 * 1024; + +enum ThunkIndex { + compileMethodIndex, + compileVirtualMethodIndex, + linkDynamicMethodIndex, + invokeNativeIndex, + throwArrayIndexOutOfBoundsIndex, + throwStackOverflowIndex, + +#define THUNK(s) s##Index, +#include "thunks.cpp" +#undef THUNK + dummyIndex +}; + +inline bool isVmInvokeUnsafeStack(void* ip) +{ + return reinterpret_cast(ip) + >= reinterpret_cast(voidPointer(vmInvoke_returnAddress)) + and reinterpret_cast(ip) + < reinterpret_cast(voidPointer(vmInvoke_safeStack)); +} + +class MyThread; + +void* getIp(MyThread*); + +class MyThread : public Thread { + public: + class CallTrace { + public: + CallTrace(MyThread* t, GcMethod* method) + : t(t), + ip(getIp(t)), + stack(t->stack), + scratch(t->scratch), + continuation(t->continuation), + nativeMethod((method->flags() & ACC_NATIVE) ? method : 0), + targetMethod(0), + originalMethod(method), + next(t->trace) + { + doTransition(t, 0, 0, 0, this); + } + + ~CallTrace() + { + assertT(t, t->stack == 0); + + t->scratch = scratch; + + doTransition(t, ip, stack, continuation, next); + } + + MyThread* t; + void* ip; + void* stack; + void* scratch; + GcContinuation* continuation; + GcMethod* nativeMethod; + GcMethod* targetMethod; + GcMethod* originalMethod; + CallTrace* next; + }; + + class Context { + public: + class MyProtector : public Thread::Protector { + public: + MyProtector(MyThread* t, Context* context) + : Protector(t), context(context) + { + } + + virtual void visit(Heap::Visitor* v) + { + v->visit(&(context->continuation)); + } + + Context* context; + }; + + Context(MyThread* t, + void* ip, + void* stack, + GcContinuation* continuation, + CallTrace* trace) + : ip(ip), + stack(stack), + continuation(continuation), + trace(trace), + protector(t, this) + { + } + + void* ip; + void* stack; + GcContinuation* continuation; + CallTrace* trace; + MyProtector protector; + }; + + class TraceContext : public Context { + public: + TraceContext(MyThread* t, + void* ip, + void* stack, + GcContinuation* continuation, + CallTrace* trace) + : Context(t, ip, stack, continuation, trace), + t(t), + link(0), + next(t->traceContext), + methodIsMostRecent(false) + { + t->traceContext = this; + } + + TraceContext(MyThread* t, void* link) + : Context(t, t->ip, t->stack, t->continuation, t->trace), + t(t), + link(link), + next(t->traceContext), + methodIsMostRecent(false) + { + t->traceContext = this; + } + + ~TraceContext() + { + t->traceContext = next; + } + + MyThread* t; + void* link; + TraceContext* next; + bool methodIsMostRecent; + }; + + static void doTransition(MyThread* t, + void* ip, + void* stack, + GcContinuation* continuation, + MyThread::CallTrace* trace) + { + // in this function, we "atomically" update the thread context + // fields in such a way to ensure that another thread may + // interrupt us at any time and still get a consistent, accurate + // stack trace. See MyProcessor::getStackTrace for details. + + assertT(t, t->transition == 0); + + Context c(t, ip, stack, continuation, trace); + + compileTimeMemoryBarrier(); + + t->transition = &c; + + compileTimeMemoryBarrier(); + + t->ip = ip; + t->stack = stack; + t->continuation = continuation; + t->trace = trace; + + compileTimeMemoryBarrier(); + + t->transition = 0; + } + + MyThread(Machine* m, + GcThread* javaThread, + MyThread* parent, + bool useNativeFeatures) + : Thread(m, javaThread, parent), + ip(0), + stack(0), + newStack(0), + scratch(0), + continuation(0), + exceptionStackAdjustment(0), + exceptionOffset(0), + exceptionHandler(0), + tailAddress(0), + virtualCallTarget(0), + virtualCallIndex(0), + heapImage(0), + codeImage(0), + thunkTable(0), + dynamicTable(0), + trace(0), + reference(0), + arch(parent ? parent->arch : avian::codegen::makeArchitectureNative( + m->system, + useNativeFeatures)), + transition(0), + traceContext(0), + stackLimit(0), + referenceFrame(0), + methodLockIsClean(true) + { + arch->acquire(); + } + + void* ip; + void* stack; + void* newStack; + void* scratch; + GcContinuation* continuation; + uintptr_t exceptionStackAdjustment; + uintptr_t exceptionOffset; + void* exceptionHandler; + void* tailAddress; + void* virtualCallTarget; + uintptr_t virtualCallIndex; + uintptr_t* heapImage; + uint8_t* codeImage; + void** thunkTable; + void** dynamicTable; + CallTrace* trace; + Reference* reference; + avian::codegen::Architecture* arch; + Context* transition; + TraceContext* traceContext; + uintptr_t stackLimit; + List* referenceFrame; + bool methodLockIsClean; +}; + +void transition(MyThread* t, + void* ip, + void* stack, + GcContinuation* continuation, + MyThread::CallTrace* trace) +{ + MyThread::doTransition(t, ip, stack, continuation, trace); +} + +object resolveThisPointer(MyThread* t, void* stack) +{ + return reinterpret_cast( + stack)[t->arch->frameFooterSize() + t->arch->frameReturnAddressSize()]; +} + +GcMethod* findMethod(Thread* t, GcMethod* method, object instance) +{ + if ((method->flags() & ACC_STATIC) == 0) { + if (method->class_()->flags() & ACC_INTERFACE) { + return findInterfaceMethod(t, method, objectClass(t, instance)); + } else if (methodVirtual(t, method)) { + return findVirtualMethod(t, method, objectClass(t, instance)); + } + } + return method; +} + +GcMethod* resolveTarget(MyThread* t, void* stack, GcMethod* method) +{ + GcClass* class_ = objectClass(t, resolveThisPointer(t, stack)); + + if (class_->vmFlags() & BootstrapFlag) { + PROTECT(t, method); + PROTECT(t, class_); + + resolveSystemClass(t, roots(t)->bootLoader(), class_->name()); + } + + if (method->class_()->flags() & ACC_INTERFACE) { + return findInterfaceMethod(t, method, class_); + } else { + return findVirtualMethod(t, method, class_); + } +} + +GcMethod* resolveTarget(MyThread* t, GcClass* class_, unsigned index) +{ + if (class_->vmFlags() & BootstrapFlag) { + PROTECT(t, class_); + + resolveSystemClass(t, roots(t)->bootLoader(), class_->name()); + } + + return cast( + t, cast(t, class_->virtualTable())->body()[index]); +} + +GcCompileRoots* compileRoots(Thread* t); + +intptr_t methodCompiled(Thread* t UNUSED, GcMethod* method) +{ + return method->code()->compiled(); +} + +unsigned methodCompiledSize(Thread* t UNUSED, GcMethod* method) +{ + return method->code()->compiledSize(); +} + +intptr_t compareIpToMethodBounds(Thread* t, intptr_t ip, object om) +{ + GcMethod* method = cast(t, om); + intptr_t start = methodCompiled(t, method); + + if (DebugMethodTree) { + fprintf(stderr, + "find %p in (%p,%p)\n", + reinterpret_cast(ip), + reinterpret_cast(start), + reinterpret_cast(start + methodCompiledSize(t, method))); + } + + if (ip < start) { + return -1; + } else if (ip < start + + static_cast(methodCompiledSize(t, method))) { + return 0; + } else { + return 1; + } +} + +GcMethod* methodForIp(MyThread* t, void* ip) +{ + if (DebugMethodTree) { + fprintf(stderr, "query for method containing %p\n", ip); + } + + // we must use a version of the method tree at least as recent as the + // compiled form of the method containing the specified address (see + // compile(MyThread*, FixedAllocator*, BootContext*, object)): + loadMemoryBarrier(); + + return cast(t, + treeQuery(t, + compileRoots(t)->methodTree(), + reinterpret_cast(ip), + compileRoots(t)->methodTreeSentinal(), + compareIpToMethodBounds)); +} + +unsigned localSize(MyThread* t UNUSED, GcMethod* method) +{ + unsigned size = method->code()->maxLocals(); + if ((method->flags() & (ACC_SYNCHRONIZED | ACC_STATIC)) == ACC_SYNCHRONIZED) { + ++size; + } + return size; +} + +unsigned alignedFrameSize(MyThread* t, GcMethod* method) +{ + return t->arch->alignFrameSize( + localSize(t, method) - method->parameterFootprint() + + method->code()->maxStack() + + t->arch->frameFootprint(MaxNativeCallFootprint)); +} + +void nextFrame(MyThread* t, + void** ip, + void** sp, + GcMethod* method, + GcMethod* target, + bool mostRecent) +{ + GcCode* code = method->code(); + intptr_t start = code->compiled(); + void* link; + bool methodIsMostRecent; + + if (t->traceContext) { + link = t->traceContext->link; + methodIsMostRecent = mostRecent and t->traceContext->methodIsMostRecent; + } else { + link = 0; + methodIsMostRecent = false; + } + + if (false) { + fprintf(stderr, + "nextFrame %s.%s%s target %s.%s%s ip %p sp %p\n", + method->class_()->name()->body().begin(), + method->name()->body().begin(), + method->spec()->body().begin(), + target ? target->class_()->name()->body().begin() : 0, + target ? target->name()->body().begin() : 0, + target ? target->spec()->body().begin() : 0, + *ip, + *sp); + } + + t->arch->nextFrame(reinterpret_cast(start), + code->compiledSize(), + alignedFrameSize(t, method), + link, + methodIsMostRecent, + target ? target->parameterFootprint() : -1, + ip, + sp); + + if (false) { + fprintf(stderr, "next frame ip %p sp %p\n", *ip, *sp); + } +} + +void* getIp(MyThread* t, void* ip, void* stack) +{ + // Here we use the convention that, if the return address is neither + // pushed on to the stack automatically as part of the call nor + // stored in the caller's frame, it will be saved in MyThread::ip + // instead of on the stack. See the various implementations of + // Assembler::saveFrame for details on how this is done. + return t->arch->returnAddressOffset() < 0 ? ip : t->arch->frameIp(stack); +} + +void* getIp(MyThread* t) +{ + return getIp(t, t->ip, t->stack); +} + +class MyStackWalker : public Processor::StackWalker { + public: + enum State { Start, Next, Trace, Continuation, Method, NativeMethod, Finish }; + + class MyProtector : public Thread::Protector { + public: + MyProtector(MyStackWalker* walker) : Protector(walker->t), walker(walker) + { + } + + virtual void visit(Heap::Visitor* v) + { + v->visit(&(walker->method_)); + v->visit(&(walker->target)); + v->visit(&(walker->continuation)); + } + + MyStackWalker* walker; + }; + + MyStackWalker(MyThread* t) + : t(t), state(Start), method_(0), target(0), count_(0), protector(this) + { + if (t->traceContext) { + ip_ = t->traceContext->ip; + stack = t->traceContext->stack; + trace = t->traceContext->trace; + continuation = t->traceContext->continuation; + } else { + ip_ = getIp(t); + stack = t->stack; + trace = t->trace; + continuation = t->continuation; + } + } + + MyStackWalker(MyStackWalker* w) + : t(w->t), + state(w->state), + ip_(w->ip_), + stack(w->stack), + trace(w->trace), + method_(w->method_), + target(w->target), + continuation(w->continuation), + count_(w->count_), + protector(this) + { + } + + virtual void walk(Processor::StackVisitor* v) + { + for (MyStackWalker it(this); it.valid();) { + MyStackWalker walker(&it); + if (not v->visit(&walker)) { + break; + } + it.next(); + } + } + + bool valid() + { + while (true) { + if (false) { + fprintf(stderr, "state: %d\n", state); + } + switch (state) { + case Start: + if (trace and trace->nativeMethod) { + method_ = trace->nativeMethod; + state = NativeMethod; + } else { + state = Next; + } + break; + + case Next: + if (stack) { + target = method_; + method_ = methodForIp(t, ip_); + if (method_) { + state = Method; + } else if (continuation) { + method_ = continuation->method(); + state = Continuation; + } else { + state = Trace; + } + } else { + state = Trace; + } + break; + + case Trace: { + if (trace) { + continuation = trace->continuation; + stack = trace->stack; + ip_ = trace->ip; + trace = trace->next; + + state = Start; + } else { + state = Finish; + } + } break; + + case Continuation: + case Method: + case NativeMethod: + return true; + + case Finish: + return false; + + default: + abort(t); + } + } + } + + void next() + { + expect(t, count_ <= stackSizeInWords(t)); + + switch (state) { + case Continuation: + continuation = continuation->next(); + break; + + case Method: + nextFrame(t, &ip_, &stack, method_, target, count_ == 0); + break; + + case NativeMethod: + break; + + default: + abort(t); + } + + ++count_; + + state = Next; + } + + virtual GcMethod* method() + { + if (false) { + fprintf(stderr, + "method %s.%s\n", + method_->class_()->name()->body().begin(), + method_->name()->body().begin()); + } + return method_; + } + + virtual int ip() + { + switch (state) { + case Continuation: + return reinterpret_cast(continuation->address()) + - methodCompiled(t, continuation->method()); + + case Method: + return reinterpret_cast(ip_) - methodCompiled(t, method_); + + case NativeMethod: + return 0; + + default: + abort(t); + } + } + + virtual unsigned count() + { + unsigned count = 0; + + for (MyStackWalker walker(this); walker.valid();) { + walker.next(); + ++count; + } + + return count; + } + + MyThread* t; + State state; + void* ip_; + void* stack; + MyThread::CallTrace* trace; + GcMethod* method_; + GcMethod* target; + GcContinuation* continuation; + unsigned count_; + MyProtector protector; +}; + +int localOffset(MyThread* t, int v, GcMethod* method) +{ + int parameterFootprint = method->parameterFootprint(); + int frameSize = alignedFrameSize(t, method); + + int offset + = ((v < parameterFootprint) + ? (frameSize + parameterFootprint + t->arch->frameFooterSize() + + t->arch->frameHeaderSize() - v - 1) + : (frameSize + parameterFootprint - v - 1)); + + assertT(t, offset >= 0); + return offset; +} + +int localOffsetFromStack(MyThread* t, int index, GcMethod* method) +{ + return localOffset(t, index, method) + t->arch->frameReturnAddressSize(); +} + +object* localObject(MyThread* t, void* stack, GcMethod* method, unsigned index) +{ + return static_cast(stack) + localOffsetFromStack(t, index, method); +} + +int stackOffsetFromFrame(MyThread* t, GcMethod* method) +{ + return alignedFrameSize(t, method) + t->arch->frameHeaderSize(); +} + +void* stackForFrame(MyThread* t, void* frame, GcMethod* method) +{ + return static_cast(frame) - stackOffsetFromFrame(t, method); +} + +class PoolElement : public avian::codegen::Promise { + public: + PoolElement(Thread* t, object target, PoolElement* next) + : t(t), target(target), address(0), next(next) + { + } + + virtual int64_t value() + { + assertT(t, resolved()); + return address; + } + + virtual bool resolved() + { + return address != 0; + } + + Thread* t; + object target; + intptr_t address; + PoolElement* next; +}; + +class Subroutine { + public: + Subroutine(unsigned index, + unsigned returnAddress, + unsigned methodSize, + Subroutine* outer) + : index(index), + outer(outer), + returnAddress(returnAddress), + duplicatedBaseIp(methodSize * index), + visited(false) + { + } + + // Index of this subroutine, in the (unmaterialized) list of subroutines in + // this method. + // Note that in the presence of nested finallys, this could theoretically end + // up being greater than the number of jsr instructions (but this will be + // extremely rare - I don't think we've seen this in practice). + const unsigned index; + + // Subroutine outer to this one (if, for instance, we have nested finallys) + Subroutine* const outer; + + // Starting ip in the original bytecode (always < original bytecode size) + const unsigned returnAddress; + + // Starting ip for this subroutine's copy of the method bytecode + const unsigned duplicatedBaseIp; + + bool visited; +}; + +class Context; + +class TraceElement : public avian::codegen::TraceHandler { + public: + static const unsigned VirtualCall = 1 << 0; + static const unsigned TailCall = 1 << 1; + static const unsigned LongCall = 1 << 2; + + TraceElement(Context* context, + unsigned ip, + GcMethod* target, + unsigned flags, + TraceElement* next, + unsigned mapSize) + : context(context), + address(0), + next(next), + target(target), + ip(ip), + argumentIndex(0), + flags(flags), + watch(false) + { + memset(map, 0xFF, mapSize * BytesPerWord); + } + + virtual void handleTrace(avian::codegen::Promise* address, + unsigned argumentIndex) + { + if (this->address == 0) { + this->address = address; + this->argumentIndex = argumentIndex; + } + } + + Context* context; + avian::codegen::Promise* address; + TraceElement* next; + GcMethod* target; + unsigned ip; + unsigned argumentIndex; + unsigned flags; + bool watch; + uintptr_t map[0]; +}; + +class TraceElementPromise : public avian::codegen::Promise { + public: + TraceElementPromise(System* s, TraceElement* trace) : s(s), trace(trace) + { + } + + virtual int64_t value() + { + assertT(s, resolved()); + return trace->address->value(); + } + + virtual bool resolved() + { + return trace->address != 0 and trace->address->resolved(); + } + + System* s; + TraceElement* trace; +}; + +enum Event { + PushContextEvent, + PopContextEvent, + IpEvent, + MarkEvent, + ClearEvent, + PushExceptionHandlerEvent, + TraceEvent, +}; + +unsigned frameMapSizeInBits(MyThread* t, GcMethod* method) +{ + return localSize(t, method) + method->code()->maxStack(); +} + +unsigned frameMapSizeInWords(MyThread* t, GcMethod* method) +{ + return ceilingDivide(frameMapSizeInBits(t, method), BitsPerWord); +} + +enum Thunk { +#define THUNK(s) s##Thunk, + +#include "thunks.cpp" + +#undef THUNK +}; + +const unsigned ThunkCount = idleIfNecessaryThunk + 1; + +intptr_t getThunk(MyThread* t, Thunk thunk); + +class BootContext { + public: + class MyProtector : public Thread::Protector { + public: + MyProtector(Thread* t, BootContext* c) : Protector(t), c(c) + { + } + + virtual void visit(Heap::Visitor* v) + { + v->visit(&(c->constants)); + v->visit(&(c->calls)); + } + + BootContext* c; + }; + + BootContext(Thread* t, + GcTriple* constants, + GcTriple* calls, + avian::codegen::DelayedPromise* addresses, + Zone* zone, + OffsetResolver* resolver, + JavaVM* hostVM) + : protector(t, this), + constants(constants), + calls(calls), + addresses(addresses), + addressSentinal(addresses), + zone(zone), + resolver(resolver), + hostVM(hostVM) + { + } + + MyProtector protector; + GcTriple* constants; + GcTriple* calls; + avian::codegen::DelayedPromise* addresses; + avian::codegen::DelayedPromise* addressSentinal; + Zone* zone; + OffsetResolver* resolver; + JavaVM* hostVM; +}; + +class Context { + public: + class MyResource : public Thread::AutoResource { + public: + MyResource(Context* c) : AutoResource(c->thread), c(c) + { + } + + virtual void release() + { + c->dispose(); + } + + Context* c; + }; + + class MyProtector : public Thread::Protector { + public: + MyProtector(Context* c) : Protector(c->thread), c(c) + { + } + + virtual void visit(Heap::Visitor* v) + { + v->visit(&(c->method)); + + for (PoolElement* p = c->objectPool; p; p = p->next) { + v->visit(&(p->target)); + } + + for (TraceElement* p = c->traceLog; p; p = p->next) { + v->visit(&(p->target)); + } + } + + Context* c; + }; + + class MyClient : public Compiler::Client { + public: + MyClient(MyThread* t) : t(t) + { + } + + virtual intptr_t getThunk(avian::codegen::lir::UnaryOperation, unsigned) + { + abort(t); + } + + virtual intptr_t getThunk(avian::codegen::lir::BinaryOperation op, + unsigned size, + unsigned resultSize) + { + if (size == 8) { + switch (op) { + case avian::codegen::lir::Absolute: + assertT(t, resultSize == 8); + return local::getThunk(t, absoluteLongThunk); + + case avian::codegen::lir::FloatNegate: + assertT(t, resultSize == 8); + return local::getThunk(t, negateDoubleThunk); + + case avian::codegen::lir::FloatSquareRoot: + assertT(t, resultSize == 8); + return local::getThunk(t, squareRootDoubleThunk); + + case avian::codegen::lir::Float2Float: + assertT(t, resultSize == 4); + return local::getThunk(t, doubleToFloatThunk); + + case avian::codegen::lir::Float2Int: + if (resultSize == 8) { + return local::getThunk(t, doubleToLongThunk); + } else { + assertT(t, resultSize == 4); + return local::getThunk(t, doubleToIntThunk); + } + + case avian::codegen::lir::Int2Float: + if (resultSize == 8) { + return local::getThunk(t, longToDoubleThunk); + } else { + assertT(t, resultSize == 4); + return local::getThunk(t, longToFloatThunk); + } + + default: + abort(t); + } + } else { + assertT(t, size == 4); + + switch (op) { + case avian::codegen::lir::Absolute: + assertT(t, resultSize == 4); + return local::getThunk(t, absoluteIntThunk); + + case avian::codegen::lir::FloatNegate: + assertT(t, resultSize == 4); + return local::getThunk(t, negateFloatThunk); + + case avian::codegen::lir::FloatAbsolute: + assertT(t, resultSize == 4); + return local::getThunk(t, absoluteFloatThunk); + + case avian::codegen::lir::Float2Float: + assertT(t, resultSize == 8); + return local::getThunk(t, floatToDoubleThunk); + + case avian::codegen::lir::Float2Int: + if (resultSize == 4) { + return local::getThunk(t, floatToIntThunk); + } else { + assertT(t, resultSize == 8); + return local::getThunk(t, floatToLongThunk); + } + + case avian::codegen::lir::Int2Float: + if (resultSize == 4) { + return local::getThunk(t, intToFloatThunk); + } else { + assertT(t, resultSize == 8); + return local::getThunk(t, intToDoubleThunk); + } + + default: + abort(t); + } + } + } + + virtual intptr_t getThunk(avian::codegen::lir::TernaryOperation op, + unsigned size, + unsigned, + bool* threadParameter) + { + *threadParameter = false; + + if (size == 8) { + switch (op) { + case avian::codegen::lir::Divide: + *threadParameter = true; + return local::getThunk(t, divideLongThunk); + + case avian::codegen::lir::Remainder: + *threadParameter = true; + return local::getThunk(t, moduloLongThunk); + + case avian::codegen::lir::FloatAdd: + return local::getThunk(t, addDoubleThunk); + + case avian::codegen::lir::FloatSubtract: + return local::getThunk(t, subtractDoubleThunk); + + case avian::codegen::lir::FloatMultiply: + return local::getThunk(t, multiplyDoubleThunk); + + case avian::codegen::lir::FloatDivide: + return local::getThunk(t, divideDoubleThunk); + + case avian::codegen::lir::FloatRemainder: + return local::getThunk(t, moduloDoubleThunk); + + case avian::codegen::lir::JumpIfFloatEqual: + case avian::codegen::lir::JumpIfFloatNotEqual: + case avian::codegen::lir::JumpIfFloatLess: + case avian::codegen::lir::JumpIfFloatGreater: + case avian::codegen::lir::JumpIfFloatLessOrEqual: + case avian::codegen::lir::JumpIfFloatGreaterOrUnordered: + case avian::codegen::lir::JumpIfFloatGreaterOrEqualOrUnordered: + return local::getThunk(t, compareDoublesGThunk); + + case avian::codegen::lir::JumpIfFloatGreaterOrEqual: + case avian::codegen::lir::JumpIfFloatLessOrUnordered: + case avian::codegen::lir::JumpIfFloatLessOrEqualOrUnordered: + return local::getThunk(t, compareDoublesLThunk); + + default: + abort(t); + } + } else { + assertT(t, size == 4); + switch (op) { + case avian::codegen::lir::Divide: + *threadParameter = true; + return local::getThunk(t, divideIntThunk); + + case avian::codegen::lir::Remainder: + *threadParameter = true; + return local::getThunk(t, moduloIntThunk); + + case avian::codegen::lir::FloatAdd: + return local::getThunk(t, addFloatThunk); + + case avian::codegen::lir::FloatSubtract: + return local::getThunk(t, subtractFloatThunk); + + case avian::codegen::lir::FloatMultiply: + return local::getThunk(t, multiplyFloatThunk); + + case avian::codegen::lir::FloatDivide: + return local::getThunk(t, divideFloatThunk); + + case avian::codegen::lir::FloatRemainder: + return local::getThunk(t, moduloFloatThunk); + + case avian::codegen::lir::JumpIfFloatEqual: + case avian::codegen::lir::JumpIfFloatNotEqual: + case avian::codegen::lir::JumpIfFloatLess: + case avian::codegen::lir::JumpIfFloatGreater: + case avian::codegen::lir::JumpIfFloatLessOrEqual: + case avian::codegen::lir::JumpIfFloatGreaterOrUnordered: + case avian::codegen::lir::JumpIfFloatGreaterOrEqualOrUnordered: + return local::getThunk(t, compareFloatsGThunk); + + case avian::codegen::lir::JumpIfFloatGreaterOrEqual: + case avian::codegen::lir::JumpIfFloatLessOrUnordered: + case avian::codegen::lir::JumpIfFloatLessOrEqualOrUnordered: + return local::getThunk(t, compareFloatsLThunk); + + default: + abort(t); + } + } + } + + MyThread* t; + }; + + Context(MyThread* t, BootContext* bootContext, GcMethod* method) + : thread(t), + zone(t->m->heap, InitialZoneCapacityInBytes), + assembler(t->arch->makeAssembler(t->m->heap, &zone)), + client(t), + compiler(makeCompiler(t->m->system, assembler, &zone, &client)), + method(method), + bootContext(bootContext), + objectPool(0), + subroutineCount(0), + traceLog(0), + visitTable( + Slice::allocAndSet(&zone, method->code()->length(), 0)), + rootTable(Slice::allocAndSet( + &zone, + method->code()->length() * frameMapSizeInWords(t, method), + ~(uintptr_t)0)), + executableAllocator(0), + executableStart(0), + executableSize(0), + objectPoolCount(0), + traceLogCount(0), + dirtyRoots(false), + leaf(true), + eventLog(t->m->system, t->m->heap, 1024), + protector(this), + resource(this), + argumentBuffer( + (ir::Value**)t->m->heap->allocate(256 * sizeof(ir::Value*)), + 256) // below the maximal allowed parameter count for Java + { + } + + Context(MyThread* t) + : thread(t), + zone(t->m->heap, InitialZoneCapacityInBytes), + assembler(t->arch->makeAssembler(t->m->heap, &zone)), + client(t), + compiler(0), + method(0), + bootContext(0), + objectPool(0), + subroutineCount(0), + traceLog(0), + visitTable(0, 0), + rootTable(0, 0), + executableAllocator(0), + executableStart(0), + executableSize(0), + objectPoolCount(0), + traceLogCount(0), + dirtyRoots(false), + leaf(true), + eventLog(t->m->system, t->m->heap, 0), + protector(this), + resource(this), + argumentBuffer(0, 0) + { + } + + ~Context() + { + dispose(); + } + + void dispose() + { + if (compiler) { + compiler->dispose(); + } + + assembler->dispose(); + + if (executableAllocator) { + executableAllocator->free(executableStart, executableSize); + } + + eventLog.dispose(); + + zone.dispose(); + + if (argumentBuffer.begin()) { + thread->m->heap->free(argumentBuffer.begin(), 256 * sizeof(ir::Value*)); + } + } + + void extendLogicalCode(unsigned more) + { + compiler->extendLogicalCode(more); + visitTable = visitTable.cloneAndSet(&zone, visitTable.count + more, 0); + rootTable = rootTable.cloneAndSet( + &zone, + rootTable.count + more * frameMapSizeInWords(thread, method), + ~(uintptr_t)0); + } + + MyThread* thread; + Zone zone; + avian::codegen::Assembler* assembler; + MyClient client; + avian::codegen::Compiler* compiler; + GcMethod* method; + BootContext* bootContext; + PoolElement* objectPool; + unsigned subroutineCount; + TraceElement* traceLog; + Slice visitTable; + Slice rootTable; + Alloc* executableAllocator; + void* executableStart; + unsigned executableSize; + unsigned objectPoolCount; + unsigned traceLogCount; + bool dirtyRoots; + bool leaf; + Vector eventLog; + MyProtector protector; + MyResource resource; + Slice argumentBuffer; +}; + +unsigned& dynamicIndex(MyThread* t); + +void**& dynamicTable(MyThread* t); + +unsigned& dynamicTableSize(MyThread* t); + +void updateDynamicTable(MyThread* t, MyThread* o) +{ + o->dynamicTable = dynamicTable(t); + if (t->peer) + updateDynamicTable(static_cast(t->peer), o); + if (t->child) + updateDynamicTable(static_cast(t->child), o); +} + +uintptr_t defaultDynamicThunk(MyThread* t); + +uintptr_t compileVirtualThunk(MyThread* t, + unsigned index, + unsigned* size, + uintptr_t thunk, + const char* baseName); + +Allocator* allocator(MyThread* t); + +unsigned addDynamic(MyThread* t, GcInvocation* invocation) +{ + ACQUIRE(t, t->m->classLock); + + int index = invocation->index(); + if (index == -1) { + index = dynamicIndex(t)++; + invocation->index() = index; + + unsigned oldCapacity = roots(t)->invocations() + ? roots(t)->invocations()->length() + : 0; + + if (static_cast(index) >= oldCapacity) { + unsigned newCapacity = oldCapacity ? 2 * oldCapacity : 4096; + + void** newTable = static_cast( + allocator(t)->allocate(newCapacity * BytesPerWord)); + + GcArray* newData = makeArray(t, newCapacity); + PROTECT(t, newData); + + GcWordArray* newThunks = makeWordArray(t, newCapacity * 2); + PROTECT(t, newThunks); + + if (dynamicTable(t)) { + memcpy(newTable, dynamicTable(t), oldCapacity * BytesPerWord); + + for(size_t i = 0; i < oldCapacity; i++) { + newData->setBodyElement(t, i, + roots(t)->invocations()->body()[i]); + } + + + mark(t, newData, ArrayBody, oldCapacity); + + memcpy(newThunks->body().begin(), + compileRoots(t)->dynamicThunks()->body().begin(), + compileRoots(t)->dynamicThunks()->length() * BytesPerWord); + } + + ENTER(t, Thread::ExclusiveState); + + if (dynamicTable(t)) { + allocator(t)->free(dynamicTable(t), dynamicTableSize(t)); + } + dynamicTable(t) = newTable; + dynamicTableSize(t) = newCapacity * BytesPerWord; + roots(t)->setInvocations(t, newData); + + updateDynamicTable(static_cast(t->m->rootThread), t); + + compileRoots(t)->setDynamicThunks(t, newThunks); + } + + unsigned size; + uintptr_t thunk = compileVirtualThunk( + t, index, &size, defaultDynamicThunk(t), "dynamicThunk"); + compileRoots(t)->dynamicThunks()->body()[index * 2] = thunk; + compileRoots(t)->dynamicThunks()->body()[(index * 2) + 1] = size; + + t->dynamicTable[index] = reinterpret_cast(thunk); + + roots(t)->invocations()->setBodyElement(t, index, invocation); + } + + return index; +} + +unsigned translateLocalIndex(Context* context, + unsigned footprint, + unsigned index) +{ + unsigned parameterFootprint = context->method->parameterFootprint(); + + if (index < parameterFootprint) { + return parameterFootprint - index - footprint; + } else { + return index; + } +} + +ir::Value* loadLocal(Context* context, + unsigned footprint, + ir::Type type, + unsigned index) +{ + ir::Value* result = context->compiler->loadLocal( + type, translateLocalIndex(context, footprint, index)); + + assertT(context->thread, type == result->type); + return result; +} + +void storeLocal(Context* context, + unsigned footprint, + ir::Type type UNUSED, + ir::Value* value, + unsigned index) +{ + assertT(context->thread, type == value->type); + context->compiler->storeLocal(value, + translateLocalIndex(context, footprint, index)); +} + +avian::util::FixedAllocator* codeAllocator(MyThread* t); + +ir::Type operandTypeForFieldCode(Thread* t, unsigned code) +{ + switch (code) { + case ByteField: + case BooleanField: + case CharField: + case ShortField: + case IntField: + return ir::Type::i4(); + case LongField: + return ir::Type::i8(); + + case ObjectField: + return ir::Type::object(); + + case FloatField: + return ir::Type::f4(); + case DoubleField: + return ir::Type::f8(); + + case VoidField: + return ir::Type::void_(); + + default: + abort(t); + } +} + +unsigned methodReferenceParameterFootprint(Thread* t, + GcReference* reference, + bool isStatic) +{ + return parameterFootprint( + t, + reinterpret_cast(reference->spec()->body().begin()), + isStatic); +} + +int methodReferenceReturnCode(Thread* t, GcReference* reference) +{ + unsigned parameterCount; + unsigned parameterFootprint; + unsigned returnCode; + scanMethodSpec( + t, + reinterpret_cast(reference->spec()->body().begin()), + true, + ¶meterCount, + ¶meterFootprint, + &returnCode); + + return returnCode; +} + +class Frame { + public: + Frame(Context* context, ir::Type* stackMap) + : context(context), + t(context->thread), + c(context->compiler), + subroutine(0), + stackMap(stackMap), + ip(0), + sp(localSize()), + level(0) + { + memset(stackMap, 0, context->method->code()->maxStack() * sizeof(ir::Type)); + } + + Frame(Frame* f, ir::Type* stackMap) + : context(f->context), + t(context->thread), + c(context->compiler), + subroutine(f->subroutine), + stackMap(stackMap), + ip(f->ip), + sp(f->sp), + level(f->level + 1) + { + memcpy(stackMap, + f->stackMap, + context->method->code()->maxStack() * sizeof(ir::Type)); + + if (level > 1) { + context->eventLog.append(PushContextEvent); + } + } + + ~Frame() + { + dispose(); + } + + void dispose() + { + if (level > 1) { + context->eventLog.append(PopContextEvent); + } + } + + ir::Value* append(object o) + { + BootContext* bc = context->bootContext; + if (bc) { + avian::codegen::Promise* p = new (bc->zone) + avian::codegen::ListenPromise(t->m->system, bc->zone); + + PROTECT(t, o); + object pointer = makePointer(t, p); + bc->constants = makeTriple(t, o, pointer, bc->constants); + + return c->binaryOp( + lir::Add, + ir::Type::object(), + c->memory( + c->threadRegister(), ir::Type::object(), TARGET_THREAD_HEAPIMAGE), + c->promiseConstant(p, ir::Type::object())); + } else { + for (PoolElement* e = context->objectPool; e; e = e->next) { + if (o == e->target) { + return c->address(ir::Type::object(), e); + } + } + + context->objectPool = new (&context->zone) + PoolElement(t, o, context->objectPool); + + ++context->objectPoolCount; + + return c->address(ir::Type::object(), context->objectPool); + } + } + + unsigned localSize() + { + return local::localSize(t, context->method); + } + + unsigned stackSize() + { + return context->method->code()->maxStack(); + } + + unsigned frameSize() + { + return localSize() + stackSize(); + } + + void set(unsigned index, ir::Type type) + { + assertT(t, index < frameSize()); + + if (type == ir::Type::object()) { + context->eventLog.append(MarkEvent); + context->eventLog.append2(index); + } else { + context->eventLog.append(ClearEvent); + context->eventLog.append2(index); + } + + int si = index - localSize(); + if (si >= 0) { + stackMap[si] = type; + } + } + + ir::Type get(unsigned index) + { + assertT(t, index < frameSize()); + int si = index - localSize(); + assertT(t, si >= 0); + return stackMap[si]; + } + + void popped(unsigned count) + { + assertT(t, sp >= count); + assertT(t, sp - count >= localSize()); + while (count) { + set(--sp, ir::Type::i4()); + --count; + } + } + + avian::codegen::Promise* addressPromise(avian::codegen::Promise* p) + { + BootContext* bc = context->bootContext; + if (bc) { + bc->addresses = new (bc->zone) avian::codegen::DelayedPromise( + t->m->system, bc->zone, p, bc->addresses); + return bc->addresses; + } else { + return p; + } + } + + ir::Value* addressOperand(avian::codegen::Promise* p) + { + return c->promiseConstant(p, ir::Type::iptr()); + } + + ir::Value* absoluteAddressOperand(avian::codegen::Promise* p) + { + return context->bootContext + ? c->binaryOp( + lir::Add, + ir::Type::iptr(), + c->memory(c->threadRegister(), + ir::Type::iptr(), + TARGET_THREAD_CODEIMAGE), + c->promiseConstant( + new (&context->zone) avian::codegen::OffsetPromise( + p, + -reinterpret_cast( + codeAllocator(t)->memory.begin())), + ir::Type::iptr())) + : addressOperand(p); + } + + ir::Value* machineIpValue(unsigned logicalIp) + { + return c->promiseConstant(machineIp(logicalIp), ir::Type::iptr()); + } + + unsigned duplicatedIp(unsigned bytecodeIp) + { + if (UNLIKELY(subroutine)) { + return bytecodeIp + subroutine->duplicatedBaseIp; + } else { + return bytecodeIp; + } + } + + Promise* machineIp(unsigned bytecodeIp) + { + return c->machineIp(duplicatedIp(bytecodeIp)); + } + + void visitLogicalIp(unsigned bytecodeIp) + { + unsigned dupIp = duplicatedIp(bytecodeIp); + c->visitLogicalIp(dupIp); + + context->eventLog.append(IpEvent); + context->eventLog.append2(bytecodeIp); + } + + void startLogicalIp(unsigned bytecodeIp) + { + unsigned dupIp = duplicatedIp(bytecodeIp); + c->startLogicalIp(dupIp); + + context->eventLog.append(IpEvent); + context->eventLog.append2(bytecodeIp); + + this->ip = bytecodeIp; + } + + void push(ir::Type type, ir::Value* o) + { + assertT(t, type == o->type); + c->push(o->type, o); + assertT(t, sp + 1 <= frameSize()); + set(sp++, type); + } + + void pushObject() + { + c->pushed(ir::Type::object()); + + assertT(t, sp + 1 <= frameSize()); + set(sp++, ir::Type::object()); + } + + void pushLarge(ir::Type type, ir::Value* o) + { + assertT(t, o->type == type); + c->push(type, o); + assertT(t, sp + 2 <= frameSize()); + set(sp++, type); + set(sp++, type); + } + + void popFootprint(unsigned count) + { + popped(count); + c->popped(count); + } + + ir::Value* pop(ir::Type type) + { + assertT(t, sp >= 1); + assertT(t, sp - 1 >= localSize()); + assertT(t, get(sp - 1) == type); + set(--sp, ir::Type::i4()); + return c->pop(type); + } + + ir::Value* popLarge(ir::Type type) + { + assertT(t, sp >= 1); + assertT(t, sp - 2 >= localSize()); + assertT(t, get(sp - 1) == type); + assertT(t, get(sp - 2) == type); + sp -= 2; + return c->pop(type); + } + + void load(ir::Type type, unsigned index) + { + assertT(t, index < localSize()); + push(type, loadLocal(context, 1, type, index)); + } + + void loadLarge(ir::Type type, unsigned index) + { + assertT(t, index < static_cast(localSize() - 1)); + pushLarge(type, loadLocal(context, 2, type, index)); + } + + void store(ir::Type type, unsigned index) + { + assertT(t, + type == ir::Type::i4() || type == ir::Type::f4() + || type == ir::Type::object()); + storeLocal(context, 1, type, pop(type), index); + unsigned ti = translateLocalIndex(context, 1, index); + assertT(t, ti < localSize()); + set(ti, type); + } + + void storeLarge(ir::Type type, unsigned index) + { + assertT(t, type.rawSize() == 8); + storeLocal(context, 2, type, popLarge(type), index); + unsigned ti = translateLocalIndex(context, 2, index); + assertT(t, ti + 1 < localSize()); + set(ti, type); + set(ti + 1, type); + } + + void dup() + { + c->push(ir::Type::i4(), c->peek(1, 0)); + + assertT(t, sp + 1 <= frameSize()); + assertT(t, sp - 1 >= localSize()); + set(sp, get(sp - 1)); + ++sp; + } + + void dupX1() + { + ir::Value* s0 = c->pop(ir::Type::i4()); + ir::Value* s1 = c->pop(ir::Type::i4()); + + c->push(ir::Type::i4(), s0); + c->push(ir::Type::i4(), s1); + c->push(ir::Type::i4(), s0); + + assertT(t, sp + 1 <= frameSize()); + assertT(t, sp - 2 >= localSize()); + + ir::Type b2 = get(sp - 2); + ir::Type b1 = get(sp - 1); + + set(sp - 1, b2); + set(sp - 2, b1); + set(sp, b1); + + ++sp; + } + + void dupX2() + { + ir::Value* s0 = c->pop(ir::Type::i4()); + + if (get(sp - 2).rawSize() == 8) { + ir::Value* s1 = c->pop(ir::Type::i8()); + + c->push(ir::Type::i4(), s0); + c->push(ir::Type::i8(), s1); + c->push(ir::Type::i4(), s0); + } else { + ir::Value* s1 = c->pop(ir::Type::i4()); + ir::Value* s2 = c->pop(ir::Type::i4()); + + c->push(ir::Type::i4(), s0); + c->push(ir::Type::i4(), s2); + c->push(ir::Type::i4(), s1); + c->push(ir::Type::i4(), s0); + } + + assertT(t, sp + 1 <= frameSize()); + assertT(t, sp - 3 >= localSize()); + + ir::Type b3 = get(sp - 3); + ir::Type b2 = get(sp - 2); + ir::Type b1 = get(sp - 1); + + set(sp - 2, b3); + set(sp - 1, b2); + set(sp - 3, b1); + set(sp, b1); + + ++sp; + } + + void dup2() + { + if (get(sp - 1).rawSize() == 8) { + c->push(ir::Type::i8(), c->peek(2, 0)); + } else { + ir::Value* s0 = c->pop(ir::Type::i4()); + ir::Value* s1 = c->pop(ir::Type::i4()); + + c->push(ir::Type::i4(), s1); + c->push(ir::Type::i4(), s0); + c->push(ir::Type::i4(), s1); + c->push(ir::Type::i4(), s0); + } + + assertT(t, sp + 2 <= frameSize()); + assertT(t, sp - 2 >= localSize()); + + ir::Type b2 = get(sp - 2); + ir::Type b1 = get(sp - 1); + + set(sp, b2); + set(sp + 1, b1); + + sp += 2; + } + + void dup2X1() + { + if (get(sp - 1).rawSize() == 8) { + ir::Value* s0 = c->pop(ir::Type::i8()); + ir::Value* s1 = c->pop(ir::Type::i4()); + + c->push(ir::Type::i8(), s0); + c->push(ir::Type::i4(), s1); + c->push(ir::Type::i8(), s0); + } else { + ir::Value* s0 = c->pop(ir::Type::i4()); + ir::Value* s1 = c->pop(ir::Type::i4()); + ir::Value* s2 = c->pop(ir::Type::i4()); + + c->push(ir::Type::i4(), s1); + c->push(ir::Type::i4(), s0); + c->push(ir::Type::i4(), s2); + c->push(ir::Type::i4(), s1); + c->push(ir::Type::i4(), s0); + } + + assertT(t, sp + 2 <= frameSize()); + assertT(t, sp - 3 >= localSize()); + + ir::Type b3 = get(sp - 3); + ir::Type b2 = get(sp - 2); + ir::Type b1 = get(sp - 1); + + set(sp - 1, b3); + set(sp - 3, b2); + set(sp, b2); + set(sp - 2, b1); + set(sp + 1, b1); + + sp += 2; + } + + void dup2X2() + { + if (get(sp - 1).rawSize() == 8) { + ir::Value* s0 = c->pop(ir::Type::i8()); + + if (get(sp - 3).rawSize() == 8) { + ir::Value* s1 = c->pop(ir::Type::i8()); + + c->push(ir::Type::i8(), s0); + c->push(ir::Type::i8(), s1); + c->push(ir::Type::i8(), s0); + } else { + ir::Value* s1 = c->pop(ir::Type::i4()); + ir::Value* s2 = c->pop(ir::Type::i4()); + + c->push(ir::Type::i8(), s0); + c->push(ir::Type::i4(), s2); + c->push(ir::Type::i4(), s1); + c->push(ir::Type::i8(), s0); + } + } else { + ir::Value* s0 = c->pop(ir::Type::i4()); + ir::Value* s1 = c->pop(ir::Type::i4()); + ir::Value* s2 = c->pop(ir::Type::i4()); + ir::Value* s3 = c->pop(ir::Type::i4()); + + c->push(ir::Type::i4(), s1); + c->push(ir::Type::i4(), s0); + c->push(ir::Type::i4(), s3); + c->push(ir::Type::i4(), s2); + c->push(ir::Type::i4(), s1); + c->push(ir::Type::i4(), s0); + } + + assertT(t, sp + 2 <= frameSize()); + assertT(t, sp - 4 >= localSize()); + + ir::Type b4 = get(sp - 4); + ir::Type b3 = get(sp - 3); + ir::Type b2 = get(sp - 2); + ir::Type b1 = get(sp - 1); + + set(sp - 2, b4); + set(sp - 1, b3); + set(sp - 4, b2); + set(sp, b2); + set(sp - 3, b1); + set(sp + 1, b1); + + sp += 2; + } + + void swap() + { + ir::Value* s0 = c->pop(ir::Type::i4()); + ir::Value* s1 = c->pop(ir::Type::i4()); + + c->push(ir::Type::i4(), s0); + c->push(ir::Type::i4(), s1); + + assertT(t, sp - 2 >= localSize()); + + ir::Type saved = get(sp - 1); + + set(sp - 1, get(sp - 2)); + set(sp - 2, saved); + } + + TraceElement* trace(GcMethod* target, unsigned flags) + { + unsigned mapSize = frameMapSizeInWords(t, context->method); + + TraceElement* e = context->traceLog = new ( + context->zone.allocate(sizeof(TraceElement) + (mapSize * BytesPerWord))) + TraceElement(context, + duplicatedIp(ip), + target, + flags, + context->traceLog, + mapSize); + + ++context->traceLogCount; + + context->eventLog.append(TraceEvent); + context->eventLog.appendAddress(e); + + return e; + } + + void pushReturnValue(unsigned code, ir::Value* result) + { + switch (code) { + case ByteField: + case BooleanField: + case CharField: + case ShortField: + case IntField: + return push(ir::Type::i4(), result); + case FloatField: + return push(ir::Type::f4(), result); + + case ObjectField: + return push(ir::Type::object(), result); + + case LongField: + return pushLarge(ir::Type::i8(), result); + case DoubleField: + return pushLarge(ir::Type::f8(), result); + + default: + abort(t); + } + } + + Slice peekMethodArguments(unsigned footprint) + { + ir::Value** ptr = context->argumentBuffer.items; + + for (unsigned i = 0; i < footprint; i++) { + *(ptr++) = c->peek(1, footprint - i - 1); + } + + return Slice(context->argumentBuffer.items, footprint); + } + + void stackCall(ir::Value* methodValue, + GcMethod* methodObject, + unsigned flags, + TraceElement* trace) + { + unsigned footprint = methodObject->parameterFootprint(); + unsigned returnCode = methodObject->returnCode(); + ir::Value* result = c->stackCall(methodValue, + flags, + trace, + operandTypeForFieldCode(t, returnCode), + peekMethodArguments(footprint)); + + popFootprint(footprint); + + if (returnCode != VoidField) { + pushReturnValue(returnCode, result); + } + } + + void referenceStackCall(bool isStatic, + ir::Value* methodValue, + GcReference* methodReference, + unsigned flags, + TraceElement* trace) + { + unsigned footprint + = methodReferenceParameterFootprint(t, methodReference, isStatic); + unsigned returnCode = methodReferenceReturnCode(t, methodReference); + ir::Value* result = c->stackCall(methodValue, + flags, + trace, + operandTypeForFieldCode(t, returnCode), + peekMethodArguments(footprint)); + + popFootprint(footprint); + + if (returnCode != VoidField) { + pushReturnValue(returnCode, result); + } + } + + void startSubroutine(unsigned ip, unsigned returnAddress) + { + // Push a dummy value to the stack, representing the return address (which + // we don't need, since we're expanding everything statically). + // TODO: in the future, push a value that we can track through type checking + push(ir::Type::object(), c->constant(0, ir::Type::object())); + + if (DebugInstructions) { + fprintf(stderr, "startSubroutine %u %u\n", ip, returnAddress); + } + + Subroutine* subroutine = new (&context->zone) + Subroutine(context->subroutineCount++, + returnAddress, + context->method->code()->length(), + this->subroutine); + + context->extendLogicalCode(context->method->code()->length()); + + this->subroutine = subroutine; + } + + unsigned endSubroutine(unsigned returnAddressLocal UNUSED) + { + // TODO: use returnAddressLocal to decide which subroutine we're returning + // from (in case it's ever not the most recent one entered). I'm unsure of + // whether such a subroutine pattern would pass bytecode verification. + + unsigned returnAddress = subroutine->returnAddress; + + if (DebugInstructions) { + fprintf(stderr, "endSubroutine %u %u\n", ip, returnAddress); + } + + subroutine = subroutine->outer; + + return returnAddress; + } + + Context* context; + MyThread* t; + avian::codegen::Compiler* c; + + // Innermost subroutine we're compiling code for + Subroutine* subroutine; + + ir::Type* stackMap; + unsigned ip; + unsigned sp; + unsigned level; +}; + +unsigned savedTargetIndex(MyThread* t UNUSED, GcMethod* method) +{ + return method->code()->maxLocals(); +} + +GcCallNode* findCallNode(MyThread* t, void* address); + +void* findExceptionHandler(Thread* t, GcMethod* method, void* ip) +{ + if (t->exception) { + GcArray* table = cast(t, method->code()->exceptionHandlerTable()); + if (table) { + GcIntArray* index = cast(t, table->body()[0]); + + uint8_t* compiled = reinterpret_cast(methodCompiled(t, method)); + + for (unsigned i = 0; i < table->length() - 1; ++i) { + unsigned start = index->body()[i * 3]; + unsigned end = index->body()[(i * 3) + 1]; + unsigned key = difference(ip, compiled) - 1; + + if (key >= start and key < end) { + GcClass* catchType = cast(t, table->body()[i + 1]); + + if (exceptionMatch(t, catchType, t->exception)) { + return compiled + index->body()[(i * 3) + 2]; + } + } + } + } + } + + return 0; +} + +void releaseLock(MyThread* t, GcMethod* method, void* stack) +{ + if (method->flags() & ACC_SYNCHRONIZED) { + if (t->methodLockIsClean) { + object lock; + if (method->flags() & ACC_STATIC) { + lock = getJClass(t, method->class_()); + } else { + lock = *localObject(t, + stackForFrame(t, stack, method), + method, + savedTargetIndex(t, method)); + } + + release(t, lock); + } else { + // got an exception while trying to acquire the lock for a + // synchronized method -- don't try to release it, since we + // never succeeded in acquiring it. + t->methodLockIsClean = true; + } + } +} + +void findUnwindTarget(MyThread* t, + void** targetIp, + void** targetFrame, + void** targetStack, + GcContinuation** targetContinuation) +{ + void* ip; + void* stack; + GcContinuation* continuation; + + if (t->traceContext) { + ip = t->traceContext->ip; + stack = t->traceContext->stack; + continuation = t->traceContext->continuation; + } else { + ip = getIp(t); + stack = t->stack; + continuation = t->continuation; + } + + GcMethod* target = t->trace->targetMethod; + bool mostRecent = true; + + *targetIp = 0; + while (*targetIp == 0) { + GcMethod* method = methodForIp(t, ip); + if (method) { + void* handler = findExceptionHandler(t, method, ip); + + if (handler) { + *targetIp = handler; + + nextFrame(t, &ip, &stack, method, target, mostRecent); + + void** sp = static_cast(stackForFrame(t, stack, method)) + + t->arch->frameReturnAddressSize(); + + *targetFrame = static_cast(stack) + + t->arch->framePointerOffset(); + *targetStack = sp; + *targetContinuation = continuation; + + sp[localOffset(t, localSize(t, method), method)] = t->exception; + + t->exception = 0; + } else { + nextFrame(t, &ip, &stack, method, target, mostRecent); + + if (t->exception) { + releaseLock(t, method, stack); + } + + target = method; + } + } else { + expect(t, ip); + *targetIp = ip; + *targetFrame = 0; + *targetStack = static_cast(stack) + + t->arch->frameReturnAddressSize(); + *targetContinuation = continuation; + + while (Continuations and *targetContinuation) { + GcContinuation* c = *targetContinuation; + + GcMethod* method = c->method(); + + void* handler = findExceptionHandler(t, method, c->address()); + + if (handler) { + t->exceptionHandler = handler; + + t->exceptionStackAdjustment + = (stackOffsetFromFrame(t, method) + - ((c->framePointerOffset() / BytesPerWord) + - t->arch->framePointerOffset() + + t->arch->frameReturnAddressSize())) * BytesPerWord; + + t->exceptionOffset = localOffset(t, localSize(t, method), method) + * BytesPerWord; + + break; + } else if (t->exception) { + releaseLock(t, + method, + reinterpret_cast(c) + ContinuationBody + + c->returnAddressOffset() + - t->arch->returnAddressOffset()); + } + + *targetContinuation = c->next(); + } + } + + mostRecent = false; + } +} + +GcContinuation* makeCurrentContinuation(MyThread* t, + void** targetIp, + void** targetStack) +{ + void* ip = getIp(t); + void* stack = t->stack; + + GcContinuationContext* context + = t->continuation + ? t->continuation->context() + : makeContinuationContext(t, 0, 0, 0, 0, t->trace->originalMethod); + PROTECT(t, context); + + GcMethod* target = t->trace->targetMethod; + PROTECT(t, target); + + GcContinuation* first = 0; + PROTECT(t, first); + + GcContinuation* last = 0; + PROTECT(t, last); + + bool mostRecent = true; + + *targetIp = 0; + while (*targetIp == 0) { + assertT(t, ip); + + GcMethod* method = methodForIp(t, ip); + if (method) { + PROTECT(t, method); + + void** top = static_cast(stack) + + t->arch->frameReturnAddressSize() + + t->arch->frameFooterSize(); + unsigned argumentFootprint + = t->arch->argumentFootprint(target->parameterFootprint()); + unsigned alignment = t->arch->stackAlignmentInWords(); + if (avian::codegen::TailCalls and argumentFootprint > alignment) { + top += argumentFootprint - alignment; + } + + void* nextIp = ip; + nextFrame(t, &nextIp, &stack, method, target, mostRecent); + + void** bottom = static_cast(stack) + + t->arch->frameReturnAddressSize(); + unsigned frameSize = bottom - top; + unsigned totalSize + = frameSize + t->arch->frameFooterSize() + + t->arch->argumentFootprint(method->parameterFootprint()); + + GcContinuation* c = makeContinuation( + t, + 0, + context, + method, + ip, + (frameSize + t->arch->frameFooterSize() + + t->arch->returnAddressOffset() - t->arch->frameReturnAddressSize()) + * BytesPerWord, + (frameSize + t->arch->frameFooterSize() + + t->arch->framePointerOffset() - t->arch->frameReturnAddressSize()) + * BytesPerWord, + totalSize); + + memcpy(c->body().begin(), top, totalSize * BytesPerWord); + + if (last) { + last->setNext(t, c); + } else { + first = c; + } + last = c; + + ip = nextIp; + + target = method; + } else { + *targetIp = ip; + *targetStack = static_cast(stack) + + t->arch->frameReturnAddressSize(); + } + + mostRecent = false; + } + + expect(t, last); + last->setNext(t, t->continuation); + + return first; +} + +void NO_RETURN unwind(MyThread* t) +{ + void* ip; + void* frame; + void* stack; + GcContinuation* continuation; + findUnwindTarget(t, &ip, &frame, &stack, &continuation); + + t->trace->targetMethod = 0; + t->trace->nativeMethod = 0; + + transition(t, ip, stack, continuation, t->trace); + + vmJump(ip, frame, stack, t, 0, 0); +} + +class MyCheckpoint : public Thread::Checkpoint { + public: + MyCheckpoint(MyThread* t) : Checkpoint(t) + { + } + + virtual void unwind() + { + local::unwind(static_cast(t)); + } +}; + +uintptr_t defaultThunk(MyThread* t); + +uintptr_t nativeThunk(MyThread* t); + +uintptr_t bootNativeThunk(MyThread* t); + +uintptr_t virtualThunk(MyThread* t, unsigned index); + +bool unresolved(MyThread* t, uintptr_t methodAddress); + +uintptr_t methodAddress(Thread* t, GcMethod* method) +{ + if (method->flags() & ACC_NATIVE) { + return bootNativeThunk(static_cast(t)); + } else { + return methodCompiled(t, method); + } +} + +void tryInitClass(MyThread* t, GcClass* class_) +{ + initClass(t, class_); +} + +void compile(MyThread* t, + FixedAllocator* allocator, + BootContext* bootContext, + GcMethod* method); + +GcMethod* resolveMethod(Thread* t, GcPair* pair) +{ + GcReference* reference = cast(t, pair->second()); + PROTECT(t, reference); + + GcClass* class_ = resolveClassInObject( + t, + cast(t, pair->first())->class_()->loader(), + reference, + ReferenceClass); + + return cast(t, + findInHierarchy(t, + class_, + reference->name(), + reference->spec(), + findMethodInClass, + GcNoSuchMethodError::Type)); +} + +bool methodAbstract(Thread* t UNUSED, GcMethod* method) +{ + return method->code() == 0 and (method->flags() & ACC_NATIVE) == 0; +} + +int64_t prepareMethodForCall(MyThread* t, GcMethod* target) +{ + if (methodAbstract(t, target)) { + throwNew(t, + GcAbstractMethodError::Type, + "%s.%s%s", + target->class_()->name()->body().begin(), + target->name()->body().begin(), + target->spec()->body().begin()); + } else { + if (unresolved(t, methodAddress(t, target))) { + PROTECT(t, target); + + compile(t, codeAllocator(t), 0, target); + } + + if (target->flags() & ACC_NATIVE) { + t->trace->nativeMethod = target; + } + + return methodAddress(t, target); + } +} + +int64_t findInterfaceMethodFromInstance(MyThread* t, + GcMethod* method, + object instance) +{ + if (instance) { + return prepareMethodForCall( + t, findInterfaceMethod(t, method, objectClass(t, instance))); + } else { + throwNew(t, GcNullPointerException::Type); + } +} + +int64_t findInterfaceMethodFromInstanceAndReference(MyThread* t, + GcPair* pair, + object instance) +{ + PROTECT(t, instance); + + GcMethod* method = resolveMethod(t, pair); + + return findInterfaceMethodFromInstance(t, method, instance); +} + +void checkMethod(Thread* t, GcMethod* method, bool shouldBeStatic) +{ + if (((method->flags() & ACC_STATIC) == 0) == shouldBeStatic) { + throwNew(t, + GcIncompatibleClassChangeError::Type, + "expected %s.%s%s to be %s", + method->class_()->name()->body().begin(), + method->name()->body().begin(), + method->spec()->body().begin(), + shouldBeStatic ? "static" : "non-static"); + } +} + +int64_t findSpecialMethodFromReference(MyThread* t, GcPair* pair) +{ + PROTECT(t, pair); + + GcMethod* target = resolveMethod(t, pair); + + GcClass* class_ = cast(t, pair->first())->class_(); + if (isSpecialMethod(t, target, class_)) { + target = findVirtualMethod(t, target, class_->super()); + } + + checkMethod(t, target, false); + + return prepareMethodForCall(t, target); +} + +int64_t findStaticMethodFromReference(MyThread* t, GcPair* pair) +{ + GcMethod* target = resolveMethod(t, pair); + + checkMethod(t, target, true); + + return prepareMethodForCall(t, target); +} + +int64_t findVirtualMethodFromReference(MyThread* t, + GcPair* pair, + object instance) +{ + PROTECT(t, instance); + + GcMethod* target = resolveMethod(t, pair); + + target = findVirtualMethod(t, target, objectClass(t, instance)); + + checkMethod(t, target, false); + + return prepareMethodForCall(t, target); +} + +int64_t getMethodAddress(MyThread* t, GcMethod* target) +{ + return prepareMethodForCall(t, target); +} + +int64_t getJClassFromReference(MyThread* t, GcPair* pair) +{ + return reinterpret_cast(getJClass( + t, + resolveClass(t, + cast(t, pair->first())->class_()->loader(), + cast(t, pair->second())->name()))); +} + +unsigned traceSize(Thread* t) +{ + class Counter : public Processor::StackVisitor { + public: + Counter() : count(0) + { + } + + virtual bool visit(Processor::StackWalker*) + { + ++count; + return true; + } + + unsigned count; + } counter; + + t->m->processor->walkStack(t, &counter); + + return pad(GcArray::FixedSize) + + (counter.count * pad(ArrayElementSizeOfArray)) + + (counter.count * pad(GcTraceElement::FixedSize)); +} + +void NO_RETURN throwArithmetic(MyThread* t) +{ + if (ensure(t, GcArithmeticException::FixedSize + traceSize(t))) { + t->setFlag(Thread::TracingFlag); + THREAD_RESOURCE0(t, t->clearFlag(Thread::TracingFlag)); + + throwNew(t, GcArithmeticException::Type); + } else { + // not enough memory available for a new exception and stack trace + // -- use a preallocated instance instead + throw_(t, roots(t)->arithmeticException()); + } +} + +int64_t divideLong(MyThread* t, int64_t b, int64_t a) +{ + if (LIKELY(b)) { + return a / b; + } else { + throwArithmetic(t); + } +} + +int64_t divideInt(MyThread* t, int32_t b, int32_t a) +{ + if (LIKELY(b)) { + return a / b; + } else { + throwArithmetic(t); + } +} + +int64_t moduloLong(MyThread* t, int64_t b, int64_t a) +{ + if (LIKELY(b)) { + return a % b; + } else { + throwArithmetic(t); + } +} + +int64_t moduloInt(MyThread* t, int32_t b, int32_t a) +{ + if (LIKELY(b)) { + return a % b; + } else { + throwArithmetic(t); + } +} + +uint64_t makeBlankObjectArray(MyThread* t, GcClass* class_, int32_t length) +{ + if (length >= 0) { + return reinterpret_cast(makeObjectArray(t, class_, length)); + } else { + throwNew(t, GcNegativeArraySizeException::Type, "%d", length); + } +} + +uint64_t makeBlankObjectArrayFromReference(MyThread* t, + GcPair* pair, + int32_t length) +{ + return makeBlankObjectArray( + t, + resolveClass(t, + cast(t, pair->first())->class_()->loader(), + cast(t, pair->second())->name()), + length); +} + +uint64_t makeBlankArray(MyThread* t, unsigned type, int32_t length) +{ + if (length >= 0) { + switch (type) { + case T_BOOLEAN: + return reinterpret_cast(makeBooleanArray(t, length)); + case T_CHAR: + return reinterpret_cast(makeCharArray(t, length)); + case T_FLOAT: + return reinterpret_cast(makeFloatArray(t, length)); + case T_DOUBLE: + return reinterpret_cast(makeDoubleArray(t, length)); + case T_BYTE: + return reinterpret_cast(makeByteArray(t, length)); + case T_SHORT: + return reinterpret_cast(makeShortArray(t, length)); + case T_INT: + return reinterpret_cast(makeIntArray(t, length)); + case T_LONG: + return reinterpret_cast(makeLongArray(t, length)); + default: + abort(t); + } + } else { + throwNew(t, GcNegativeArraySizeException::Type, "%d", length); + } +} + +uint64_t lookUpAddress(int32_t key, + uintptr_t* start, + int32_t count, + uintptr_t default_) +{ + int32_t bottom = 0; + int32_t top = count; + for (int32_t span = top - bottom; span; span = top - bottom) { + int32_t middle = bottom + (span / 2); + uintptr_t* p = start + (middle * 2); + int32_t k = *p; + + if (key < k) { + top = middle; + } else if (key > k) { + bottom = middle + 1; + } else { + return p[1]; + } + } + + return default_; +} + +void setMaybeNull(MyThread* t, object o, unsigned offset, object value) +{ + if (LIKELY(o)) { + setField(t, o, offset, value); + } else { + throwNew(t, GcNullPointerException::Type); + } +} + +void acquireMonitorForObject(MyThread* t, object o) +{ + if (LIKELY(o)) { + acquire(t, o); + } else { + throwNew(t, GcNullPointerException::Type); + } +} + +void acquireMonitorForObjectOnEntrance(MyThread* t, object o) +{ + if (LIKELY(o)) { + t->methodLockIsClean = false; + acquire(t, o); + t->methodLockIsClean = true; + } else { + throwNew(t, GcNullPointerException::Type); + } +} + +void releaseMonitorForObject(MyThread* t, object o) +{ + if (LIKELY(o)) { + release(t, o); + } else { + throwNew(t, GcNullPointerException::Type); + } +} + +void acquireMonitorForClassOnEntrance(MyThread* t, GcClass* o) +{ + if (LIKELY(o)) { + t->methodLockIsClean = false; + acquire(t, getJClass(t, o)); + t->methodLockIsClean = true; + } else { + throwNew(t, GcNullPointerException::Type); + } +} + +void releaseMonitorForClass(MyThread* t, GcClass* o) +{ + if (LIKELY(o)) { + release(t, getJClass(t, o)); + } else { + throwNew(t, GcNullPointerException::Type); + } +} + +object makeMultidimensionalArray2(MyThread* t, + GcClass* class_, + uintptr_t* countStack, + int32_t dimensions) +{ + PROTECT(t, class_); + + THREAD_RUNTIME_ARRAY(t, int32_t, counts, dimensions); + for (int i = dimensions - 1; i >= 0; --i) { + RUNTIME_ARRAY_BODY(counts)[i] = countStack[dimensions - i - 1]; + if (UNLIKELY(RUNTIME_ARRAY_BODY(counts)[i] < 0)) { + throwNew(t, + GcNegativeArraySizeException::Type, + "%d", + RUNTIME_ARRAY_BODY(counts)[i]); + return 0; + } + } + + object array = makeArray(t, RUNTIME_ARRAY_BODY(counts)[0]); + setObjectClass(t, array, class_); + PROTECT(t, array); + + populateMultiArray(t, array, RUNTIME_ARRAY_BODY(counts), 0, dimensions); + + return array; +} + +uint64_t makeMultidimensionalArray(MyThread* t, + GcClass* class_, + int32_t dimensions, + int32_t offset) +{ + return reinterpret_cast(makeMultidimensionalArray2( + t, class_, static_cast(t->stack) + offset, dimensions)); +} + +uint64_t makeMultidimensionalArrayFromReference(MyThread* t, + GcPair* pair, + int32_t dimensions, + int32_t offset) +{ + return makeMultidimensionalArray( + t, + resolveClass(t, + cast(t, pair->first())->class_()->loader(), + cast(t, pair->second())->name()), + dimensions, + offset); +} + +void NO_RETURN throwArrayIndexOutOfBounds(MyThread* t) +{ + if (ensure(t, GcArrayIndexOutOfBoundsException::FixedSize + traceSize(t))) { + t->setFlag(Thread::TracingFlag); + THREAD_RESOURCE0(t, t->clearFlag(Thread::TracingFlag)); + + throwNew(t, GcArrayIndexOutOfBoundsException::Type); + } else { + // not enough memory available for a new exception and stack trace + // -- use a preallocated instance instead + throw_(t, roots(t)->arrayIndexOutOfBoundsException()); + } +} + +void NO_RETURN throwStackOverflow(MyThread* t) +{ + throwNew(t, GcStackOverflowError::Type); +} + +void NO_RETURN throw_(MyThread* t, GcThrowable* o) +{ + if (LIKELY(o)) { + vm::throw_(t, o); + } else { + throwNew(t, GcNullPointerException::Type); + } +} + +void checkCast(MyThread* t, GcClass* class_, object o) +{ + if (UNLIKELY(o and not isAssignableFrom(t, class_, objectClass(t, o)))) { + GcByteArray* classNameFrom = objectClass(t, o)->name(); + GcByteArray* classNameTo = class_->name(); + THREAD_RUNTIME_ARRAY(t, char, classFrom, classNameFrom->length()); + THREAD_RUNTIME_ARRAY(t, char, classTo, classNameTo->length()); + replace('/', + '.', + RUNTIME_ARRAY_BODY(classFrom), + reinterpret_cast(classNameFrom->body().begin())); + replace('/', + '.', + RUNTIME_ARRAY_BODY(classTo), + reinterpret_cast(classNameTo->body().begin())); + throwNew(t, + GcClassCastException::Type, + "%s cannot be cast to %s", + RUNTIME_ARRAY_BODY(classFrom), + RUNTIME_ARRAY_BODY(classTo)); + } +} + +void checkCastFromReference(MyThread* t, GcPair* pair, object o) +{ + PROTECT(t, o); + + GcClass* c + = resolveClass(t, + cast(t, pair->first())->class_()->loader(), + cast(t, pair->second())->name()); + + checkCast(t, c, o); +} + +GcField* resolveField(Thread* t, GcPair* pair) +{ + GcReference* reference = cast(t, pair->second()); + PROTECT(t, reference); + + GcClass* class_ = resolveClassInObject( + t, + cast(t, pair->first())->class_()->loader(), + reference, + ReferenceClass); + + return cast(t, + findInHierarchy(t, + class_, + reference->name(), + reference->spec(), + findFieldInClass, + GcNoSuchFieldError::Type)); +} + +uint64_t getFieldValue(Thread* t, object target, GcField* field) +{ + switch (field->code()) { + case ByteField: + case BooleanField: + return fieldAtOffset(target, field->offset()); + + case CharField: + case ShortField: + return fieldAtOffset(target, field->offset()); + + case FloatField: + case IntField: + return fieldAtOffset(target, field->offset()); + + case DoubleField: + case LongField: + return fieldAtOffset(target, field->offset()); + + case ObjectField: + return fieldAtOffset(target, field->offset()); + + default: + abort(t); + } +} + +uint64_t getStaticFieldValueFromReference(MyThread* t, GcPair* pair) +{ + GcField* field = resolveField(t, pair); + PROTECT(t, field); + + initClass(t, field->class_()); + + ACQUIRE_FIELD_FOR_READ(t, field); + + return getFieldValue(t, field->class_()->staticTable(), field); +} + +uint64_t getFieldValueFromReference(MyThread* t, GcPair* pair, object instance) +{ + PROTECT(t, instance); + + GcField* field = resolveField(t, pair); + PROTECT(t, field); + + ACQUIRE_FIELD_FOR_READ(t, field); + + return getFieldValue(t, instance, field); +} + +void setStaticLongFieldValueFromReference(MyThread* t, + GcPair* pair, + uint64_t value) +{ + GcField* field = resolveField(t, pair); + PROTECT(t, field); + + initClass(t, field->class_()); + + ACQUIRE_FIELD_FOR_WRITE(t, field); + + fieldAtOffset(field->class_()->staticTable(), field->offset()) + = value; +} + +void setLongFieldValueFromReference(MyThread* t, + GcPair* pair, + object instance, + uint64_t value) +{ + PROTECT(t, instance); + + GcField* field = resolveField(t, pair); + PROTECT(t, field); + + ACQUIRE_FIELD_FOR_WRITE(t, field); + + fieldAtOffset(instance, field->offset()) = value; +} + +void setStaticObjectFieldValueFromReference(MyThread* t, + GcPair* pair, + object value) +{ + PROTECT(t, value); + + GcField* field = resolveField(t, pair); + PROTECT(t, field); + + initClass(t, field->class_()); + + ACQUIRE_FIELD_FOR_WRITE(t, field); + + setField(t, field->class_()->staticTable(), field->offset(), value); +} + +void setObjectFieldValueFromReference(MyThread* t, + GcPair* pair, + object instance, + object value) +{ + PROTECT(t, instance); + PROTECT(t, value); + + GcField* field = resolveField(t, pair); + PROTECT(t, field); + + ACQUIRE_FIELD_FOR_WRITE(t, field); + + setField(t, instance, field->offset(), value); +} + +void setFieldValue(MyThread* t, object target, GcField* field, uint32_t value) +{ + switch (field->code()) { + case ByteField: + case BooleanField: + fieldAtOffset(target, field->offset()) = value; + break; + + case CharField: + case ShortField: + fieldAtOffset(target, field->offset()) = value; + break; + + case FloatField: + case IntField: + fieldAtOffset(target, field->offset()) = value; + break; + + default: + abort(t); + } +} + +void setStaticFieldValueFromReference(MyThread* t, GcPair* pair, uint32_t value) +{ + GcField* field = resolveField(t, pair); + PROTECT(t, field); + + initClass(t, field->class_()); + + ACQUIRE_FIELD_FOR_WRITE(t, field); + + setFieldValue(t, field->class_()->staticTable(), field, value); +} + +void setFieldValueFromReference(MyThread* t, + GcPair* pair, + object instance, + uint32_t value) +{ + PROTECT(t, instance); + GcField* field = resolveField(t, pair); + PROTECT(t, field); + + ACQUIRE_FIELD_FOR_WRITE(t, field); + + setFieldValue(t, instance, field, value); +} + +uint64_t instanceOf64(Thread* t, GcClass* class_, object o) +{ + return instanceOf(t, class_, o); +} + +uint64_t instanceOfFromReference(Thread* t, GcPair* pair, object o) +{ + PROTECT(t, o); + + GcClass* c + = resolveClass(t, + cast(t, pair->first())->class_()->loader(), + cast(t, pair->second())->name()); + + return instanceOf64(t, c, o); +} + +uint64_t makeNewGeneral64(Thread* t, GcClass* class_) +{ + PROTECT(t, class_); + + initClass(t, class_); + + return reinterpret_cast(makeNewGeneral(t, class_)); +} + +uint64_t makeNew64(Thread* t, GcClass* class_) +{ + PROTECT(t, class_); + + initClass(t, class_); + + return reinterpret_cast(makeNew(t, class_)); +} + +uint64_t makeNewFromReference(Thread* t, GcPair* pair) +{ + GcClass* class_ + = resolveClass(t, + cast(t, pair->first())->class_()->loader(), + cast(t, pair->second())->name()); + + PROTECT(t, class_); + + initClass(t, class_); + + return makeNewGeneral64(t, class_); +} + +uint64_t getJClass64(Thread* t, GcClass* class_) +{ + return reinterpret_cast(getJClass(t, class_)); +} + +void gcIfNecessary(MyThread* t) +{ + stress(t); + + if (UNLIKELY(t->getFlags() & Thread::UseBackupHeapFlag)) { + collect(t, Heap::MinorCollection); + } +} + +void idleIfNecessary(MyThread* t) +{ + if (UNLIKELY(t->m->exclusive)) { + ENTER(t, Thread::IdleState); + } +} + +bool useLongJump(MyThread* t, uintptr_t target) +{ + uintptr_t reach = t->arch->maximumImmediateJump(); + FixedAllocator* a = codeAllocator(t); + uintptr_t start = reinterpret_cast(a->memory.begin()); + uintptr_t end = reinterpret_cast(a->memory.begin()) + + a->memory.count; + assertT(t, end - start < reach); + + return (target > end && (target - start) > reach) + or (target < start && (end - target) > reach); +} + +FILE* compileLog = 0; + +void logCompile(MyThread* t, + const void* code, + unsigned size, + const char* class_, + const char* name, + const char* spec); + +unsigned simpleFrameMapTableSize(MyThread* t, GcMethod* method, GcIntArray* map) +{ + int size = frameMapSizeInBits(t, method); + return ceilingDivide(map->length() * size, 32 + size); +} + +#ifndef AVIAN_AOT_ONLY +unsigned resultSize(MyThread* t, unsigned code) +{ + switch (code) { + case ByteField: + case BooleanField: + case CharField: + case ShortField: + case FloatField: + case IntField: + return 4; + + case ObjectField: + return TargetBytesPerWord; + + case LongField: + case DoubleField: + return 8; + + case VoidField: + return 0; + + default: + abort(t); + } +} + +ir::Value* popField(MyThread* t, Frame* frame, int code) +{ + switch (code) { + case ByteField: + case BooleanField: + case CharField: + case ShortField: + case IntField: + return frame->pop(ir::Type::i4()); + case FloatField: + return frame->pop(ir::Type::f4()); + + case LongField: + return frame->popLarge(ir::Type::i8()); + case DoubleField: + return frame->popLarge(ir::Type::f8()); + + case ObjectField: + return frame->pop(ir::Type::object()); + + default: + abort(t); + } +} + +void compileSafePoint(MyThread* t, Compiler* c, Frame* frame) +{ + c->nativeCall( + c->constant(getThunk(t, idleIfNecessaryThunk), ir::Type::iptr()), + 0, + frame->trace(0, 0), + ir::Type::void_(), + args(c->threadRegister())); +} + +void compileDirectInvoke(MyThread* t, + Frame* frame, + GcMethod* target, + bool tailCall, + bool useThunk, + avian::codegen::Promise* addressPromise) +{ + avian::codegen::Compiler* c = frame->c; + + unsigned flags + = (avian::codegen::TailCalls and tailCall ? Compiler::TailJump : 0); + unsigned traceFlags; + + if (addressPromise == 0 and useLongJump(t, methodAddress(t, target))) { + flags |= Compiler::LongJumpOrCall; + traceFlags = TraceElement::LongCall; + } else { + traceFlags = 0; + } + + if (useThunk or (avian::codegen::TailCalls and tailCall + and (target->flags() & ACC_NATIVE))) { + if (frame->context->bootContext == 0) { + flags |= Compiler::Aligned; + } + + if (avian::codegen::TailCalls and tailCall) { + traceFlags |= TraceElement::TailCall; + + TraceElement* trace = frame->trace(target, traceFlags); + + avian::codegen::Promise* returnAddressPromise + = new (frame->context->zone.allocate(sizeof(TraceElementPromise))) + TraceElementPromise(t->m->system, trace); + + frame->stackCall( + c->promiseConstant(returnAddressPromise, ir::Type::iptr()), + target, + flags, + trace); + + c->store(frame->absoluteAddressOperand(returnAddressPromise), + c->memory(c->threadRegister(), + ir::Type::iptr(), + TARGET_THREAD_TAILADDRESS)); + + c->exit(c->constant( + (target->flags() & ACC_NATIVE) ? nativeThunk(t) : defaultThunk(t), + ir::Type::iptr())); + } else { + return frame->stackCall(c->constant(defaultThunk(t), ir::Type::iptr()), + target, + flags, + frame->trace(target, traceFlags)); + } + } else { + ir::Value* address + = (addressPromise + ? c->promiseConstant(addressPromise, ir::Type::iptr()) + : c->constant(methodAddress(t, target), ir::Type::iptr())); + + frame->stackCall( + address, + target, + flags, + tailCall ? 0 : frame->trace((target->flags() & ACC_NATIVE) ? target : 0, + 0)); + } +} + +bool compileDirectInvoke(MyThread* t, + Frame* frame, + GcMethod* target, + bool tailCall) +{ + // don't bother calling an empty method unless calling it might + // cause the class to be initialized, which may have side effects + if (emptyMethod(t, target) and (not classNeedsInit(t, target->class_()))) { + frame->popFootprint(target->parameterFootprint()); + tailCall = false; + } else { + BootContext* bc = frame->context->bootContext; + if (bc) { + if ((target->class_() == frame->context->method->class_() + or (not classNeedsInit(t, target->class_()))) + and (not(avian::codegen::TailCalls and tailCall + and (target->flags() & ACC_NATIVE)))) { + avian::codegen::Promise* p = new (bc->zone) + avian::codegen::ListenPromise(t->m->system, bc->zone); + + PROTECT(t, target); + object pointer = makePointer(t, p); + bc->calls = makeTriple(t, target, pointer, bc->calls); + + compileDirectInvoke(t, frame, target, tailCall, false, p); + } else { + compileDirectInvoke(t, frame, target, tailCall, true, 0); + } + } else if (unresolved(t, methodAddress(t, target)) + or classNeedsInit(t, target->class_())) { + compileDirectInvoke(t, frame, target, tailCall, true, 0); + } else { + compileDirectInvoke(t, frame, target, tailCall, false, 0); + } + } + + return tailCall; +} + +void compileReferenceInvoke(Frame* frame, + ir::Value* method, + GcReference* reference, + bool isStatic, + bool tailCall) +{ + frame->referenceStackCall(isStatic, + method, + reference, + tailCall ? Compiler::TailJump : 0, + frame->trace(0, 0)); +} + +void compileDirectReferenceInvoke(MyThread* t, + Frame* frame, + Thunk thunk, + GcReference* reference, + bool isStatic, + bool tailCall) +{ + avian::codegen::Compiler* c = frame->c; + + PROTECT(t, reference); + + GcPair* pair = makePair(t, frame->context->method, reference); + + compileReferenceInvoke( + frame, + c->nativeCall(c->constant(getThunk(t, thunk), ir::Type::iptr()), + 0, + frame->trace(0, 0), + ir::Type::iptr(), + args(c->threadRegister(), frame->append(pair))), + reference, + isStatic, + tailCall); +} + +void compileAbstractInvoke(Frame* frame, + ir::Value* method, + GcMethod* target, + bool tailCall) +{ + frame->stackCall( + method, target, tailCall ? Compiler::TailJump : 0, frame->trace(0, 0)); +} + +void compileDirectAbstractInvoke(MyThread* t, + Frame* frame, + Thunk thunk, + GcMethod* target, + bool tailCall) +{ + avian::codegen::Compiler* c = frame->c; + + compileAbstractInvoke( + frame, + c->nativeCall(c->constant(getThunk(t, thunk), ir::Type::iptr()), + 0, + frame->trace(0, 0), + ir::Type::iptr(), + args(c->threadRegister(), frame->append(target))), + target, + tailCall); +} + +void handleMonitorEvent(MyThread* t, Frame* frame, intptr_t function) +{ + avian::codegen::Compiler* c = frame->c; + GcMethod* method = frame->context->method; + + if (method->flags() & ACC_SYNCHRONIZED) { + ir::Value* lock; + if (method->flags() & ACC_STATIC) { + PROTECT(t, method); + + lock = frame->append(method->class_()); + } else { + lock = loadLocal( + frame->context, 1, ir::Type::object(), savedTargetIndex(t, method)); + } + + c->nativeCall(c->constant(function, ir::Type::iptr()), + 0, + frame->trace(0, 0), + ir::Type::void_(), + args(c->threadRegister(), lock)); + } +} + +void handleEntrance(MyThread* t, Frame* frame) +{ + GcMethod* method = frame->context->method; + + if ((method->flags() & (ACC_SYNCHRONIZED | ACC_STATIC)) == ACC_SYNCHRONIZED) { + // save 'this' pointer in case it is overwritten. + unsigned index = savedTargetIndex(t, method); + storeLocal(frame->context, + 1, + ir::Type::object(), + loadLocal(frame->context, 1, ir::Type::object(), 0), + index); + frame->set(index, ir::Type::object()); + } + + handleMonitorEvent(t, + frame, + getThunk(t, + method->flags() & ACC_STATIC + ? acquireMonitorForClassOnEntranceThunk + : acquireMonitorForObjectOnEntranceThunk)); +} + +void handleExit(MyThread* t, Frame* frame) +{ + handleMonitorEvent(t, + frame, + getThunk(t, + frame->context->method->flags() & ACC_STATIC + ? releaseMonitorForClassThunk + : releaseMonitorForObjectThunk)); +} + +bool inTryBlock(MyThread* t UNUSED, GcCode* code, unsigned ip) +{ + GcExceptionHandlerTable* table + = cast(t, code->exceptionHandlerTable()); + if (table) { + unsigned length = table->length(); + for (unsigned i = 0; i < length; ++i) { + uint64_t eh = table->body()[i]; + if (ip >= exceptionHandlerStart(eh) and ip < exceptionHandlerEnd(eh)) { + return true; + } + } + } + return false; +} + +bool needsReturnBarrier(MyThread* t UNUSED, GcMethod* method) +{ + return (method->flags() & ConstructorFlag) + and (method->class_()->vmFlags() & HasFinalMemberFlag); +} + +bool returnsNext(MyThread* t, GcCode* code, unsigned ip) +{ + switch (code->body()[ip]) { + case return_: + case areturn: + case ireturn: + case freturn: + case lreturn: + case dreturn: + return true; + + case goto_: { + uint32_t offset = codeReadInt16(t, code, ++ip); + uint32_t newIp = (ip - 3) + offset; + assertT(t, newIp < code->length()); + + return returnsNext(t, code, newIp); + } + + case goto_w: { + uint32_t offset = codeReadInt32(t, code, ++ip); + uint32_t newIp = (ip - 5) + offset; + assertT(t, newIp < code->length()); + + return returnsNext(t, code, newIp); + } + + default: + return false; + } +} + +bool isTailCall(MyThread* t, + GcCode* code, + unsigned ip, + GcMethod* caller, + int calleeReturnCode, + GcByteArray* calleeClassName, + GcByteArray* calleeMethodName, + GcByteArray* calleeMethodSpec) +{ + return avian::codegen::TailCalls + and ((caller->flags() & ACC_SYNCHRONIZED) == 0) + and (not inTryBlock(t, code, ip - 1)) + and (not needsReturnBarrier(t, caller)) + and (caller->returnCode() == VoidField + or caller->returnCode() == calleeReturnCode) + and returnsNext(t, code, ip) + and t->m->classpath->canTailCall(t, + caller, + calleeClassName, + calleeMethodName, + calleeMethodSpec); +} + +bool isTailCall(MyThread* t, + GcCode* code, + unsigned ip, + GcMethod* caller, + GcMethod* callee) +{ + return isTailCall(t, + code, + ip, + caller, + callee->returnCode(), + callee->class_()->name(), + callee->name(), + callee->spec()); +} + +bool isReferenceTailCall(MyThread* t, + GcCode* code, + unsigned ip, + GcMethod* caller, + GcReference* calleeReference) +{ + return isTailCall(t, + code, + ip, + caller, + methodReferenceReturnCode(t, calleeReference), + calleeReference->class_(), + calleeReference->name(), + calleeReference->spec()); +} + +lir::TernaryOperation toCompilerJumpOp(MyThread* t, unsigned instruction) +{ + switch (instruction) { + case ifeq: + case if_icmpeq: + case if_acmpeq: + case ifnull: + return lir::JumpIfEqual; + case ifne: + case if_icmpne: + case if_acmpne: + case ifnonnull: + return lir::JumpIfNotEqual; + case ifgt: + case if_icmpgt: + return lir::JumpIfGreater; + case ifge: + case if_icmpge: + return lir::JumpIfGreaterOrEqual; + case iflt: + case if_icmplt: + return lir::JumpIfLess; + case ifle: + case if_icmple: + return lir::JumpIfLessOrEqual; + default: + abort(t); + } +} + +bool integerBranch(MyThread* t, + Frame* frame, + GcCode* code, + unsigned& ip, + ir::Value* a, + ir::Value* b, + unsigned* newIpp) +{ + if (ip + 3 > code->length()) { + return false; + } + + avian::codegen::Compiler* c = frame->c; + unsigned instruction = code->body()[ip++]; + uint32_t offset = codeReadInt16(t, code, ip); + uint32_t newIp = (ip - 3) + offset; + assertT(t, newIp < code->length()); + + ir::Value* target = frame->machineIpValue(newIp); + + switch (instruction) { + case ifeq: + case ifne: + case ifgt: + case ifge: + case iflt: + case ifle: + c->condJump(toCompilerJumpOp(t, instruction), a, b, target); + break; + + default: + ip -= 3; + return false; + } + + *newIpp = newIp; + return true; +} + +lir::TernaryOperation toCompilerFloatJumpOp(MyThread* t, + unsigned instruction, + bool lessIfUnordered) +{ + switch (instruction) { + case ifeq: + return lir::JumpIfFloatEqual; + case ifne: + return lir::JumpIfFloatNotEqual; + case ifgt: + if (lessIfUnordered) { + return lir::JumpIfFloatGreater; + } else { + return lir::JumpIfFloatGreaterOrUnordered; + } + case ifge: + if (lessIfUnordered) { + return lir::JumpIfFloatGreaterOrEqual; + } else { + return lir::JumpIfFloatGreaterOrEqualOrUnordered; + } + case iflt: + if (lessIfUnordered) { + return lir::JumpIfFloatLessOrUnordered; + } else { + return lir::JumpIfFloatLess; + } + case ifle: + if (lessIfUnordered) { + return lir::JumpIfFloatLessOrEqualOrUnordered; + } else { + return lir::JumpIfFloatLessOrEqual; + } + default: + abort(t); + } +} + +bool floatBranch(MyThread* t, + Frame* frame, + GcCode* code, + unsigned& ip, + bool lessIfUnordered, + ir::Value* a, + ir::Value* b, + unsigned* newIpp) +{ + if (ip + 3 > code->length()) { + return false; + } + + avian::codegen::Compiler* c = frame->c; + unsigned instruction = code->body()[ip++]; + uint32_t offset = codeReadInt16(t, code, ip); + uint32_t newIp = (ip - 3) + offset; + assertT(t, newIp < code->length()); + + ir::Value* target = frame->machineIpValue(newIp); + + switch (instruction) { + case ifeq: + case ifne: + case ifgt: + case ifge: + case iflt: + case ifle: + c->condJump( + toCompilerFloatJumpOp(t, instruction, lessIfUnordered), a, b, target); + break; + + default: + ip -= 3; + return false; + } + + *newIpp = newIp; + return true; +} + +ir::Value* popLongAddress(Frame* frame) +{ + return TargetBytesPerWord == 8 + ? frame->popLarge(ir::Type::i8()) + : frame->c->load(ir::ExtendMode::Signed, + frame->popLarge(ir::Type::i8()), + ir::Type::iptr()); +} + +bool intrinsic(MyThread* t UNUSED, Frame* frame, GcMethod* target) +{ +#define MATCH(name, constant) \ + (name->length() == sizeof(constant) \ + and ::strcmp(reinterpret_cast(name->body().begin()), constant) == 0) + + GcByteArray* className = target->class_()->name(); + if (UNLIKELY(MATCH(className, "java/lang/Math"))) { + avian::codegen::Compiler* c = frame->c; + if (MATCH(target->name(), "sqrt") and MATCH(target->spec(), "(D)D")) { + frame->pushLarge( + ir::Type::f8(), + c->unaryOp(lir::FloatSquareRoot, frame->popLarge(ir::Type::f8()))); + return true; + } else if (MATCH(target->name(), "abs")) { + if (MATCH(target->spec(), "(I)I")) { + frame->push(ir::Type::i4(), + c->unaryOp(lir::Absolute, frame->pop(ir::Type::i4()))); + return true; + } else if (MATCH(target->spec(), "(J)J")) { + frame->pushLarge( + ir::Type::i8(), + c->unaryOp(lir::Absolute, frame->popLarge(ir::Type::i8()))); + return true; + } else if (MATCH(target->spec(), "(F)F")) { + frame->push(ir::Type::f4(), + c->unaryOp(lir::FloatAbsolute, frame->pop(ir::Type::f4()))); + return true; + } + } + } else if (UNLIKELY(MATCH(className, "sun/misc/Unsafe"))) { + avian::codegen::Compiler* c = frame->c; + if (MATCH(target->name(), "getByte") and MATCH(target->spec(), "(J)B")) { + ir::Value* address = popLongAddress(frame); + frame->pop(ir::Type::object()); + frame->push(ir::Type::i4(), + c->load(ir::ExtendMode::Signed, + c->memory(address, ir::Type::i1()), + ir::Type::i4())); + return true; + } else if (MATCH(target->name(), "putByte") + and MATCH(target->spec(), "(JB)V")) { + ir::Value* value = frame->pop(ir::Type::i4()); + ir::Value* address = popLongAddress(frame); + frame->pop(ir::Type::object()); + c->store(value, c->memory(address, ir::Type::i1())); + return true; + } else if ((MATCH(target->name(), "getShort") + and MATCH(target->spec(), "(J)S")) + or (MATCH(target->name(), "getChar") + and MATCH(target->spec(), "(J)C"))) { + ir::Value* address = popLongAddress(frame); + frame->pop(ir::Type::object()); + frame->push(ir::Type::i4(), + c->load(ir::ExtendMode::Signed, + c->memory(address, ir::Type::i2()), + ir::Type::i4())); + return true; + } else if ((MATCH(target->name(), "putShort") + and MATCH(target->spec(), "(JS)V")) + or (MATCH(target->name(), "putChar") + and MATCH(target->spec(), "(JC)V"))) { + ir::Value* value = frame->pop(ir::Type::i4()); + ir::Value* address = popLongAddress(frame); + frame->pop(ir::Type::object()); + c->store(value, c->memory(address, ir::Type::i2())); + return true; + } else if ((MATCH(target->name(), "getInt") + and MATCH(target->spec(), "(J)I")) + or (MATCH(target->name(), "getFloat") + and MATCH(target->spec(), "(J)F"))) { + ir::Value* address = popLongAddress(frame); + frame->pop(ir::Type::object()); + ir::Type type = MATCH(target->name(), "getInt") ? ir::Type::i4() + : ir::Type::f4(); + frame->push( + type, + c->load(ir::ExtendMode::Signed, c->memory(address, type), type)); + return true; + } else if ((MATCH(target->name(), "putInt") + and MATCH(target->spec(), "(JI)V")) + or (MATCH(target->name(), "putFloat") + and MATCH(target->spec(), "(JF)V"))) { + ir::Type type = MATCH(target->name(), "putInt") ? ir::Type::i4() + : ir::Type::f4(); + ir::Value* value = frame->pop(type); + ir::Value* address = popLongAddress(frame); + frame->pop(ir::Type::object()); + c->store(value, c->memory(address, type)); + return true; + } else if ((MATCH(target->name(), "getLong") + and MATCH(target->spec(), "(J)J")) + or (MATCH(target->name(), "getDouble") + and MATCH(target->spec(), "(J)D"))) { + ir::Value* address = popLongAddress(frame); + frame->pop(ir::Type::object()); + ir::Type type = MATCH(target->name(), "getLong") ? ir::Type::i8() + : ir::Type::f8(); + frame->pushLarge( + type, + c->load(ir::ExtendMode::Signed, c->memory(address, type), type)); + return true; + } else if ((MATCH(target->name(), "putLong") + and MATCH(target->spec(), "(JJ)V")) + or (MATCH(target->name(), "putDouble") + and MATCH(target->spec(), "(JD)V"))) { + ir::Type type = MATCH(target->name(), "putLong") ? ir::Type::i8() + : ir::Type::f8(); + ir::Value* value = frame->popLarge(type); + ir::Value* address = popLongAddress(frame); + frame->pop(ir::Type::object()); + c->store(value, c->memory(address, type)); + return true; + } else if (MATCH(target->name(), "getAddress") + and MATCH(target->spec(), "(J)J")) { + ir::Value* address = popLongAddress(frame); + frame->pop(ir::Type::object()); + frame->pushLarge(ir::Type::i8(), + c->load(ir::ExtendMode::Signed, + c->memory(address, ir::Type::iptr()), + ir::Type::i8())); + return true; + } else if (MATCH(target->name(), "putAddress") + and MATCH(target->spec(), "(JJ)V")) { + ir::Value* value = frame->popLarge(ir::Type::i8()); + ir::Value* address = popLongAddress(frame); + frame->pop(ir::Type::object()); + c->store(value, c->memory(address, ir::Type::iptr())); + return true; + } + } + return false; +} + +unsigned targetFieldOffset(Context* context, GcField* field) +{ + if (context->bootContext) { + return context->bootContext->resolver->fieldOffset(context->thread, field); + } else { + return field->offset(); + } +} + +class Stack { + public: + class MyResource : public Thread::AutoResource { + public: + MyResource(Stack* s) : AutoResource(s->thread), s(s) + { + } + + virtual void release() + { + s->zone.dispose(); + } + + Stack* s; + }; + + Stack(MyThread* t) : thread(t), zone(t->m->heap, 0), resource(this) + { + } + + ~Stack() + { + zone.dispose(); + } + + void pushValue(uintptr_t v) + { + *static_cast(push(BytesPerWord)) = v; + } + + uintptr_t peekValue(unsigned offset) + { + return *static_cast(peek((offset + 1) * BytesPerWord)); + } + + uintptr_t popValue() + { + uintptr_t v = peekValue(0); + pop(BytesPerWord); + return v; + } + + void* push(unsigned size) + { + return zone.allocate(size); + } + + void* peek(unsigned size) + { + return zone.peek(size); + } + + void pop(unsigned size) + { + zone.pop(size); + } + + MyThread* thread; + Zone zone; + MyResource resource; +}; + +class SwitchState { + public: + SwitchState(Compiler::State* state, + unsigned count, + unsigned defaultIp, + ir::Value* key, + avian::codegen::Promise* start, + int bottom, + int top) + : state(state), + count(count), + defaultIp(defaultIp), + key(key), + start(start), + bottom(bottom), + top(top), + index(0) + { + } + + Frame* frame() + { + return reinterpret_cast(reinterpret_cast(this) + - pad(count * 4) - pad(sizeof(Frame))); + } + + uint32_t* ipTable() + { + return reinterpret_cast(reinterpret_cast(this) + - pad(count * 4)); + } + + Compiler::State* state; + unsigned count; + unsigned defaultIp; + ir::Value* key; + avian::codegen::Promise* start; + int bottom; + int top; + unsigned index; +}; + +lir::TernaryOperation toCompilerBinaryOp(MyThread* t, unsigned instruction) +{ + switch (instruction) { + case iadd: + case ladd: + return lir::Add; + case ior: + case lor: + return lir::Or; + case ishl: + case lshl: + return lir::ShiftLeft; + case ishr: + case lshr: + return lir::ShiftRight; + case iushr: + case lushr: + return lir::UnsignedShiftRight; + case fadd: + case dadd: + return lir::FloatAdd; + case fsub: + case dsub: + return lir::FloatSubtract; + case fmul: + case dmul: + return lir::FloatMultiply; + case fdiv: + case ddiv: + return lir::FloatDivide; + case frem: + case vm::drem: + return lir::FloatRemainder; + case iand: + case land: + return lir::And; + case isub: + case lsub: + return lir::Subtract; + case ixor: + case lxor: + return lir::Xor; + case imul: + case lmul: + return lir::Multiply; + default: + abort(t); + } +} + +uintptr_t aioobThunk(MyThread* t); + +uintptr_t stackOverflowThunk(MyThread* t); + +void checkField(Thread* t, GcField* field, bool shouldBeStatic) +{ + if (((field->flags() & ACC_STATIC) == 0) == shouldBeStatic) { + throwNew(t, + GcIncompatibleClassChangeError::Type, + "expected %s.%s to be %s", + field->class_()->name()->body().begin(), + field->name()->body().begin(), + shouldBeStatic ? "static" : "non-static"); + } +} + +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) + or (vm::strcmp(reinterpret_cast("altMetafactory"), + bootstrap->name()->body().begin()) == 0 + and vm::strcmp( + reinterpret_cast( + "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/" + "lang/" + "String;Ljava/lang/invoke/MethodType;[Ljava/lang/" + "Object;)Ljava/lang/invoke/CallSite;"), + bootstrap->spec()->body().begin()) == 0)); +} + +void compile(MyThread* t, + Frame* initialFrame, + unsigned initialIp, + int exceptionHandlerStart = -1) +{ + enum { Return, Unbranch, Unsubroutine, Untable0, Untable1, Unswitch }; + + Frame* frame = initialFrame; + avian::codegen::Compiler* c = frame->c; + Context* context = frame->context; + unsigned stackSize = context->method->code()->maxStack(); + Stack stack(t); + unsigned ip = initialIp; + unsigned newIp; + stack.pushValue(Return); + +start: + ir::Type* stackMap + = static_cast(stack.push(stackSize * sizeof(ir::Type))); + frame = new (stack.push(sizeof(Frame))) Frame(frame, stackMap); + +loop: + GcCode* code = context->method->code(); + PROTECT(t, code); + + while (ip < code->length()) { + if (context->visitTable[frame->duplicatedIp(ip)]++) { + // we've already visited this part of the code + frame->visitLogicalIp(ip); + goto next; + } + + frame->startLogicalIp(ip); + + if (exceptionHandlerStart >= 0) { + c->initLocalsFromLogicalIp(exceptionHandlerStart); + + exceptionHandlerStart = -1; + + frame->pushObject(); + + c->nativeCall( + c->constant(getThunk(t, gcIfNecessaryThunk), ir::Type::iptr()), + 0, + frame->trace(0, 0), + ir::Type::void_(), + args(c->threadRegister())); + } + + if (DebugInstructions) { + unsigned startingIp = ip; + fprintf(stderr, " stack: ["); + for (size_t i = frame->localSize(); i < frame->sp; i++) { + ir::Type ty = frame->get(i); + if (ty == ir::Type::i4()) { + fprintf(stderr, "I"); + } else if (ty == ir::Type::i8()) { + fprintf(stderr, "L"); + } else if (ty == ir::Type::f4()) { + fprintf(stderr, "F"); + } else if (ty == ir::Type::f8()) { + fprintf(stderr, "D"); + } else if (ty == ir::Type::object()) { + fprintf(stderr, "O"); + } else { + fprintf(stderr, "?"); + } + } + fprintf(stderr, "]\n"); + fprintf(stderr, "% 5d: ", startingIp); + avian::jvm::debug::printInstruction(code->body().begin(), startingIp); + fprintf(stderr, "\n"); + } + + unsigned instruction = code->body()[ip++]; + + switch (instruction) { + case aaload: + case baload: + case caload: + case daload: + case faload: + case iaload: + case laload: + case saload: { + ir::Value* index = frame->pop(ir::Type::i4()); + ir::Value* array = frame->pop(ir::Type::object()); + + if (inTryBlock(t, code, ip - 1)) { + c->saveLocals(); + frame->trace(0, 0); + } + + if (CheckArrayBounds) { + c->checkBounds(array, TargetArrayLength, index, aioobThunk(t)); + } + + switch (instruction) { + case aaload: + frame->push( + ir::Type::object(), + c->load( + ir::ExtendMode::Signed, + c->memory(array, ir::Type::object(), TargetArrayBody, index), + ir::Type::object())); + break; + + case faload: + frame->push( + ir::Type::f4(), + c->load(ir::ExtendMode::Signed, + c->memory(array, ir::Type::f4(), TargetArrayBody, index), + ir::Type::f4())); + break; + + case iaload: + frame->push( + ir::Type::i4(), + c->load(ir::ExtendMode::Signed, + c->memory(array, ir::Type::i4(), TargetArrayBody, index), + ir::Type::i4())); + break; + + case baload: + frame->push( + ir::Type::i4(), + c->load(ir::ExtendMode::Signed, + c->memory(array, ir::Type::i1(), TargetArrayBody, index), + ir::Type::i4())); + break; + + case caload: + frame->push( + ir::Type::i4(), + c->load(ir::ExtendMode::Unsigned, + c->memory(array, ir::Type::i2(), TargetArrayBody, index), + ir::Type::i4())); + break; + + case daload: + frame->pushLarge( + ir::Type::f8(), + c->load(ir::ExtendMode::Signed, + c->memory(array, ir::Type::f8(), TargetArrayBody, index), + ir::Type::f8())); + break; + + case laload: + frame->pushLarge( + ir::Type::i8(), + c->load(ir::ExtendMode::Signed, + c->memory(array, ir::Type::i8(), TargetArrayBody, index), + ir::Type::i8())); + break; + + case saload: + frame->push( + ir::Type::i4(), + c->load(ir::ExtendMode::Signed, + c->memory(array, ir::Type::i2(), TargetArrayBody, index), + ir::Type::i4())); + break; + } + } break; + + case aastore: + case bastore: + case castore: + case dastore: + case fastore: + case iastore: + case lastore: + case sastore: { + ir::Value* value; + if (instruction == lastore) { + value = frame->popLarge(ir::Type::i8()); + } else if (instruction == dastore) { + value = frame->popLarge(ir::Type::f8()); + } else if (instruction == aastore) { + value = frame->pop(ir::Type::object()); + } else if (instruction == fastore) { + value = frame->pop(ir::Type::f4()); + } else { + value = frame->pop(ir::Type::i4()); + } + + ir::Value* index = frame->pop(ir::Type::i4()); + ir::Value* array = frame->pop(ir::Type::object()); + + if (inTryBlock(t, code, ip - 1)) { + c->saveLocals(); + frame->trace(0, 0); + } + + if (CheckArrayBounds) { + c->checkBounds(array, TargetArrayLength, index, aioobThunk(t)); + } + + switch (instruction) { + case aastore: { + c->nativeCall( + c->constant(getThunk(t, setMaybeNullThunk), ir::Type::iptr()), + 0, + frame->trace(0, 0), + ir::Type::void_(), + args(c->threadRegister(), + array, + c->binaryOp(lir::Add, + ir::Type::i4(), + c->constant(TargetArrayBody, ir::Type::i4()), + c->binaryOp(lir::ShiftLeft, + ir::Type::i4(), + c->constant(log(TargetBytesPerWord), + ir::Type::i4()), + index)), + value)); + } break; + + case fastore: + c->store(value, + c->memory(array, ir::Type::f4(), TargetArrayBody, index)); + break; + + case iastore: + c->store(value, + c->memory(array, ir::Type::i4(), TargetArrayBody, index)); + break; + + case bastore: + c->store(value, + c->memory(array, ir::Type::i1(), TargetArrayBody, index)); + break; + + case castore: + case sastore: + c->store(value, + c->memory(array, ir::Type::i2(), TargetArrayBody, index)); + break; + + case dastore: + c->store(value, + c->memory(array, ir::Type::f8(), TargetArrayBody, index)); + break; + + case lastore: + c->store(value, + c->memory(array, ir::Type::i8(), TargetArrayBody, index)); + break; + } + } break; + + case aconst_null: + frame->push(ir::Type::object(), c->constant(0, ir::Type::object())); + break; + + case aload: + frame->load(ir::Type::object(), code->body()[ip++]); + break; + + case aload_0: + frame->load(ir::Type::object(), 0); + break; + + case aload_1: + frame->load(ir::Type::object(), 1); + break; + + case aload_2: + frame->load(ir::Type::object(), 2); + break; + + case aload_3: + frame->load(ir::Type::object(), 3); + break; + + case anewarray: { + uint16_t index = codeReadInt16(t, code, ip); + + object reference + = singletonObject(t, context->method->code()->pool(), index - 1); + + PROTECT(t, reference); + + GcClass* class_ + = resolveClassInPool(t, context->method, index - 1, false); + + ir::Value* length = frame->pop(ir::Type::i4()); + + object argument; + Thunk thunk; + if (LIKELY(class_)) { + argument = class_; + thunk = makeBlankObjectArrayThunk; + } else { + argument = makePair(t, context->method, reference); + thunk = makeBlankObjectArrayFromReferenceThunk; + } + + frame->push( + ir::Type::object(), + c->nativeCall( + c->constant(getThunk(t, thunk), ir::Type::iptr()), + 0, + frame->trace(0, 0), + ir::Type::object(), + args(c->threadRegister(), frame->append(argument), length))); + } break; + + case areturn: { + handleExit(t, frame); + c->return_(frame->pop(ir::Type::object())); + } + goto next; + + case arraylength: { + frame->push(ir::Type::i4(), + c->load(ir::ExtendMode::Signed, + c->memory(frame->pop(ir::Type::object()), + ir::Type::iptr(), + TargetArrayLength), + ir::Type::i4())); + } break; + + case astore: + frame->store(ir::Type::object(), code->body()[ip++]); + break; + + case astore_0: + frame->store(ir::Type::object(), 0); + break; + + case astore_1: + frame->store(ir::Type::object(), 1); + break; + + case astore_2: + frame->store(ir::Type::object(), 2); + break; + + case astore_3: + frame->store(ir::Type::object(), 3); + break; + + case athrow: { + ir::Value* target = frame->pop(ir::Type::object()); + c->nativeCall(c->constant(getThunk(t, throw_Thunk), ir::Type::iptr()), + Compiler::NoReturn, + frame->trace(0, 0), + ir::Type::void_(), + args(c->threadRegister(), target)); + + c->nullaryOp(lir::Trap); + } + goto next; + + case bipush: + frame->push( + ir::Type::i4(), + c->constant(static_cast(code->body()[ip++]), ir::Type::i4())); + break; + + case checkcast: { + uint16_t index = codeReadInt16(t, code, ip); + + object reference + = singletonObject(t, context->method->code()->pool(), index - 1); + + PROTECT(t, reference); + + GcClass* class_ + = resolveClassInPool(t, context->method, index - 1, false); + + object argument; + Thunk thunk; + if (LIKELY(class_)) { + argument = class_; + thunk = checkCastThunk; + } else { + argument = makePair(t, context->method, reference); + thunk = checkCastFromReferenceThunk; + } + + ir::Value* instance = c->peek(1, 0); + + c->nativeCall( + c->constant(getThunk(t, thunk), ir::Type::iptr()), + 0, + frame->trace(0, 0), + ir::Type::void_(), + args(c->threadRegister(), frame->append(argument), instance)); + } break; + + case d2f: { + frame->push(ir::Type::f4(), + c->f2f(ir::Type::f4(), frame->popLarge(ir::Type::f8()))); + } break; + + case d2i: { + frame->push(ir::Type::i4(), + c->f2i(ir::Type::i4(), frame->popLarge(ir::Type::f8()))); + } break; + + case d2l: { + frame->pushLarge(ir::Type::i8(), + c->f2i(ir::Type::i8(), frame->popLarge(ir::Type::f8()))); + } break; + + case dadd: + case dsub: + case dmul: + case ddiv: + case vm::drem: { + ir::Value* a = frame->popLarge(ir::Type::f8()); + ir::Value* b = frame->popLarge(ir::Type::f8()); + + frame->pushLarge( + ir::Type::f8(), + c->binaryOp( + toCompilerBinaryOp(t, instruction), ir::Type::f8(), a, b)); + } break; + + case dcmpg: { + ir::Value* a = frame->popLarge(ir::Type::f8()); + ir::Value* b = frame->popLarge(ir::Type::f8()); + + if (floatBranch(t, frame, code, ip, false, a, b, &newIp)) { + goto branch; + } else { + frame->push(ir::Type::i4(), + c->nativeCall(c->constant(getThunk(t, compareDoublesGThunk), + ir::Type::iptr()), + 0, + 0, + ir::Type::i4(), + args(nullptr, a, nullptr, b))); + } + } break; + + case dcmpl: { + ir::Value* a = frame->popLarge(ir::Type::f8()); + ir::Value* b = frame->popLarge(ir::Type::f8()); + + if (floatBranch(t, frame, code, ip, true, a, b, &newIp)) { + goto branch; + } else { + frame->push(ir::Type::i4(), + c->nativeCall(c->constant(getThunk(t, compareDoublesLThunk), + ir::Type::iptr()), + 0, + 0, + ir::Type::i4(), + args(nullptr, a, nullptr, b))); + } + } break; + + case dconst_0: + frame->pushLarge(ir::Type::f8(), + c->constant(doubleToBits(0.0), ir::Type::f8())); + break; + + case dconst_1: + frame->pushLarge(ir::Type::f8(), + c->constant(doubleToBits(1.0), ir::Type::f8())); + break; + + case dneg: { + frame->pushLarge( + ir::Type::f8(), + c->unaryOp(lir::FloatNegate, frame->popLarge(ir::Type::f8()))); + } break; + + case vm::dup: + frame->dup(); + break; + + case dup_x1: + frame->dupX1(); + break; + + case dup_x2: + frame->dupX2(); + break; + + case vm::dup2: + frame->dup2(); + break; + + case dup2_x1: + frame->dup2X1(); + break; + + case dup2_x2: + frame->dup2X2(); + break; + + case f2d: { + frame->pushLarge(ir::Type::f8(), + c->f2f(ir::Type::f8(), frame->pop(ir::Type::f4()))); + } break; + + case f2i: { + frame->push(ir::Type::i4(), + c->f2i(ir::Type::i4(), frame->pop(ir::Type::f4()))); + } break; + + case f2l: { + frame->pushLarge(ir::Type::i8(), + c->f2i(ir::Type::i8(), frame->pop(ir::Type::f4()))); + } break; + + case fadd: + case fsub: + case fmul: + case fdiv: + case frem: { + ir::Value* a = frame->pop(ir::Type::f4()); + ir::Value* b = frame->pop(ir::Type::f4()); + + frame->push( + ir::Type::f4(), + c->binaryOp( + toCompilerBinaryOp(t, instruction), ir::Type::f4(), a, b)); + } break; + + case fcmpg: { + ir::Value* a = frame->pop(ir::Type::f4()); + ir::Value* b = frame->pop(ir::Type::f4()); + + if (floatBranch(t, frame, code, ip, false, a, b, &newIp)) { + goto branch; + } else { + frame->push(ir::Type::i4(), + c->nativeCall(c->constant(getThunk(t, compareFloatsGThunk), + ir::Type::iptr()), + 0, + 0, + ir::Type::i4(), + args(a, b))); + } + } break; + + case fcmpl: { + ir::Value* a = frame->pop(ir::Type::f4()); + ir::Value* b = frame->pop(ir::Type::f4()); + + if (floatBranch(t, frame, code, ip, true, a, b, &newIp)) { + goto branch; + } else { + frame->push(ir::Type::i4(), + c->nativeCall(c->constant(getThunk(t, compareFloatsLThunk), + ir::Type::iptr()), + 0, + 0, + ir::Type::i4(), + args(a, b))); + } + } break; + + case fconst_0: + frame->push(ir::Type::f4(), + c->constant(floatToBits(0.0), ir::Type::f4())); + break; + + case fconst_1: + frame->push(ir::Type::f4(), + c->constant(floatToBits(1.0), ir::Type::f4())); + break; + + case fconst_2: + frame->push(ir::Type::f4(), + c->constant(floatToBits(2.0), ir::Type::f4())); + break; + + case fneg: { + frame->push(ir::Type::f4(), + c->unaryOp(lir::FloatNegate, frame->pop(ir::Type::f4()))); + } break; + + case getfield: + case getstatic: { + uint16_t index = codeReadInt16(t, code, ip); + + object reference + = singletonObject(t, context->method->code()->pool(), index - 1); + + PROTECT(t, reference); + + GcField* field = resolveField(t, context->method, index - 1, false); + + if (LIKELY(field)) { + if ((field->flags() & ACC_VOLATILE) and TargetBytesPerWord == 4 + and (field->code() == DoubleField or field->code() == LongField)) { + PROTECT(t, field); + + c->nativeCall(c->constant(getThunk(t, acquireMonitorForObjectThunk), + ir::Type::iptr()), + 0, + frame->trace(0, 0), + ir::Type::void_(), + args(c->threadRegister(), frame->append(field))); + } + + ir::Value* table; + + if (instruction == getstatic) { + checkField(t, field, true); + + PROTECT(t, field); + + if (classNeedsInit(t, field->class_())) { + c->nativeCall( + c->constant(getThunk(t, tryInitClassThunk), ir::Type::iptr()), + 0, + frame->trace(0, 0), + ir::Type::void_(), + args(c->threadRegister(), frame->append(field->class_()))); + } + + table = frame->append(field->class_()->staticTable()); + } else { + checkField(t, field, false); + + table = frame->pop(ir::Type::object()); + + if (inTryBlock(t, code, ip - 3)) { + c->saveLocals(); + frame->trace(0, 0); + } + } + + switch (field->code()) { + case ByteField: + case BooleanField: + frame->push(ir::Type::i4(), + c->load(ir::ExtendMode::Signed, + c->memory(table, + ir::Type::i1(), + targetFieldOffset(context, field)), + ir::Type::i4())); + break; + + case CharField: + frame->push(ir::Type::i4(), + c->load(ir::ExtendMode::Unsigned, + c->memory(table, + ir::Type::i2(), + targetFieldOffset(context, field)), + ir::Type::i4())); + break; + + case ShortField: + frame->push(ir::Type::i4(), + c->load(ir::ExtendMode::Signed, + c->memory(table, + ir::Type::i2(), + targetFieldOffset(context, field)), + ir::Type::i4())); + break; + + case FloatField: + frame->push(ir::Type::f4(), + c->load(ir::ExtendMode::Signed, + c->memory(table, + ir::Type::f4(), + targetFieldOffset(context, field)), + ir::Type::f4())); + break; + + case IntField: + frame->push(ir::Type::i4(), + c->load(ir::ExtendMode::Signed, + c->memory(table, + ir::Type::i4(), + targetFieldOffset(context, field)), + ir::Type::i4())); + break; + + case DoubleField: + frame->pushLarge(ir::Type::f8(), + c->load(ir::ExtendMode::Signed, + c->memory(table, + ir::Type::f8(), + targetFieldOffset(context, field)), + ir::Type::f8())); + break; + + case LongField: + frame->pushLarge(ir::Type::i8(), + c->load(ir::ExtendMode::Signed, + c->memory(table, + ir::Type::i8(), + targetFieldOffset(context, field)), + ir::Type::i8())); + break; + + case ObjectField: + frame->push(ir::Type::object(), + c->load(ir::ExtendMode::Signed, + c->memory(table, + ir::Type::object(), + targetFieldOffset(context, field)), + ir::Type::object())); + break; + + default: + abort(t); + } + + if (field->flags() & ACC_VOLATILE) { + if (TargetBytesPerWord == 4 and (field->code() == DoubleField + or field->code() == LongField)) { + c->nativeCall(c->constant(getThunk(t, releaseMonitorForObjectThunk), + ir::Type::iptr()), + 0, + frame->trace(0, 0), + ir::Type::void_(), + args(c->threadRegister(), frame->append(field))); + } else { + c->nullaryOp(lir::LoadBarrier); + } + } + } else { + GcReference* ref = cast(t, reference); + PROTECT(t, ref); + int fieldCode = vm::fieldCode(t, ref->spec()->body()[0]); + + GcPair* pair = makePair(t, context->method, reference); + + ir::Type rType = operandTypeForFieldCode(t, fieldCode); + + ir::Value* result; + if (instruction == getstatic) { + result = c->nativeCall( + c->constant(getThunk(t, getStaticFieldValueFromReferenceThunk), + ir::Type::iptr()), + 0, + frame->trace(0, 0), + rType, + args(c->threadRegister(), frame->append(pair))); + } else { + ir::Value* instance = frame->pop(ir::Type::object()); + + result = c->nativeCall( + c->constant(getThunk(t, getFieldValueFromReferenceThunk), + ir::Type::iptr()), + 0, + frame->trace(0, 0), + rType, + args(c->threadRegister(), frame->append(pair), instance)); + } + + frame->pushReturnValue(fieldCode, result); + } + } break; + + case goto_: { + uint32_t offset = codeReadInt16(t, code, ip); + uint32_t newIp = (ip - 3) + offset; + assertT(t, newIp < code->length()); + + if (newIp <= ip) { + compileSafePoint(t, c, frame); + } + + c->jmp(frame->machineIpValue(newIp)); + ip = newIp; + } break; + + case goto_w: { + uint32_t offset = codeReadInt32(t, code, ip); + uint32_t newIp = (ip - 5) + offset; + assertT(t, newIp < code->length()); + + if (newIp <= ip) { + compileSafePoint(t, c, frame); + } + + c->jmp(frame->machineIpValue(newIp)); + ip = newIp; + } break; + + case i2b: { + frame->push(ir::Type::i4(), + c->truncateThenExtend(ir::ExtendMode::Signed, + ir::Type::i4(), + ir::Type::i1(), + frame->pop(ir::Type::i4()))); + } break; + + case i2c: { + frame->push(ir::Type::i4(), + c->truncateThenExtend(ir::ExtendMode::Unsigned, + ir::Type::i4(), + ir::Type::i2(), + frame->pop(ir::Type::i4()))); + } break; + + case i2d: { + frame->pushLarge(ir::Type::f8(), + c->i2f(ir::Type::f8(), frame->pop(ir::Type::i4()))); + } break; + + case i2f: { + frame->push(ir::Type::f4(), + c->i2f(ir::Type::f4(), frame->pop(ir::Type::i4()))); + } break; + + case i2l: + frame->pushLarge(ir::Type::i8(), + c->truncateThenExtend(ir::ExtendMode::Signed, + ir::Type::i8(), + ir::Type::i4(), + frame->pop(ir::Type::i4()))); + break; + + case i2s: { + frame->push(ir::Type::i4(), + c->truncateThenExtend(ir::ExtendMode::Signed, + ir::Type::i4(), + ir::Type::i2(), + frame->pop(ir::Type::i4()))); + } break; + + case iadd: + case iand: + case ior: + case ishl: + case ishr: + case iushr: + case isub: + case ixor: + case imul: { + ir::Value* a = frame->pop(ir::Type::i4()); + ir::Value* b = frame->pop(ir::Type::i4()); + frame->push( + ir::Type::i4(), + c->binaryOp( + toCompilerBinaryOp(t, instruction), ir::Type::i4(), a, b)); + } break; + + case iconst_m1: + frame->push(ir::Type::i4(), c->constant(-1, ir::Type::i4())); + break; + + case iconst_0: + frame->push(ir::Type::i4(), c->constant(0, ir::Type::i4())); + break; + + case iconst_1: + frame->push(ir::Type::i4(), c->constant(1, ir::Type::i4())); + break; + + case iconst_2: + frame->push(ir::Type::i4(), c->constant(2, ir::Type::i4())); + break; + + case iconst_3: + frame->push(ir::Type::i4(), c->constant(3, ir::Type::i4())); + break; + + case iconst_4: + frame->push(ir::Type::i4(), c->constant(4, ir::Type::i4())); + break; + + case iconst_5: + frame->push(ir::Type::i4(), c->constant(5, ir::Type::i4())); + break; + + case idiv: { + ir::Value* a = frame->pop(ir::Type::i4()); + ir::Value* b = frame->pop(ir::Type::i4()); + + if (inTryBlock(t, code, ip - 1)) { + c->saveLocals(); + frame->trace(0, 0); + } + + frame->push(ir::Type::i4(), + c->binaryOp(lir::Divide, ir::Type::i4(), a, b)); + } break; + + case if_acmpeq: + case if_acmpne: { + uint32_t offset = codeReadInt16(t, code, ip); + newIp = (ip - 3) + offset; + assertT(t, newIp < code->length()); + + if (newIp <= ip) { + compileSafePoint(t, c, frame); + } + + ir::Value* a = frame->pop(ir::Type::object()); + ir::Value* b = frame->pop(ir::Type::object()); + ir::Value* target = frame->machineIpValue(newIp); + + c->condJump(toCompilerJumpOp(t, instruction), a, b, target); + } + goto branch; + + case if_icmpeq: + case if_icmpne: + case if_icmpgt: + case if_icmpge: + case if_icmplt: + case if_icmple: { + uint32_t offset = codeReadInt16(t, code, ip); + newIp = (ip - 3) + offset; + assertT(t, newIp < code->length()); + + if (newIp <= ip) { + compileSafePoint(t, c, frame); + } + + ir::Value* a = frame->pop(ir::Type::i4()); + ir::Value* b = frame->pop(ir::Type::i4()); + ir::Value* target = frame->machineIpValue(newIp); + + c->condJump(toCompilerJumpOp(t, instruction), a, b, target); + } + goto branch; + + case ifeq: + case ifne: + case ifgt: + case ifge: + case iflt: + case ifle: { + uint32_t offset = codeReadInt16(t, code, ip); + newIp = (ip - 3) + offset; + assertT(t, newIp < code->length()); + + ir::Value* target = frame->machineIpValue(newIp); + + if (newIp <= ip) { + compileSafePoint(t, c, frame); + } + + ir::Value* a = c->constant(0, ir::Type::i4()); + ir::Value* b = frame->pop(ir::Type::i4()); + + c->condJump(toCompilerJumpOp(t, instruction), a, b, target); + } + goto branch; + + case ifnull: + case ifnonnull: { + uint32_t offset = codeReadInt16(t, code, ip); + newIp = (ip - 3) + offset; + assertT(t, newIp < code->length()); + + if (newIp <= ip) { + compileSafePoint(t, c, frame); + } + + ir::Value* a = c->constant(0, ir::Type::object()); + ir::Value* b = frame->pop(ir::Type::object()); + ir::Value* target = frame->machineIpValue(newIp); + + c->condJump(toCompilerJumpOp(t, instruction), a, b, target); + } + goto branch; + + case iinc: { + uint8_t index = code->body()[ip++]; + int8_t count = code->body()[ip++]; + + storeLocal(context, + 1, + ir::Type::i4(), + c->binaryOp(lir::Add, + ir::Type::i4(), + c->constant(count, ir::Type::i4()), + loadLocal(context, 1, ir::Type::i4(), index)), + index); + } break; + + case iload: + frame->load(ir::Type::i4(), code->body()[ip++]); + break; + case fload: + frame->load(ir::Type::f4(), code->body()[ip++]); + break; + + case iload_0: + frame->load(ir::Type::i4(), 0); + break; + case fload_0: + frame->load(ir::Type::f4(), 0); + break; + + case iload_1: + frame->load(ir::Type::i4(), 1); + break; + case fload_1: + frame->load(ir::Type::f4(), 1); + break; + + case iload_2: + frame->load(ir::Type::i4(), 2); + break; + case fload_2: + frame->load(ir::Type::f4(), 2); + break; + + case iload_3: + frame->load(ir::Type::i4(), 3); + break; + case fload_3: + frame->load(ir::Type::f4(), 3); + break; + + case ineg: { + frame->push(ir::Type::i4(), + c->unaryOp(lir::Negate, frame->pop(ir::Type::i4()))); + } break; + + case instanceof: { + uint16_t index = codeReadInt16(t, code, ip); + + object reference + = singletonObject(t, context->method->code()->pool(), index - 1); + + PROTECT(t, reference); + + GcClass* class_ + = resolveClassInPool(t, context->method, index - 1, false); + + ir::Value* instance = frame->pop(ir::Type::object()); + + object argument; + Thunk thunk; + if (LIKELY(class_)) { + argument = class_; + thunk = instanceOf64Thunk; + } else { + argument = makePair(t, context->method, reference); + thunk = instanceOfFromReferenceThunk; + } + + frame->push( + ir::Type::i4(), + c->nativeCall( + c->constant(getThunk(t, thunk), ir::Type::iptr()), + 0, + frame->trace(0, 0), + ir::Type::i4(), + args(c->threadRegister(), frame->append(argument), instance))); + } break; + + case invokedynamic: { + context->leaf = false; + + uint16_t poolIndex = codeReadInt16(t, code, ip); + ip += 2; + + GcInvocation* invocation = cast( + t, + singletonObject(t, context->method->code()->pool(), poolIndex - 1)); + + PROTECT(t, invocation); + + invocation->setClass(t, context->method->class_()); + + 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). + + GcClass* c = context->method->class_(); + PROTECT(t, c); + + GcMethod* target + = c->addendum()->bootstrapLambdaTable() + ? cast( + t, + cast(t, c->addendum()->bootstrapLambdaTable()) + ->body()[invocation->bootstrap()]) + : nullptr; + PROTECT(t, target); + + if (target == nullptr) { + GcCharArray* bootstrapArray = cast( + t, + cast(t, c->addendum()->bootstrapMethodTable()) + ->body()[invocation->bootstrap()]); + PROTECT(t, bootstrapArray); + + if (isLambda(t, c->loader(), bootstrapArray, invocation)) { + if (bc->hostVM == 0) { + throwNew( + t, + GcVirtualMachineError::Type, + "lambda expression encountered, but host VM is not " + "available; use -hostvm option to bootimage-generator to " + "fix this"); + } + + JNIEnv* e; + if (bc->hostVM->vtable->AttachCurrentThread(bc->hostVM, &e, 0) + == 0) { + e->vtable->PushLocalFrame(e, 256); + + 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"); + + GcReference* reference = cast( + t, + singletonObject( + t, invocation->pool(), bootstrapArray->body()[2])); + int kind = reference->kind(); + + 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()); + + target = resolveMethod( + t, lambdaClass, "make", RUNTIME_ARRAY_BODY(spec)); + + GcArray* table + = cast(t, c->addendum()->bootstrapLambdaTable()); + if (table == nullptr) { + table = makeArray( + t, + cast(t, c->addendum()->bootstrapMethodTable()) + ->length()); + c->addendum()->setBootstrapLambdaTable(t, table); + } + + table->setBodyElement(t, invocation->bootstrap(), target); + } 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"); + } + } + + bool tailCall = isTailCall(t, code, ip, context->method, target); + compileDirectInvoke(t, frame, target, tailCall); + } 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; + + case invokeinterface: { + context->leaf = false; + + uint16_t index = codeReadInt16(t, code, ip); + ip += 2; + + object reference + = singletonObject(t, context->method->code()->pool(), index - 1); + + PROTECT(t, reference); + + GcMethod* target = resolveMethod(t, context->method, index - 1, false); + + object argument; + Thunk thunk; + unsigned parameterFootprint; + int returnCode; + bool tailCall; + if (LIKELY(target)) { + checkMethod(t, target, false); + + argument = target; + thunk = findInterfaceMethodFromInstanceThunk; + parameterFootprint = target->parameterFootprint(); + returnCode = target->returnCode(); + tailCall = isTailCall(t, code, ip, context->method, target); + } else { + GcReference* ref = cast(t, reference); + PROTECT(t, ref); + argument = makePair(t, context->method, reference); + thunk = findInterfaceMethodFromInstanceAndReferenceThunk; + parameterFootprint = methodReferenceParameterFootprint(t, ref, false); + returnCode = methodReferenceReturnCode(t, ref); + tailCall = isReferenceTailCall(t, code, ip, context->method, ref); + } + + unsigned rSize = resultSize(t, returnCode); + + ir::Value* result = c->stackCall( + c->nativeCall(c->constant(getThunk(t, thunk), ir::Type::iptr()), + 0, + frame->trace(0, 0), + ir::Type::iptr(), + args(c->threadRegister(), + frame->append(argument), + c->peek(1, parameterFootprint - 1))), + tailCall ? Compiler::TailJump : 0, + frame->trace(0, 0), + operandTypeForFieldCode(t, returnCode), + frame->peekMethodArguments(parameterFootprint)); + + frame->popFootprint(parameterFootprint); + + if (rSize) { + frame->pushReturnValue(returnCode, result); + } + } break; + + case invokespecial: { + context->leaf = false; + + uint16_t index = codeReadInt16(t, code, ip); + + object reference + = singletonObject(t, context->method->code()->pool(), index - 1); + + PROTECT(t, reference); + + GcMethod* target = resolveMethod(t, context->method, index - 1, false); + + if (LIKELY(target)) { + GcClass* class_ = context->method->class_(); + if (isSpecialMethod(t, target, class_)) { + target = findVirtualMethod(t, target, class_->super()); + } + + checkMethod(t, target, false); + + bool tailCall = isTailCall(t, code, ip, context->method, target); + + if (UNLIKELY(methodAbstract(t, target))) { + compileDirectAbstractInvoke( + t, frame, getMethodAddressThunk, target, tailCall); + } else { + compileDirectInvoke(t, frame, target, tailCall); + } + } else { + GcReference* ref = cast(t, reference); + PROTECT(t, ref); + compileDirectReferenceInvoke( + t, + frame, + findSpecialMethodFromReferenceThunk, + ref, + false, + isReferenceTailCall(t, code, ip, context->method, ref)); + } + } break; + + case invokestatic: { + context->leaf = false; + + uint16_t index = codeReadInt16(t, code, ip); + + object reference + = singletonObject(t, context->method->code()->pool(), index - 1); + + PROTECT(t, reference); + + GcMethod* target = resolveMethod(t, context->method, index - 1, false); + + if (LIKELY(target)) { + checkMethod(t, target, true); + + if (not intrinsic(t, frame, target)) { + bool tailCall = isTailCall(t, code, ip, context->method, target); + compileDirectInvoke(t, frame, target, tailCall); + } + } else { + GcReference* ref = cast(t, reference); + PROTECT(t, ref); + compileDirectReferenceInvoke( + t, + frame, + findStaticMethodFromReferenceThunk, + ref, + true, + isReferenceTailCall(t, code, ip, context->method, ref)); + } + } break; + + case invokevirtual: { + context->leaf = false; + + uint16_t index = codeReadInt16(t, code, ip); + + object reference + = singletonObject(t, context->method->code()->pool(), index - 1); + + PROTECT(t, reference); + + GcMethod* target = resolveMethod(t, context->method, index - 1, false); + + if (LIKELY(target)) { + checkMethod(t, target, false); + + if (not intrinsic(t, frame, target)) { + bool tailCall = isTailCall(t, code, ip, context->method, target); + + if (LIKELY(methodVirtual(t, target))) { + unsigned parameterFootprint = target->parameterFootprint(); + + unsigned offset = TargetClassVtable + + (target->offset() * TargetBytesPerWord); + + ir::Value* instance = c->peek(1, parameterFootprint - 1); + + frame->stackCall( + c->memory(c->binaryOp( + lir::And, + ir::Type::iptr(), + c->constant(TargetPointerMask, ir::Type::iptr()), + c->memory(instance, ir::Type::object())), + ir::Type::object(), + offset), + target, + tailCall ? Compiler::TailJump : 0, + frame->trace(0, 0)); + } else { + // OpenJDK generates invokevirtual calls to private methods + // (e.g. readObject and writeObject for serialization), so + // we must handle such cases here. + + compileDirectInvoke(t, frame, target, tailCall); + } + } + } else { + GcReference* ref = cast(t, reference); + PROTECT(t, reference); + PROTECT(t, ref); + + GcPair* pair = makePair(t, context->method, reference); + + compileReferenceInvoke( + frame, + c->nativeCall( + c->constant(getThunk(t, findVirtualMethodFromReferenceThunk), + ir::Type::iptr()), + 0, + frame->trace(0, 0), + ir::Type::iptr(), + args(c->threadRegister(), + frame->append(pair), + c->peek(1, + methodReferenceParameterFootprint(t, ref, false) + - 1))), + ref, + false, + isReferenceTailCall(t, code, ip, context->method, ref)); + } + } break; + + case irem: { + ir::Value* a = frame->pop(ir::Type::i4()); + ir::Value* b = frame->pop(ir::Type::i4()); + + if (inTryBlock(t, code, ip - 1)) { + c->saveLocals(); + frame->trace(0, 0); + } + + frame->push(ir::Type::i4(), + c->binaryOp(lir::Remainder, ir::Type::i4(), a, b)); + } break; + + case ireturn: { + handleExit(t, frame); + c->return_(frame->pop(ir::Type::i4())); + } + goto next; + + case freturn: { + handleExit(t, frame); + c->return_(frame->pop(ir::Type::f4())); + } + goto next; + + case istore: + frame->store(ir::Type::i4(), code->body()[ip++]); + break; + case fstore: + frame->store(ir::Type::f4(), code->body()[ip++]); + break; + + case istore_0: + frame->store(ir::Type::i4(), 0); + break; + case fstore_0: + frame->store(ir::Type::f4(), 0); + break; + + case istore_1: + frame->store(ir::Type::i4(), 1); + break; + case fstore_1: + frame->store(ir::Type::f4(), 1); + break; + + case istore_2: + frame->store(ir::Type::i4(), 2); + break; + case fstore_2: + frame->store(ir::Type::f4(), 2); + break; + + case istore_3: + frame->store(ir::Type::i4(), 3); + break; + case fstore_3: + frame->store(ir::Type::f4(), 3); + break; + + case jsr: + case jsr_w: { + uint32_t thisIp; + + if (instruction == jsr) { + uint32_t offset = codeReadInt16(t, code, ip); + thisIp = ip - 3; + newIp = thisIp + offset; + } else { + uint32_t offset = codeReadInt32(t, code, ip); + thisIp = ip - 5; + newIp = thisIp + offset; + } + + assertT(t, newIp < code->length()); + + frame->startSubroutine(newIp, ip); + + c->jmp(frame->machineIpValue(newIp)); + + ip = newIp; + } break; + + case l2d: { + frame->pushLarge(ir::Type::f8(), + c->i2f(ir::Type::f8(), frame->popLarge(ir::Type::i8()))); + } break; + + case l2f: { + frame->push(ir::Type::f4(), + c->i2f(ir::Type::f4(), frame->popLarge(ir::Type::i8()))); + } break; + + case l2i: + frame->push(ir::Type::i4(), + c->truncate(ir::Type::i4(), frame->popLarge(ir::Type::i8()))); + break; + + case ladd: + case land: + case lor: + case lsub: + case lxor: + case lmul: { + ir::Value* a = frame->popLarge(ir::Type::i8()); + ir::Value* b = frame->popLarge(ir::Type::i8()); + frame->pushLarge( + ir::Type::i8(), + c->binaryOp( + toCompilerBinaryOp(t, instruction), ir::Type::i8(), a, b)); + } break; + + case lcmp: { + ir::Value* a = frame->popLarge(ir::Type::i8()); + ir::Value* b = frame->popLarge(ir::Type::i8()); + + if (integerBranch(t, frame, code, ip, a, b, &newIp)) { + goto branch; + } else { + frame->push(ir::Type::i4(), + c->nativeCall(c->constant(getThunk(t, compareLongsThunk), + ir::Type::iptr()), + 0, + 0, + ir::Type::i4(), + args(nullptr, a, nullptr, b))); + } + } break; + + case lconst_0: + frame->pushLarge(ir::Type::i8(), c->constant(0, ir::Type::i8())); + break; + + case lconst_1: + frame->pushLarge(ir::Type::i8(), c->constant(1, ir::Type::i8())); + break; + + case ldc: + case ldc_w: { + uint16_t index; + + if (instruction == ldc) { + index = code->body()[ip++]; + } else { + index = codeReadInt16(t, code, ip); + } + + GcSingleton* pool = code->pool(); + + if (singletonIsObject(t, pool, index - 1)) { + object v = singletonObject(t, pool, index - 1); + + loadMemoryBarrier(); + + if (objectClass(t, v) == type(t, GcReference::Type)) { + GcReference* reference = cast(t, v); + PROTECT(t, reference); + + v = resolveClassInPool(t, context->method, index - 1, false); + + if (UNLIKELY(v == 0)) { + frame->push( + ir::Type::object(), + c->nativeCall( + c->constant(getThunk(t, getJClassFromReferenceThunk), + ir::Type::iptr()), + 0, + frame->trace(0, 0), + ir::Type::object(), + args(c->threadRegister(), + frame->append( + makePair(t, context->method, reference))))); + } + } + + if (v) { + if (objectClass(t, v) == type(t, GcClass::Type)) { + frame->push( + ir::Type::object(), + c->nativeCall(c->constant(getThunk(t, getJClass64Thunk), + ir::Type::iptr()), + 0, + frame->trace(0, 0), + ir::Type::object(), + args(c->threadRegister(), frame->append(v)))); + } else { + frame->push(ir::Type::object(), frame->append(v)); + } + } + } else { + ir::Type type = singletonBit(t, pool, poolSize(t, pool), index - 1) + ? ir::Type::f4() + : ir::Type::i4(); + frame->push(type, + c->constant(singletonValue(t, pool, index - 1), type)); + } + } break; + + case ldc2_w: { + uint16_t index = codeReadInt16(t, code, ip); + + GcSingleton* pool = code->pool(); + + uint64_t v; + memcpy(&v, &singletonValue(t, pool, index - 1), 8); + ir::Type type = singletonBit(t, pool, poolSize(t, pool), index - 1) + ? ir::Type::f8() + : ir::Type::i8(); + frame->pushLarge(type, c->constant(v, type)); + } break; + + case ldiv_: { + ir::Value* a = frame->popLarge(ir::Type::i8()); + ir::Value* b = frame->popLarge(ir::Type::i8()); + + if (inTryBlock(t, code, ip - 1)) { + c->saveLocals(); + frame->trace(0, 0); + } + + frame->pushLarge(ir::Type::i8(), + c->binaryOp(lir::Divide, ir::Type::i8(), a, b)); + } break; + + case lload: + frame->loadLarge(ir::Type::i8(), code->body()[ip++]); + break; + case dload: + frame->loadLarge(ir::Type::f8(), code->body()[ip++]); + break; + + case lload_0: + frame->loadLarge(ir::Type::i8(), 0); + break; + case dload_0: + frame->loadLarge(ir::Type::f8(), 0); + break; + + case lload_1: + frame->loadLarge(ir::Type::i8(), 1); + break; + case dload_1: + frame->loadLarge(ir::Type::f8(), 1); + break; + + case lload_2: + frame->loadLarge(ir::Type::i8(), 2); + break; + case dload_2: + frame->loadLarge(ir::Type::f8(), 2); + break; + + case lload_3: + frame->loadLarge(ir::Type::i8(), 3); + break; + case dload_3: + frame->loadLarge(ir::Type::f8(), 3); + break; + + case lneg: + frame->pushLarge( + ir::Type::i8(), + c->unaryOp(lir::Negate, frame->popLarge(ir::Type::i8()))); + break; + + case lookupswitch: { + int32_t base = ip - 1; + + ip = (ip + 3) & ~3; // pad to four byte boundary + + ir::Value* key = frame->pop(ir::Type::i4()); + + uint32_t defaultIp = base + codeReadInt32(t, code, ip); + assertT(t, defaultIp < code->length()); + + int32_t pairCount = codeReadInt32(t, code, ip); + + if (pairCount) { + ir::Value* default_ = frame->addressOperand( + frame->addressPromise(frame->machineIp(defaultIp))); + + avian::codegen::Promise* start = 0; + uint32_t* ipTable + = static_cast(stack.push(sizeof(uint32_t) * pairCount)); + for (int32_t i = 0; i < pairCount; ++i) { + unsigned index = ip + (i * 8); + int32_t key = codeReadInt32(t, code, index); + uint32_t newIp = base + codeReadInt32(t, code, index); + assertT(t, newIp < code->length()); + + ipTable[i] = newIp; + + avian::codegen::Promise* p = c->poolAppend(key); + if (i == 0) { + start = p; + } + c->poolAppendPromise(frame->addressPromise(frame->machineIp(newIp))); + } + assertT(t, start); + + ir::Value* address = c->nativeCall( + c->constant(getThunk(t, lookUpAddressThunk), ir::Type::iptr()), + 0, + 0, + ir::Type::iptr(), + args(key, + frame->absoluteAddressOperand(start), + c->constant(pairCount, ir::Type::i4()), + default_)); + + c->jmp(context->bootContext + ? c->binaryOp(lir::Add, + ir::Type::iptr(), + c->memory(c->threadRegister(), + ir::Type::iptr(), + TARGET_THREAD_CODEIMAGE), + address) + : address); + + new (stack.push(sizeof(SwitchState))) + SwitchState(c->saveState(), pairCount, defaultIp, 0, 0, 0, 0); + + goto switchloop; + } else { + // a switch statement with no cases, apparently + c->jmp(frame->machineIpValue(defaultIp)); + ip = defaultIp; + } + } break; + + case lrem: { + ir::Value* a = frame->popLarge(ir::Type::i8()); + ir::Value* b = frame->popLarge(ir::Type::i8()); + + if (inTryBlock(t, code, ip - 1)) { + c->saveLocals(); + frame->trace(0, 0); + } + + frame->pushLarge(ir::Type::i8(), + c->binaryOp(lir::Remainder, ir::Type::i8(), a, b)); + } break; + + case lreturn: { + handleExit(t, frame); + c->return_(frame->popLarge(ir::Type::i8())); + } + goto next; + + case dreturn: { + handleExit(t, frame); + c->return_(frame->popLarge(ir::Type::f8())); + } + goto next; + + case lshl: + case lshr: + case lushr: { + ir::Value* a = frame->pop(ir::Type::i4()); + ir::Value* b = frame->popLarge(ir::Type::i8()); + frame->pushLarge( + ir::Type::i8(), + c->binaryOp( + toCompilerBinaryOp(t, instruction), ir::Type::i8(), a, b)); + } break; + + case lstore: + frame->storeLarge(ir::Type::i8(), code->body()[ip++]); + break; + case dstore: + frame->storeLarge(ir::Type::f8(), code->body()[ip++]); + break; + + case lstore_0: + frame->storeLarge(ir::Type::i8(), 0); + break; + case dstore_0: + frame->storeLarge(ir::Type::f8(), 0); + break; + + case lstore_1: + frame->storeLarge(ir::Type::i8(), 1); + break; + case dstore_1: + frame->storeLarge(ir::Type::f8(), 1); + break; + + case lstore_2: + frame->storeLarge(ir::Type::i8(), 2); + break; + case dstore_2: + frame->storeLarge(ir::Type::f8(), 2); + break; + + case lstore_3: + frame->storeLarge(ir::Type::i8(), 3); + break; + case dstore_3: + frame->storeLarge(ir::Type::f8(), 3); + break; + + case monitorenter: { + ir::Value* target = frame->pop(ir::Type::object()); + c->nativeCall(c->constant(getThunk(t, acquireMonitorForObjectThunk), + ir::Type::iptr()), + 0, + frame->trace(0, 0), + ir::Type::void_(), + args(c->threadRegister(), target)); + } break; + + case monitorexit: { + ir::Value* target = frame->pop(ir::Type::object()); + c->nativeCall(c->constant(getThunk(t, releaseMonitorForObjectThunk), + ir::Type::iptr()), + 0, + frame->trace(0, 0), + ir::Type::void_(), + args(c->threadRegister(), target)); + } break; + + case multianewarray: { + uint16_t index = codeReadInt16(t, code, ip); + uint8_t dimensions = code->body()[ip++]; + + object reference + = singletonObject(t, context->method->code()->pool(), index - 1); + + PROTECT(t, reference); + + GcClass* class_ + = resolveClassInPool(t, context->method, index - 1, false); + + object argument; + Thunk thunk; + if (LIKELY(class_)) { + argument = class_; + thunk = makeMultidimensionalArrayThunk; + } else { + argument = makePair(t, context->method, reference); + thunk = makeMultidimensionalArrayFromReferenceThunk; + } + + unsigned offset + = localOffset(t, + localSize(t, context->method) + c->topOfStack(), + context->method) + t->arch->frameReturnAddressSize(); + + ir::Value* result + = c->nativeCall(c->constant(getThunk(t, thunk), ir::Type::iptr()), + 0, + frame->trace(0, 0), + ir::Type::object(), + args(c->threadRegister(), + frame->append(argument), + c->constant(dimensions, ir::Type::i4()), + c->constant(offset, ir::Type::i4()))); + + frame->popFootprint(dimensions); + frame->push(ir::Type::object(), result); + } break; + + case new_: { + uint16_t index = codeReadInt16(t, code, ip); + + object reference + = singletonObject(t, context->method->code()->pool(), index - 1); + + PROTECT(t, reference); + + GcClass* class_ + = resolveClassInPool(t, context->method, index - 1, false); + + object argument; + Thunk thunk; + if (LIKELY(class_)) { + argument = class_; + if (class_->vmFlags() & (WeakReferenceFlag | HasFinalizerFlag)) { + thunk = makeNewGeneral64Thunk; + } else { + thunk = makeNew64Thunk; + } + } else { + argument = makePair(t, context->method, reference); + thunk = makeNewFromReferenceThunk; + } + + frame->push( + ir::Type::object(), + c->nativeCall(c->constant(getThunk(t, thunk), ir::Type::iptr()), + 0, + frame->trace(0, 0), + ir::Type::object(), + args(c->threadRegister(), frame->append(argument)))); + } break; + + case newarray: { + uint8_t type = code->body()[ip++]; + + ir::Value* length = frame->pop(ir::Type::i4()); + + frame->push(ir::Type::object(), + c->nativeCall(c->constant(getThunk(t, makeBlankArrayThunk), + ir::Type::iptr()), + 0, + frame->trace(0, 0), + ir::Type::object(), + args(c->threadRegister(), + c->constant(type, ir::Type::i4()), + length))); + } break; + + case nop: + break; + + case pop_: + frame->popFootprint(1); + break; + + case pop2: + frame->popFootprint(2); + break; + + case putfield: + case putstatic: { + uint16_t index = codeReadInt16(t, code, ip); + + object reference + = singletonObject(t, context->method->code()->pool(), index - 1); + + PROTECT(t, reference); + + GcField* field = resolveField(t, context->method, index - 1, false); + + if (LIKELY(field)) { + int fieldCode = field->code(); + + object staticTable = 0; + + if (instruction == putstatic) { + checkField(t, field, true); + + if (classNeedsInit(t, field->class_())) { + PROTECT(t, field); + + c->nativeCall( + c->constant(getThunk(t, tryInitClassThunk), ir::Type::iptr()), + 0, + frame->trace(0, 0), + ir::Type::void_(), + args(c->threadRegister(), frame->append(field->class_()))); + } + + staticTable = field->class_()->staticTable(); + } else { + checkField(t, field, false); + + if (inTryBlock(t, code, ip - 3)) { + c->saveLocals(); + frame->trace(0, 0); + } + } + + if (field->flags() & ACC_VOLATILE) { + if (TargetBytesPerWord == 4 + and (fieldCode == DoubleField or fieldCode == LongField)) { + PROTECT(t, field); + + c->nativeCall(c->constant(getThunk(t, acquireMonitorForObjectThunk), + ir::Type::iptr()), + 0, + frame->trace(0, 0), + ir::Type::void_(), + args(c->threadRegister(), frame->append(field))); + } else { + c->nullaryOp(lir::StoreStoreBarrier); + } + } + + ir::Value* value = popField(t, frame, fieldCode); + + ir::Value* table; + + if (instruction == putstatic) { + PROTECT(t, field); + + table = frame->append(staticTable); + } else { + table = frame->pop(ir::Type::object()); + } + + switch (fieldCode) { + case ByteField: + case BooleanField: + c->store( + value, + c->memory( + table, ir::Type::i1(), targetFieldOffset(context, field))); + break; + + case CharField: + case ShortField: + c->store( + value, + c->memory( + table, ir::Type::i2(), targetFieldOffset(context, field))); + break; + + case FloatField: + c->store( + value, + c->memory( + table, ir::Type::f4(), targetFieldOffset(context, field))); + break; + + case IntField: + c->store( + value, + c->memory( + table, ir::Type::i4(), targetFieldOffset(context, field))); + break; + + case DoubleField: + c->store( + value, + c->memory( + table, ir::Type::f8(), targetFieldOffset(context, field))); + break; + + case LongField: + c->store( + value, + c->memory( + table, ir::Type::i8(), targetFieldOffset(context, field))); + break; + + case ObjectField: + if (instruction == putfield) { + c->nativeCall( + c->constant(getThunk(t, setMaybeNullThunk), ir::Type::iptr()), + 0, + frame->trace(0, 0), + ir::Type::void_(), + args(c->threadRegister(), + table, + c->constant(targetFieldOffset(context, field), + ir::Type::i4()), + value)); + } else { + c->nativeCall( + c->constant(getThunk(t, setObjectThunk), ir::Type::iptr()), + 0, + 0, + ir::Type::void_(), + args(c->threadRegister(), + table, + c->constant(targetFieldOffset(context, field), + ir::Type::i4()), + value)); + } + break; + + default: + abort(t); + } + + if (field->flags() & ACC_VOLATILE) { + if (TargetBytesPerWord == 4 + and (fieldCode == DoubleField or fieldCode == LongField)) { + c->nativeCall(c->constant(getThunk(t, releaseMonitorForObjectThunk), + ir::Type::iptr()), + 0, + frame->trace(0, 0), + ir::Type::void_(), + args(c->threadRegister(), frame->append(field))); + } else { + c->nullaryOp(lir::StoreLoadBarrier); + } + } + } else { + GcReference* ref = cast(t, reference); + PROTECT(t, ref); + int fieldCode = vm::fieldCode(t, ref->spec()->body()[0]); + + ir::Value* value = popField(t, frame, fieldCode); + ir::Type rType = operandTypeForFieldCode(t, fieldCode); + + GcPair* pair = makePair(t, context->method, reference); + + switch (fieldCode) { + case ByteField: + case BooleanField: + case CharField: + case ShortField: + case FloatField: + case IntField: { + if (instruction == putstatic) { + c->nativeCall( + c->constant(getThunk(t, setStaticFieldValueFromReferenceThunk), + ir::Type::iptr()), + 0, + frame->trace(0, 0), + rType, + args(c->threadRegister(), frame->append(pair), value)); + } else { + ir::Value* instance = frame->pop(ir::Type::object()); + + c->nativeCall( + c->constant(getThunk(t, setFieldValueFromReferenceThunk), + ir::Type::iptr()), + 0, + frame->trace(0, 0), + rType, + args( + c->threadRegister(), frame->append(pair), instance, value)); + } + } break; + + case DoubleField: + case LongField: { + if (instruction == putstatic) { + c->nativeCall( + c->constant( + getThunk(t, setStaticLongFieldValueFromReferenceThunk), + ir::Type::iptr()), + 0, + frame->trace(0, 0), + rType, + args(c->threadRegister(), frame->append(pair), nullptr, value)); + } else { + ir::Value* instance = frame->pop(ir::Type::object()); + + c->nativeCall( + c->constant(getThunk(t, setLongFieldValueFromReferenceThunk), + ir::Type::iptr()), + 0, + frame->trace(0, 0), + rType, + args(c->threadRegister(), + frame->append(pair), + instance, + nullptr, + value)); + } + } break; + + case ObjectField: { + if (instruction == putstatic) { + c->nativeCall( + c->constant( + getThunk(t, setStaticObjectFieldValueFromReferenceThunk), + ir::Type::iptr()), + 0, + frame->trace(0, 0), + rType, + args(c->threadRegister(), frame->append(pair), value)); + } else { + ir::Value* instance = frame->pop(ir::Type::object()); + + c->nativeCall( + c->constant(getThunk(t, setObjectFieldValueFromReferenceThunk), + ir::Type::iptr()), + 0, + frame->trace(0, 0), + rType, + args( + c->threadRegister(), frame->append(pair), instance, value)); + } + } break; + + default: + abort(t); + } + } + } break; + + case ret: { + unsigned index = code->body()[ip]; + + unsigned returnAddress = frame->endSubroutine(index); + c->jmp(frame->machineIpValue(returnAddress)); + ip = returnAddress; + } break; + + case return_: + if (needsReturnBarrier(t, context->method)) { + c->nullaryOp(lir::StoreStoreBarrier); + } + + handleExit(t, frame); + c->return_(); + goto next; + + case sipush: + frame->push(ir::Type::i4(), + c->constant(static_cast(codeReadInt16(t, code, ip)), + ir::Type::i4())); + break; + + case swap: + frame->swap(); + break; + + case tableswitch: { + int32_t base = ip - 1; + + ip = (ip + 3) & ~3; // pad to four byte boundary + + uint32_t defaultIp = base + codeReadInt32(t, code, ip); + assertT(t, defaultIp < code->length()); + + int32_t bottom = codeReadInt32(t, code, ip); + int32_t top = codeReadInt32(t, code, ip); + + avian::codegen::Promise* start = 0; + unsigned count = top - bottom + 1; + uint32_t* ipTable + = static_cast(stack.push(sizeof(uint32_t) * count)); + for (int32_t i = 0; i < top - bottom + 1; ++i) { + unsigned index = ip + (i * 4); + uint32_t newIp = base + codeReadInt32(t, code, index); + assertT(t, newIp < code->length()); + + ipTable[i] = newIp; + + avian::codegen::Promise* p = c->poolAppendPromise( + frame->addressPromise(frame->machineIp(newIp))); + if (i == 0) { + start = p; + } + } + assertT(t, start); + + ir::Value* key = frame->pop(ir::Type::i4()); + + c->condJump(lir::JumpIfLess, + c->constant(bottom, ir::Type::i4()), + key, + frame->machineIpValue(defaultIp)); + + c->save(ir::Type::i4(), key); + + new (stack.push(sizeof(SwitchState))) SwitchState( + c->saveState(), count, defaultIp, key, start, bottom, top); + + stack.pushValue(Untable0); + ip = defaultIp; + } + goto start; + + case wide: { + switch (code->body()[ip++]) { + case aload: { + frame->load(ir::Type::object(), codeReadInt16(t, code, ip)); + } break; + + case astore: { + frame->store(ir::Type::object(), codeReadInt16(t, code, ip)); + } break; + + case iinc: { + uint16_t index = codeReadInt16(t, code, ip); + int16_t count = codeReadInt16(t, code, ip); + + storeLocal(context, + 1, + ir::Type::i4(), + c->binaryOp(lir::Add, + ir::Type::i4(), + c->constant(count, ir::Type::i4()), + loadLocal(context, 1, ir::Type::i4(), index)), + index); + } break; + + case iload: { + frame->load(ir::Type::i4(), codeReadInt16(t, code, ip)); + } break; + + case istore: { + frame->store(ir::Type::i4(), codeReadInt16(t, code, ip)); + } break; + + case lload: { + frame->loadLarge(ir::Type::i8(), codeReadInt16(t, code, ip)); + } break; + + case lstore: { + frame->storeLarge(ir::Type::i8(), codeReadInt16(t, code, ip)); + } break; + + case ret: { + unsigned index = codeReadInt16(t, code, ip); + + unsigned returnAddress = frame->endSubroutine(index); + c->jmp(frame->machineIpValue(returnAddress)); + ip = returnAddress; + } break; + + default: + abort(t); + } + } break; + + default: + abort(t); + } + } + +next: + frame->dispose(); + frame = 0; + stack.pop(sizeof(Frame)); + stack.pop(stackSize * sizeof(ir::Type)); + switch (stack.popValue()) { + case Return: + return; + + case Unbranch: + if (DebugInstructions) { + fprintf(stderr, "Unbranch\n"); + } + ip = stack.popValue(); + c->restoreState(reinterpret_cast(stack.popValue())); + frame = static_cast(stack.peek(sizeof(Frame))); + goto loop; + + case Untable0: { + if (DebugInstructions) { + fprintf(stderr, "Untable0\n"); + } + SwitchState* s = static_cast(stack.peek(sizeof(SwitchState))); + + frame = s->frame(); + + c->restoreState(s->state); + + c->condJump(lir::JumpIfGreater, + c->constant(s->top, ir::Type::i4()), + s->key, + frame->machineIpValue(s->defaultIp)); + + c->save(ir::Type::i4(), s->key); + ip = s->defaultIp; + stack.pushValue(Untable1); + } + goto start; + + case Untable1: { + if (DebugInstructions) { + fprintf(stderr, "Untable1\n"); + } + SwitchState* s = static_cast(stack.peek(sizeof(SwitchState))); + + frame = s->frame(); + + c->restoreState(s->state); + + ir::Value* normalizedKey + = (s->bottom + ? c->binaryOp(lir::Subtract, + ir::Type::i4(), + c->constant(s->bottom, ir::Type::i4()), + s->key) + : s->key); + + ir::Value* entry = c->memory(frame->absoluteAddressOperand(s->start), + ir::Type::iptr(), + 0, + normalizedKey); + + c->jmp(c->load(ir::ExtendMode::Signed, + context->bootContext + ? c->binaryOp(lir::Add, + ir::Type::iptr(), + c->memory(c->threadRegister(), + ir::Type::iptr(), + TARGET_THREAD_CODEIMAGE), + entry) + : entry, + ir::Type::iptr())); + + s->state = c->saveState(); + } + goto switchloop; + + case Unswitch: { + if (DebugInstructions) { + fprintf(stderr, "Unswitch\n"); + } + SwitchState* s = static_cast(stack.peek(sizeof(SwitchState))); + + frame = s->frame(); + + c->restoreState( + static_cast(stack.peek(sizeof(SwitchState)))->state); + } + goto switchloop; + + case Unsubroutine: { + if (DebugInstructions) { + fprintf(stderr, "Unsubroutine\n"); + } + ip = stack.popValue(); + unsigned start = stack.popValue(); + frame = reinterpret_cast(stack.peek(sizeof(Frame))); + frame->endSubroutine(start); + } + goto loop; + + default: + abort(t); + } + +switchloop : { + SwitchState* s = static_cast(stack.peek(sizeof(SwitchState))); + + if (s->index < s->count) { + ip = s->ipTable()[s->index++]; + stack.pushValue(Unswitch); + goto start; + } else { + ip = s->defaultIp; + unsigned count = s->count * 4; + stack.pop(sizeof(SwitchState)); + stack.pop(count); + frame = reinterpret_cast(stack.peek(sizeof(Frame))); + goto loop; + } +} + +branch: + stack.pushValue(reinterpret_cast(c->saveState())); + stack.pushValue(ip); + stack.pushValue(Unbranch); + ip = newIp; + goto start; +} + +int resolveIpForwards(Context* context, int start, int end) +{ + if (start < 0) { + start = 0; + } + + while (start < end and context->visitTable[start] == 0) { + ++start; + } + + if (start >= end) { + return -1; + } else { + return start; + } +} + +int resolveIpBackwards(Context* context, int start, int end) +{ + if (start >= static_cast(context->method->code()->length() + * (context->subroutineCount + 1))) { + start = context->method->code()->length(); + } else { + while (start >= end and context->visitTable[start] == 0) { + --start; + } + } + + if (start < end) { + return -1; + } else { + return start; + } +} + +GcIntArray* truncateIntArray(Thread* t, GcIntArray* array, unsigned length) +{ + expect(t, array->length() > length); + + PROTECT(t, array); + + GcIntArray* newArray = makeIntArray(t, length); + if (length) { + memcpy(newArray->body().begin(), array->body().begin(), length * 4); + } + + return newArray; +} + +GcArray* truncateArray(Thread* t, GcArray* array, unsigned length) +{ + expect(t, array->length() > length); + + PROTECT(t, array); + + GcArray* newArray = makeArray(t, length); + if (length) { + for (size_t i = 0; i < length; i++) { + newArray->setBodyElement(t, i, array->body()[i]); + } + } + + return newArray; +} + +GcLineNumberTable* truncateLineNumberTable(Thread* t, + GcLineNumberTable* table, + unsigned length) +{ + expect(t, table->length() > length); + + PROTECT(t, table); + + GcLineNumberTable* newTable = makeLineNumberTable(t, length); + if (length) { + memcpy(newTable->body().begin(), + table->body().begin(), + length * sizeof(uint64_t)); + } + + return newTable; +} + +GcArray* translateExceptionHandlerTable(MyThread* t, + Context* context, + intptr_t start, + intptr_t end) +{ + avian::codegen::Compiler* c = context->compiler; + + GcExceptionHandlerTable* oldTable = cast( + t, context->method->code()->exceptionHandlerTable()); + + if (oldTable) { + PROTECT(t, oldTable); + + unsigned length = oldTable->length(); + + GcIntArray* newIndex + = makeIntArray(t, length * (context->subroutineCount + 1) * 3); + PROTECT(t, newIndex); + + GcArray* newTable + = makeArray(t, length * (context->subroutineCount + 1) + 1); + PROTECT(t, newTable); + + unsigned ni = 0; + for (unsigned subI = 0; subI <= context->subroutineCount; ++subI) { + unsigned duplicatedBaseIp = subI * context->method->code()->length(); + + for (unsigned oi = 0; oi < length; ++oi) { + uint64_t oldHandler = oldTable->body()[oi]; + + int handlerStart = resolveIpForwards( + context, + duplicatedBaseIp + exceptionHandlerStart(oldHandler), + duplicatedBaseIp + exceptionHandlerEnd(oldHandler)); + + if (LIKELY(handlerStart >= 0)) { + assertT(t, + handlerStart + < static_cast(context->method->code()->length() + * (context->subroutineCount + 1))); + + int handlerEnd = resolveIpBackwards( + context, + duplicatedBaseIp + exceptionHandlerEnd(oldHandler), + duplicatedBaseIp + exceptionHandlerStart(oldHandler)); + + assertT(t, handlerEnd >= 0); + assertT( + t, + handlerEnd <= static_cast(context->method->code()->length() + * (context->subroutineCount + 1))); + + newIndex->body()[ni * 3] = c->machineIp(handlerStart)->value() + - start; + + newIndex->body()[(ni * 3) + 1] + = (handlerEnd + == static_cast(context->method->code()->length()) + ? end + : c->machineIp(handlerEnd)->value()) - start; + + newIndex->body()[(ni * 3) + 2] + = c->machineIp(exceptionHandlerIp(oldHandler))->value() - start; + + object type; + if (exceptionHandlerCatchType(oldHandler)) { + type = resolveClassInPool( + t, context->method, exceptionHandlerCatchType(oldHandler) - 1); + } else { + type = 0; + } + + newTable->setBodyElement(t, ni + 1, type); + + ++ni; + } + } + } + + if (UNLIKELY(ni < length)) { + newIndex = truncateIntArray(t, newIndex, ni * 3); + newTable = truncateArray(t, newTable, ni + 1); + } + + newTable->setBodyElement(t, 0, newIndex); + + return newTable; + } else { + return 0; + } +} + +GcLineNumberTable* translateLineNumberTable(MyThread* t, + Context* context, + intptr_t start) +{ + GcLineNumberTable* oldTable = context->method->code()->lineNumberTable(); + if (oldTable) { + PROTECT(t, oldTable); + + unsigned length = oldTable->length(); + GcLineNumberTable* newTable = makeLineNumberTable(t, length); + unsigned ni = 0; + for (unsigned oi = 0; oi < length; ++oi) { + uint64_t oldLine = oldTable->body()[oi]; + + int ip = resolveIpForwards( + context, + lineNumberIp(oldLine), + oi + 1 < length ? lineNumberIp(oldTable->body()[oi + 1]) - 1 + : lineNumberIp(oldLine) + 1); + + if (LIKELY(ip >= 0)) { + newTable->body()[ni++] + = lineNumber(context->compiler->machineIp(ip)->value() - start, + lineNumberLine(oldLine)); + } + } + + if (UNLIKELY(ni < length)) { + newTable = truncateLineNumberTable(t, newTable, ni); + } + + return newTable; + } else { + return 0; + } +} + +void printSet(uintptr_t* m, unsigned limit) +{ + if (limit) { + for (unsigned i = 0; i < 32; ++i) { + if ((*m >> i) & 1) { + fprintf(stderr, "1"); + } else { + fprintf(stderr, "_"); + } + } + } +} + +void calculateTryCatchRoots(Context* context, + uintptr_t* roots, + unsigned mapSize, + unsigned start, + unsigned end) +{ + memset(roots, 0xFF, mapSize * BytesPerWord); + + if (DebugFrameMaps) { + fprintf(stderr, "calculate try/catch roots from %d to %d\n", start, end); + } + + for (TraceElement* te = context->traceLog; te; te = te->next) { + if (te->ip >= start and te->ip < end) { + uintptr_t* traceRoots = 0; + traceRoots = te->map; + te->watch = true; + + if (traceRoots) { + if (DebugFrameMaps) { + fprintf(stderr, " use roots at ip %3d: ", te->ip); + printSet(traceRoots, mapSize); + fprintf(stderr, "\n"); + } + + for (unsigned wi = 0; wi < mapSize; ++wi) { + roots[wi] &= traceRoots[wi]; + } + } else { + if (DebugFrameMaps) { + fprintf(stderr, " skip roots at ip %3d\n", te->ip); + } + } + } + } + + if (DebugFrameMaps) { + fprintf(stderr, "result roots : "); + printSet(roots, mapSize); + fprintf(stderr, "\n"); + } +} + +unsigned calculateFrameMaps(MyThread* t, + Context* context, + uintptr_t* originalRoots, + unsigned eventIndex, + uintptr_t* resultRoots) +{ + // for each instruction with more than one predecessor, and for each + // stack position, determine if there exists a path to that + // instruction such that there is not an object pointer left at that + // stack position (i.e. it is uninitialized or contains primitive + // data). + + unsigned mapSize = frameMapSizeInWords(t, context->method); + + THREAD_RUNTIME_ARRAY(t, uintptr_t, roots, mapSize); + if (originalRoots) { + memcpy(RUNTIME_ARRAY_BODY(roots), originalRoots, mapSize * BytesPerWord); + } else { + memset(RUNTIME_ARRAY_BODY(roots), 0, mapSize * BytesPerWord); + } + + int32_t ip = -1; + + // invariant: for each stack position, roots contains a zero at that + // position if there exists some path to the current instruction + // such that there is definitely not an object pointer at that + // position. Otherwise, roots contains a one at that position, + // meaning either all known paths result in an object pointer at + // that position, or the contents of that position are as yet + // unknown. + + unsigned length = context->eventLog.length(); + while (eventIndex < length) { + Event e = static_cast(context->eventLog.get(eventIndex++)); + switch (e) { + case PushContextEvent: { + eventIndex = calculateFrameMaps(t, + context, + RUNTIME_ARRAY_BODY(roots), + eventIndex, /*subroutinePath,*/ + resultRoots); + } break; + + case PopContextEvent: + goto exit; + + case IpEvent: { + ip = context->eventLog.get2(eventIndex); + eventIndex += 2; + + if (DebugFrameMaps) { + fprintf(stderr, " roots at ip %3d: ", ip); + printSet(RUNTIME_ARRAY_BODY(roots), mapSize); + fprintf(stderr, "\n"); + } + + assertT(context->thread, ip * mapSize <= context->rootTable.count); + uintptr_t* tableRoots = context->rootTable.begin() + (ip * mapSize); + + if (context->visitTable[ip] > 1) { + for (unsigned wi = 0; wi < mapSize; ++wi) { + uintptr_t newRoots = tableRoots[wi] & RUNTIME_ARRAY_BODY(roots)[wi]; + + if ((eventIndex == length + or context->eventLog.get(eventIndex) == PopContextEvent) + and newRoots != tableRoots[wi]) { + if (DebugFrameMaps) { + fprintf(stderr, "dirty roots!\n"); + } + + context->dirtyRoots = true; + } + + tableRoots[wi] = newRoots; + RUNTIME_ARRAY_BODY(roots)[wi] &= tableRoots[wi]; + } + + if (DebugFrameMaps) { + fprintf(stderr, " table roots at ip %3d: ", ip); + printSet(tableRoots, mapSize); + fprintf(stderr, "\n"); + } + } else { + memcpy(tableRoots, RUNTIME_ARRAY_BODY(roots), mapSize * BytesPerWord); + } + } break; + + case MarkEvent: { + unsigned i = context->eventLog.get2(eventIndex); + eventIndex += 2; + + markBit(RUNTIME_ARRAY_BODY(roots), i); + } break; + + case ClearEvent: { + unsigned i = context->eventLog.get2(eventIndex); + eventIndex += 2; + + clearBit(RUNTIME_ARRAY_BODY(roots), i); + } break; + + case PushExceptionHandlerEvent: { + unsigned start = context->eventLog.get2(eventIndex); + eventIndex += 2; + unsigned end = context->eventLog.get2(eventIndex); + eventIndex += 2; + + calculateTryCatchRoots( + context, RUNTIME_ARRAY_BODY(roots), mapSize, start, end); + + eventIndex = calculateFrameMaps( + t, context, RUNTIME_ARRAY_BODY(roots), eventIndex, 0); + } break; + + case TraceEvent: { + TraceElement* te; + context->eventLog.get(eventIndex, &te, BytesPerWord); + if (DebugFrameMaps) { + fprintf(stderr, " trace roots at ip %3d: ", ip); + printSet(RUNTIME_ARRAY_BODY(roots), mapSize); + fprintf(stderr, "\n"); + } + + uintptr_t* map; + bool watch; + map = te->map; + watch = te->watch; + + for (unsigned wi = 0; wi < mapSize; ++wi) { + uintptr_t v = RUNTIME_ARRAY_BODY(roots)[wi]; + + if (watch and map[wi] != v) { + if (DebugFrameMaps) { + fprintf(stderr, "dirty roots due to trace watch!\n"); + } + + context->dirtyRoots = true; + } + + map[wi] = v; + } + + eventIndex += BytesPerWord; + } break; + + default: + abort(t); + } + } + +exit: + if (resultRoots and ip != -1) { + if (DebugFrameMaps) { + fprintf(stderr, "result roots at ip %3d: ", ip); + printSet(RUNTIME_ARRAY_BODY(roots), mapSize); + fprintf(stderr, "\n"); + } + + memcpy(resultRoots, RUNTIME_ARRAY_BODY(roots), mapSize * BytesPerWord); + } + + return eventIndex; +} + +int compareTraceElementPointers(const void* va, const void* vb) +{ + TraceElement* a = *static_cast(va); + TraceElement* b = *static_cast(vb); + if (a->address->value() > b->address->value()) { + return 1; + } else if (a->address->value() < b->address->value()) { + return -1; + } else { + return 0; + } +} + +uint8_t* finish(MyThread* t, + FixedAllocator* allocator, + avian::codegen::Assembler* a, + const char* name, + unsigned length) +{ + uint8_t* start + = static_cast(allocator->allocate(length, TargetBytesPerWord)); + + a->setDestination(start); + a->write(); + + logCompile(t, start, length, 0, name, 0); + + return start; +} + +void setBit(int32_t* dst, unsigned index) +{ + dst[index / 32] |= static_cast(1) << (index % 32); +} + +void clearBit(int32_t* dst, unsigned index) +{ + dst[index / 32] &= ~(static_cast(1) << (index % 32)); +} + +void copyFrameMap(int32_t* dst, + uintptr_t* src, + unsigned mapSizeInBits, + unsigned offset, + TraceElement* p) +{ + if (DebugFrameMaps) { + fprintf(stderr, " orig roots at ip %3d: ", p->ip); + printSet(src, ceilingDivide(mapSizeInBits, BitsPerWord)); + fprintf(stderr, "\n"); + + fprintf(stderr, " final roots at ip %3d: ", p->ip); + } + + for (unsigned j = 0; j < p->argumentIndex; ++j) { + if (getBit(src, j)) { + if (DebugFrameMaps) { + fprintf(stderr, "1"); + } + setBit(dst, offset + j); + } else { + if (DebugFrameMaps) { + fprintf(stderr, "_"); + } + clearBit(dst, offset + j); + } + } + + if (DebugFrameMaps) { + fprintf(stderr, "\n"); + } +} + +class FrameMapTableHeader { + public: + FrameMapTableHeader(unsigned indexCount) : indexCount(indexCount) + { + } + + unsigned indexCount; +}; + +class FrameMapTableIndexElement { + public: + FrameMapTableIndexElement(int offset, unsigned base, unsigned path) + : offset(offset), base(base), path(path) + { + } + + int offset; + unsigned base; + unsigned path; +}; + +class FrameMapTablePath { + public: + FrameMapTablePath(unsigned stackIndex, unsigned elementCount, unsigned next) + : stackIndex(stackIndex), elementCount(elementCount), next(next) + { + } + + unsigned stackIndex; + unsigned elementCount; + unsigned next; + int32_t elements[0]; +}; + +GcIntArray* makeSimpleFrameMapTable(MyThread* t, + Context* context, + uint8_t* start, + TraceElement** elements, + unsigned elementCount) +{ + unsigned mapSize = frameMapSizeInBits(t, context->method); + GcIntArray* table = makeIntArray( + t, elementCount + ceilingDivide(elementCount * mapSize, 32)); + + assertT(t, + table->length() + == elementCount + simpleFrameMapTableSize(t, context->method, table)); + + for (unsigned i = 0; i < elementCount; ++i) { + TraceElement* p = elements[i]; + + table->body()[i] = static_cast(p->address->value()) + - reinterpret_cast(start); + + assertT( + t, + elementCount + ceilingDivide((i + 1) * mapSize, 32) <= table->length()); + + if (mapSize) { + copyFrameMap( + &table->body()[elementCount], p->map, mapSize, i * mapSize, p); + } + } + + return table; +} + +void insertCallNode(MyThread* t, GcCallNode* node); + +void finish(MyThread* t, FixedAllocator* allocator, Context* context) +{ + avian::codegen::Compiler* c = context->compiler; + + if (false) { + logCompile( + t, + 0, + 0, + reinterpret_cast( + context->method->class_()->name()->body().begin()), + reinterpret_cast(context->method->name()->body().begin()), + reinterpret_cast(context->method->spec()->body().begin())); + } + + // for debugging: + if (false + and ::strcmp(reinterpret_cast( + context->method->class_()->name()->body().begin()), + "java/lang/System") == 0 + and ::strcmp(reinterpret_cast( + context->method->name()->body().begin()), + "") == 0) { + trap(); + } + + // todo: this is a CPU-intensive operation, so consider doing it + // earlier before we've acquired the global class lock to improve + // parallelism (the downside being that it may end up being a waste + // of cycles if another thread compiles the same method in parallel, + // which might be mitigated by fine-grained, per-method locking): + c->compile(context->leaf ? 0 : stackOverflowThunk(t), + TARGET_THREAD_STACKLIMIT); + + // we must acquire the class lock here at the latest + + unsigned codeSize = c->resolve(allocator->memory.begin() + allocator->offset); + + unsigned total = pad(codeSize, TargetBytesPerWord) + + pad(c->poolSize(), TargetBytesPerWord); + + target_uintptr_t* code = static_cast( + allocator->allocate(total, TargetBytesPerWord)); + uint8_t* start = reinterpret_cast(code); + + context->executableAllocator = allocator; + context->executableStart = code; + context->executableSize = total; + + if (context->objectPool) { + object pool = allocate3( + t, + allocator, + Machine::ImmortalAllocation, + GcArray::FixedSize + ((context->objectPoolCount + 1) * BytesPerWord), + true); + + context->executableSize = (allocator->memory.begin() + allocator->offset) + - static_cast(context->executableStart); + + initArray( + t, reinterpret_cast(pool), context->objectPoolCount + 1); + mark(t, pool, 0); + + setField(t, pool, ArrayBody, compileRoots(t)->objectPools()); + compileRoots(t)->setObjectPools(t, pool); + + unsigned i = 1; + for (PoolElement* p = context->objectPool; p; p = p->next) { + unsigned offset = ArrayBody + ((i++) * BytesPerWord); + + p->address = reinterpret_cast(pool) + offset; + + setField(t, pool, offset, p->target); + } + } + + c->write(); + + BootContext* bc = context->bootContext; + if (bc) { + for (avian::codegen::DelayedPromise* p = bc->addresses; + p != bc->addressSentinal; + p = p->next) { + p->basis = new (bc->zone) + avian::codegen::ResolvedPromise(p->basis->value()); + } + } + + { + GcArray* newExceptionHandlerTable = translateExceptionHandlerTable( + t, + context, + reinterpret_cast(start), + reinterpret_cast(start) + codeSize); + + PROTECT(t, newExceptionHandlerTable); + + GcLineNumberTable* newLineNumberTable = translateLineNumberTable( + t, context, reinterpret_cast(start)); + + GcCode* code = context->method->code(); + + code = makeCode(t, + 0, + 0, + newExceptionHandlerTable, + newLineNumberTable, + reinterpret_cast(start), + codeSize, + code->maxStack(), + code->maxLocals(), + 0); + + context->method->setCode(t, code); + } + + if (context->traceLogCount) { + THREAD_RUNTIME_ARRAY(t, TraceElement*, elements, context->traceLogCount); + unsigned index = 0; + // unsigned pathFootprint = 0; + // unsigned mapCount = 0; + for (TraceElement* p = context->traceLog; p; p = p->next) { + assertT(t, index < context->traceLogCount); + + if (p->address) { + RUNTIME_ARRAY_BODY(elements)[index++] = p; + + if (p->target) { + insertCallNode( + t, makeCallNode(t, p->address->value(), p->target, p->flags, 0)); + } + } + } + + qsort(RUNTIME_ARRAY_BODY(elements), + index, + sizeof(TraceElement*), + compareTraceElementPointers); + + GcIntArray* map = makeSimpleFrameMapTable( + t, context, start, RUNTIME_ARRAY_BODY(elements), index); + + context->method->code()->setStackMap(t, map); + } + + logCompile( + t, + start, + codeSize, + reinterpret_cast( + context->method->class_()->name()->body().begin()), + reinterpret_cast(context->method->name()->body().begin()), + reinterpret_cast(context->method->spec()->body().begin())); + + // for debugging: + if (false + and ::strcmp(reinterpret_cast( + context->method->class_()->name()->body().begin()), + "java/lang/System") == 0 + and ::strcmp(reinterpret_cast( + context->method->name()->body().begin()), + "") == 0) { + trap(); + } + syncInstructionCache(start, codeSize); +} + +void compile(MyThread* t, Context* context) +{ + avian::codegen::Compiler* c = context->compiler; + + if (false) { + fprintf(stderr, + "compiling %s.%s%s\n", + context->method->class_()->name()->body().begin(), + context->method->name()->body().begin(), + context->method->spec()->body().begin()); + } + + unsigned footprint = context->method->parameterFootprint(); + unsigned locals = localSize(t, context->method); + c->init(context->method->code()->length(), + footprint, + locals, + alignedFrameSize(t, context->method)); + + ir::Type* stackMap = (ir::Type*)malloc(sizeof(ir::Type) + * context->method->code()->maxStack()); + Frame frame(context, stackMap); + + unsigned index = context->method->parameterFootprint(); + if ((context->method->flags() & ACC_STATIC) == 0) { + frame.set(--index, ir::Type::object()); + c->initLocal(index, ir::Type::object()); + } + + for (MethodSpecIterator it(t, + reinterpret_cast( + context->method->spec()->body().begin())); + it.hasNext();) { + switch (*it.next()) { + case 'L': + case '[': + frame.set(--index, ir::Type::object()); + c->initLocal(index, ir::Type::object()); + break; + + case 'J': + frame.set(--index, ir::Type::i8()); + frame.set(--index, ir::Type::i8()); + c->initLocal(index, ir::Type::i8()); + break; + + case 'D': + frame.set(--index, ir::Type::f8()); + frame.set(--index, ir::Type::f8()); + c->initLocal(index, ir::Type::f8()); + break; + + case 'F': + frame.set(--index, ir::Type::i4()); + c->initLocal(index, ir::Type::f4()); + break; + + default: + frame.set(--index, ir::Type::i4()); + c->initLocal(index, ir::Type::i4()); + break; + } + } + + handleEntrance(t, &frame); + + Compiler::State* state = c->saveState(); + + compile(t, &frame, 0); + + context->dirtyRoots = false; + unsigned eventIndex = calculateFrameMaps(t, context, 0, 0, 0); + + GcExceptionHandlerTable* eht = cast( + t, context->method->code()->exceptionHandlerTable()); + if (eht) { + PROTECT(t, eht); + + unsigned visitCount = eht->length(); + + THREAD_RUNTIME_ARRAY(t, bool, visited, visitCount); + memset(RUNTIME_ARRAY_BODY(visited), 0, visitCount * sizeof(bool)); + + bool progress = true; + while (progress) { + progress = false; + + for (unsigned subI = 0; subI <= context->subroutineCount; ++subI) { + unsigned duplicatedBaseIp = subI * context->method->code()->length(); + + for (unsigned i = 0; i < eht->length(); ++i) { + uint64_t eh = eht->body()[i]; + int start + = resolveIpForwards(context, + duplicatedBaseIp + exceptionHandlerStart(eh), + duplicatedBaseIp + exceptionHandlerEnd(eh)); + + if ((not RUNTIME_ARRAY_BODY(visited)[i]) and start >= 0 + and context->visitTable[start]) { + RUNTIME_ARRAY_BODY(visited)[i] = true; + progress = true; + + c->restoreState(state); + + ir::Type* stackMap2 = (ir::Type*)malloc( + sizeof(ir::Type) * context->method->code()->maxStack()); + Frame frame2(&frame, stackMap2); + + unsigned end = duplicatedBaseIp + exceptionHandlerEnd(eh); + if (exceptionHandlerIp(eh) >= static_cast(start) + and exceptionHandlerIp(eh) < end) { + end = duplicatedBaseIp + exceptionHandlerIp(eh); + } + + context->eventLog.append(PushExceptionHandlerEvent); + context->eventLog.append2(start); + context->eventLog.append2(end); + + for (unsigned i = 1; i < context->method->code()->maxStack(); ++i) { + frame2.set(localSize(t, context->method) + i, ir::Type::i4()); + } + + compile(t, &frame2, exceptionHandlerIp(eh), start); + + context->eventLog.append(PopContextEvent); + + eventIndex = calculateFrameMaps(t, context, 0, eventIndex, 0); + free(stackMap2); + } + } + } + } + } + + while (context->dirtyRoots) { + context->dirtyRoots = false; + calculateFrameMaps(t, context, 0, 0, 0); + } + free(stackMap); +} +#endif // not AVIAN_AOT_ONLY + +void updateCall(MyThread* t, + avian::codegen::lir::UnaryOperation op, + void* returnAddress, + void* target) +{ + t->arch->updateCall(op, returnAddress, target); +} + +void* compileMethod2(MyThread* t, void* ip); + +uint64_t compileMethod(MyThread* t) +{ + void* ip; + if (t->tailAddress) { + ip = t->tailAddress; + t->tailAddress = 0; + } else { + ip = getIp(t); + } + + return reinterpret_cast(compileMethod2(t, ip)); +} + +void* compileVirtualMethod2(MyThread* t, GcClass* class_, unsigned index) +{ + // If class_ has BootstrapFlag set, that means its vtable is not yet + // available. However, we must set t->trace->targetMethod to an + // appropriate method to ensure we can accurately scan the stack for + // GC roots. We find such a method by looking for a superclass with + // a vtable and using it instead: + + GcClass* c = class_; + while (c->vmFlags() & BootstrapFlag) { + c = c->super(); + } + t->trace->targetMethod + = cast(t, cast(t, c->virtualTable())->body()[index]); + + THREAD_RESOURCE0(t, static_cast(t)->trace->targetMethod = 0;); + + PROTECT(t, class_); + + GcMethod* target = resolveTarget(t, class_, index); + PROTECT(t, target); + + compile(t, codeAllocator(t), 0, target); + + void* address = reinterpret_cast(methodAddress(t, target)); + if (target->flags() & ACC_NATIVE) { + t->trace->nativeMethod = target; + } else { + class_->vtable()[target->offset()] = address; + } + return address; +} + +uint64_t compileVirtualMethod(MyThread* t) +{ + GcClass* class_ = objectClass(t, static_cast(t->virtualCallTarget)); + t->virtualCallTarget = 0; + + unsigned index = t->virtualCallIndex; + t->virtualCallIndex = 0; + + return reinterpret_cast(compileVirtualMethod2(t, class_, index)); +} + +void* linkDynamicMethod2(MyThread* t, unsigned index) +{ + GcInvocation* invocation + = cast(t, roots(t)->invocations()->body()[index]); + + GcCallSite* site = invocation->site(); + + loadMemoryBarrier(); + + if (site == 0) { + t->trace->targetMethod = invocation->template_(); + + THREAD_RESOURCE0(t, static_cast(t)->trace->targetMethod = 0;); + + PROTECT(t, invocation); + + site = resolveDynamic(t, invocation); + PROTECT(t, site); + + compile(t, codeAllocator(t), 0, site->target()->method()); + + ACQUIRE(t, t->m->classLock); + + if (invocation->site() == 0) { + void* address + = reinterpret_cast(methodAddress(t, site->target()->method())); + + if ((site->target()->method()->flags() & ACC_NATIVE) == 0) { + t->dynamicTable[index] = address; + } + } + + storeStoreMemoryBarrier(); + + invocation->setSite(t, site); + site->setInvocation(t, invocation); + } + + GcMethod* target = invocation->site()->target()->method(); + + if (target->flags() & ACC_NATIVE) { + t->trace->nativeMethod = target; + } + + return reinterpret_cast(methodAddress(t, target)); +} + +uint64_t linkDynamicMethod(MyThread* t) +{ + unsigned index = t->virtualCallIndex; + t->virtualCallIndex = 0; + + return reinterpret_cast(linkDynamicMethod2(t, index)); +} + +uint64_t invokeNativeFast(MyThread* t, GcMethod* method, void* function) +{ + FastNativeFunction f; + memcpy(&f, &function, sizeof(void*)); + return f(t, + method, + static_cast(t->stack) + t->arch->frameFooterSize() + + t->arch->frameReturnAddressSize()); +} + +uint64_t invokeNativeSlow(MyThread* t, GcMethod* method, void* function) +{ + PROTECT(t, method); + + unsigned footprint = method->parameterFootprint() + 1; + if (method->flags() & ACC_STATIC) { + ++footprint; + } + unsigned count = method->parameterCount() + 2; + + THREAD_RUNTIME_ARRAY(t, uintptr_t, args, footprint); + unsigned argOffset = 0; + THREAD_RUNTIME_ARRAY(t, uint8_t, types, count); + unsigned typeOffset = 0; + + RUNTIME_ARRAY_BODY(args)[argOffset++] = reinterpret_cast(t); + RUNTIME_ARRAY_BODY(types)[typeOffset++] = POINTER_TYPE; + + uintptr_t* sp = static_cast(t->stack) + t->arch->frameFooterSize() + + t->arch->frameReturnAddressSize(); + + GcJclass* jclass = 0; + PROTECT(t, jclass); + + if (method->flags() & ACC_STATIC) { + jclass = getJClass(t, method->class_()); + RUNTIME_ARRAY_BODY(args)[argOffset++] + = reinterpret_cast(&jclass); + } else { + RUNTIME_ARRAY_BODY(args)[argOffset++] = reinterpret_cast(sp++); + } + RUNTIME_ARRAY_BODY(types)[typeOffset++] = POINTER_TYPE; + + MethodSpecIterator it( + t, reinterpret_cast(method->spec()->body().begin())); + + while (it.hasNext()) { + unsigned type = RUNTIME_ARRAY_BODY(types)[typeOffset++] + = fieldType(t, fieldCode(t, *it.next())); + + switch (type) { + case INT8_TYPE: + case INT16_TYPE: + case INT32_TYPE: + case FLOAT_TYPE: + RUNTIME_ARRAY_BODY(args)[argOffset++] = *(sp++); + break; + + case INT64_TYPE: + case DOUBLE_TYPE: { + memcpy(RUNTIME_ARRAY_BODY(args) + argOffset, sp, 8); + argOffset += (8 / BytesPerWord); + sp += 2; + } break; + + case POINTER_TYPE: { + if (*sp) { + RUNTIME_ARRAY_BODY(args)[argOffset++] = reinterpret_cast(sp); + } else { + RUNTIME_ARRAY_BODY(args)[argOffset++] = 0; + } + ++sp; + } break; + + default: + abort(t); + } + } + + unsigned returnCode = method->returnCode(); + unsigned returnType = fieldType(t, returnCode); + uint64_t result; + + if (DebugNatives) { + fprintf(stderr, + "invoke native method %s.%s\n", + method->class_()->name()->body().begin(), + method->name()->body().begin()); + } + + if (method->flags() & ACC_SYNCHRONIZED) { + if (method->flags() & ACC_STATIC) { + acquire(t, getJClass(t, method->class_())); + } else { + acquire(t, *reinterpret_cast(RUNTIME_ARRAY_BODY(args)[1])); + } + } + + Reference* reference = t->reference; + + { + ENTER(t, Thread::IdleState); + + bool noThrow = t->checkpoint->noThrow; + t->checkpoint->noThrow = true; + THREAD_RESOURCE(t, bool, noThrow, t->checkpoint->noThrow = noThrow); + + result = vm::dynamicCall(function, + RUNTIME_ARRAY_BODY(args), + RUNTIME_ARRAY_BODY(types), + count, + footprint * BytesPerWord, + returnType); + } + + if (method->flags() & ACC_SYNCHRONIZED) { + if (method->flags() & ACC_STATIC) { + release(t, getJClass(t, method->class_())); + } else { + release(t, *reinterpret_cast(RUNTIME_ARRAY_BODY(args)[1])); + } + } + + if (DebugNatives) { + fprintf(stderr, + "return from native method %s.%s\n", + method->class_()->name()->body().begin(), + method->name()->body().begin()); + } + + if (UNLIKELY(t->exception)) { + GcThrowable* exception = t->exception; + t->exception = 0; + vm::throw_(t, exception); + } + + switch (returnCode) { + case ByteField: + case BooleanField: + result = static_cast(result); + break; + + case CharField: + result = static_cast(result); + break; + + case ShortField: + result = static_cast(result); + break; + + case FloatField: + case IntField: + result = static_cast(result); + break; + + case LongField: + case DoubleField: + break; + + case ObjectField: + result = static_cast(result) + ? *reinterpret_cast(static_cast(result)) + : 0; + break; + + case VoidField: + result = 0; + break; + + default: + abort(t); + } + + while (t->reference != reference) { + dispose(t, t->reference); + } + + return result; +} + +uint64_t invokeNative2(MyThread* t, GcMethod* method) +{ + GcNative* native = getMethodRuntimeData(t, method)->native(); + if (native->fast()) { + return invokeNativeFast(t, method, native->function()); + } else { + return invokeNativeSlow(t, method, native->function()); + } +} + +uint64_t invokeNative(MyThread* t) +{ + if (t->trace->nativeMethod == 0) { + void* ip; + if (t->tailAddress) { + ip = t->tailAddress; + t->tailAddress = 0; + } else { + ip = getIp(t); + } + + GcCallNode* node = findCallNode(t, ip); + GcMethod* target = node->target(); + if (node->flags() & TraceElement::VirtualCall) { + target = resolveTarget(t, t->stack, target); + } + t->trace->nativeMethod = target; + } + + assertT(t, t->tailAddress == 0); + + uint64_t result = 0; + + t->trace->targetMethod = t->trace->nativeMethod; + + t->m->classpath->resolveNative(t, t->trace->nativeMethod); + + result = invokeNative2(t, t->trace->nativeMethod); + + unsigned parameterFootprint = t->trace->targetMethod->parameterFootprint(); + + uintptr_t* stack = static_cast(t->stack); + + if (avian::codegen::TailCalls + and t->arch->argumentFootprint(parameterFootprint) + > t->arch->stackAlignmentInWords()) { + stack += t->arch->argumentFootprint(parameterFootprint) + - t->arch->stackAlignmentInWords(); + } + + stack += t->arch->frameReturnAddressSize(); + + t->trace->targetMethod = 0; + t->trace->nativeMethod = 0; + + t->newStack = stack; + + return result; +} + +void findFrameMapInSimpleTable(MyThread* t, + GcMethod* method, + GcIntArray* table, + int32_t offset, + int32_t** map, + unsigned* start) +{ + unsigned tableSize = simpleFrameMapTableSize(t, method, table); + unsigned indexSize = table->length() - tableSize; + + *map = &table->body()[indexSize]; + + unsigned bottom = 0; + unsigned top = indexSize; + for (unsigned span = top - bottom; span; span = top - bottom) { + unsigned middle = bottom + (span / 2); + int32_t v = table->body()[middle]; + + if (offset == v) { + *start = frameMapSizeInBits(t, method) * middle; + return; + } else if (offset < v) { + top = middle; + } else { + bottom = middle + 1; + } + } + + abort(t); +} + +void findFrameMap(MyThread* t, + void* stack UNUSED, + GcMethod* method, + int32_t offset, + int32_t** map, + unsigned* start) +{ + findFrameMapInSimpleTable( + t, method, method->code()->stackMap(), offset, map, start); +} + +void visitStackAndLocals(MyThread* t, + Heap::Visitor* v, + void* frame, + GcMethod* method, + void* ip) +{ + unsigned count = frameMapSizeInBits(t, method); + + if (count) { + void* stack = stackForFrame(t, frame, method); + + int32_t* map; + unsigned offset; + findFrameMap( + t, + stack, + method, + difference(ip, reinterpret_cast(methodAddress(t, method))), + &map, + &offset); + + for (unsigned i = 0; i < count; ++i) { + int j = offset + i; + if (map[j / 32] & (static_cast(1) << (j % 32))) { + v->visit(localObject(t, stack, method, i)); + } + } + } +} + +void visitArgument(MyThread* t, Heap::Visitor* v, void* stack, unsigned index) +{ + v->visit(static_cast(stack) + index + + t->arch->frameReturnAddressSize() + t->arch->frameFooterSize()); +} + +void visitArguments(MyThread* t, + Heap::Visitor* v, + void* stack, + GcMethod* method) +{ + unsigned index = 0; + + if ((method->flags() & ACC_STATIC) == 0) { + visitArgument(t, v, stack, index++); + } + + for (MethodSpecIterator it( + t, reinterpret_cast(method->spec()->body().begin())); + it.hasNext();) { + switch (*it.next()) { + case 'L': + case '[': + visitArgument(t, v, stack, index++); + break; + + case 'J': + case 'D': + index += 2; + break; + + default: + ++index; + break; + } + } +} + +void visitStack(MyThread* t, Heap::Visitor* v) +{ + void* ip = getIp(t); + void* stack = t->stack; + + MyThread::CallTrace* trace = t->trace; + GcMethod* targetMethod = (trace ? trace->targetMethod : 0); + GcMethod* target = targetMethod; + bool mostRecent = true; + + while (stack) { + if (targetMethod) { + visitArguments(t, v, stack, targetMethod); + targetMethod = 0; + } + + GcMethod* method = methodForIp(t, ip); + if (method) { + PROTECT(t, method); + + void* nextIp = ip; + nextFrame(t, &nextIp, &stack, method, target, mostRecent); + + visitStackAndLocals(t, v, stack, method, ip); + + ip = nextIp; + + target = method; + } else if (trace) { + stack = trace->stack; + ip = trace->ip; + trace = trace->next; + + if (trace) { + targetMethod = trace->targetMethod; + target = targetMethod; + } else { + target = 0; + } + } else { + break; + } + + mostRecent = false; + } +} + +void walkContinuationBody(MyThread* t, + Heap::Walker* w, + GcContinuation* c, + int start) +{ + const int BodyOffset = ContinuationBody / BytesPerWord; + + GcMethod* method = t->m->heap->follow(c->method()); + int count = frameMapSizeInBits(t, method); + + if (count) { + int stack = BodyOffset + (c->framePointerOffset() / BytesPerWord) + - t->arch->framePointerOffset() + - stackOffsetFromFrame(t, method); + + int first = stack + localOffsetFromStack(t, count - 1, method); + if (start > first) { + count -= start - first; + } + + int32_t* map; + unsigned offset; + findFrameMap(t, + reinterpret_cast(c) + stack, + method, + difference(c->address(), + reinterpret_cast(methodAddress(t, method))), + &map, + &offset); + + for (int i = count - 1; i >= 0; --i) { + int j = offset + i; + if (map[j / 32] & (static_cast(1) << (j % 32))) { + if (not w->visit(stack + localOffsetFromStack(t, i, method))) { + return; + } + } + } + } +} + +void callContinuation(MyThread* t, + GcContinuation* continuation, + object result, + GcThrowable* exception, + void* ip, + void* stack) +{ + assertT(t, t->exception == 0); + + if (exception) { + t->exception = exception; + + MyThread::TraceContext c(t, ip, stack, continuation, t->trace); + + void* frame; + findUnwindTarget(t, &ip, &frame, &stack, &continuation); + } + + t->trace->nativeMethod = 0; + t->trace->targetMethod = 0; + + popResources(t); + + transition(t, ip, stack, continuation, t->trace); + + vmJump(ip, 0, stack, t, reinterpret_cast(result), 0); +} + +int8_t* returnSpec(MyThread* t, GcMethod* method) +{ + int8_t* s = method->spec()->body().begin(); + while (*s and *s != ')') + ++s; + expect(t, *s == ')'); + return s + 1; +} + +GcClass* returnClass(MyThread* t, GcMethod* method) +{ + PROTECT(t, method); + + int8_t* spec = returnSpec(t, method); + unsigned length = strlen(reinterpret_cast(spec)); + GcByteArray* name; + if (*spec == '[') { + name = makeByteArray(t, length + 1); + memcpy(name->body().begin(), spec, length); + } else { + assertT(t, *spec == 'L'); + assertT(t, spec[length - 1] == ';'); + name = makeByteArray(t, length - 1); + memcpy(name->body().begin(), spec + 1, length - 2); + } + + return resolveClass(t, method->class_()->loader(), name); +} + +bool compatibleReturnType(MyThread* t, GcMethod* oldMethod, GcMethod* newMethod) +{ + if (oldMethod == newMethod) { + return true; + } else if (oldMethod->returnCode() == newMethod->returnCode()) { + if (oldMethod->returnCode() == ObjectField) { + PROTECT(t, newMethod); + + GcClass* oldClass = returnClass(t, oldMethod); + PROTECT(t, oldClass); + + GcClass* newClass = returnClass(t, newMethod); + + return isAssignableFrom(t, oldClass, newClass); + } else { + return true; + } + } else { + return oldMethod->returnCode() == VoidField; + } +} + +void jumpAndInvoke(MyThread* t, GcMethod* method, void* stack, ...) +{ + t->trace->targetMethod = 0; + + if (method->flags() & ACC_NATIVE) { + t->trace->nativeMethod = method; + } else { + t->trace->nativeMethod = 0; + } + + unsigned argumentCount = method->parameterFootprint(); + THREAD_RUNTIME_ARRAY(t, uintptr_t, arguments, argumentCount); + va_list a; + va_start(a, stack); + for (unsigned i = 0; i < argumentCount; ++i) { + RUNTIME_ARRAY_BODY(arguments)[i] = va_arg(a, uintptr_t); + } + va_end(a); + + assertT(t, t->exception == 0); + + popResources(t); + + vmJumpAndInvoke( + t, + reinterpret_cast(methodAddress(t, method)), + stack, + argumentCount * BytesPerWord, + RUNTIME_ARRAY_BODY(arguments), + (t->arch->alignFrameSize(t->arch->argumentFootprint(argumentCount)) + + t->arch->frameReturnAddressSize()) * BytesPerWord); +} + +void callContinuation(MyThread* t, + GcContinuation* continuation, + object result, + GcThrowable* exception) +{ + enum { Call, Unwind, Rewind } action; + + GcContinuation* nextContinuation = 0; + + if (t->continuation == 0 + or t->continuation->context() != continuation->context()) { + PROTECT(t, continuation); + PROTECT(t, result); + PROTECT(t, exception); + + if (compatibleReturnType( + t, t->trace->originalMethod, continuation->context()->method())) { + GcContinuationContext* oldContext; + GcContinuationContext* unwindContext; + + if (t->continuation) { + oldContext = t->continuation->context(); + unwindContext = oldContext; + } else { + oldContext = 0; + unwindContext = 0; + } + + GcContinuationContext* rewindContext = 0; + + for (GcContinuationContext* newContext = continuation->context(); + newContext; + newContext = newContext->next()) { + if (newContext == oldContext) { + unwindContext = 0; + break; + } else { + rewindContext = newContext; + } + } + + if (unwindContext and unwindContext->continuation()) { + nextContinuation + = cast(t, unwindContext->continuation()); + result = makeUnwindResult(t, continuation, result, exception); + action = Unwind; + } else if (rewindContext and rewindContext->continuation()) { + nextContinuation + = cast(t, rewindContext->continuation()); + action = Rewind; + + if (compileRoots(t)->rewindMethod() == 0) { + PROTECT(t, nextContinuation); + + GcMethod* method = resolveMethod( + t, + roots(t)->bootLoader(), + "avian/Continuations", + "rewind", + "(Ljava/lang/Runnable;Lavian/Callback;Ljava/lang/Object;" + "Ljava/lang/Throwable;)V"); + + PROTECT(t, method); + + compile(t, local::codeAllocator(t), 0, method); + + compileRoots(t)->setRewindMethod(t, method); + } + } else { + action = Call; + } + } else { + throwNew(t, GcIncompatibleContinuationException::Type); + } + } else { + action = Call; + } + + void* ip; + void* frame; + void* stack; + GcContinuation* threadContinuation; + findUnwindTarget(t, &ip, &frame, &stack, &threadContinuation); + + switch (action) { + case Call: { + callContinuation(t, continuation, result, exception, ip, stack); + } break; + + case Unwind: { + callContinuation(t, nextContinuation, result, 0, ip, stack); + } break; + + case Rewind: { + transition(t, 0, 0, nextContinuation, t->trace); + + jumpAndInvoke(t, + compileRoots(t)->rewindMethod(), + stack, + nextContinuation->context()->before(), + continuation, + result, + exception); + } break; + + default: + abort(t); + } +} + +void callWithCurrentContinuation(MyThread* t, object receiver) +{ + GcMethod* method = 0; + void* ip = 0; + void* stack = 0; + + { + PROTECT(t, receiver); + + if (compileRoots(t)->receiveMethod() == 0) { + GcMethod* m = resolveMethod(t, + roots(t)->bootLoader(), + "avian/Function", + "call", + "(Ljava/lang/Object;)Ljava/lang/Object;"); + + if (m) { + compileRoots(t)->setReceiveMethod(t, m); + + GcClass* continuationClass = type(t, GcContinuation::Type); + + if (continuationClass->vmFlags() & BootstrapFlag) { + resolveSystemClass( + t, roots(t)->bootLoader(), continuationClass->name()); + } + } + } + + method = findInterfaceMethod( + t, compileRoots(t)->receiveMethod(), objectClass(t, receiver)); + PROTECT(t, method); + + compile(t, local::codeAllocator(t), 0, method); + + t->continuation = makeCurrentContinuation(t, &ip, &stack); + } + + jumpAndInvoke(t, method, stack, receiver, t->continuation); +} + +void dynamicWind(MyThread* t, object before, object thunk, object after) +{ + void* ip = 0; + void* stack = 0; + + { + PROTECT(t, before); + PROTECT(t, thunk); + PROTECT(t, after); + + if (compileRoots(t)->windMethod() == 0) { + GcMethod* method = resolveMethod( + t, + roots(t)->bootLoader(), + "avian/Continuations", + "wind", + "(Ljava/lang/Runnable;Ljava/util/concurrent/Callable;" + "Ljava/lang/Runnable;)Lavian/Continuations$UnwindResult;"); + + if (method) { + compileRoots(t)->setWindMethod(t, method); + compile(t, local::codeAllocator(t), 0, method); + } + } + + t->continuation = makeCurrentContinuation(t, &ip, &stack); + + GcContinuationContext* newContext + = makeContinuationContext(t, + t->continuation->context(), + before, + after, + t->continuation, + t->trace->originalMethod); + + t->continuation->setContext(t, newContext); + } + + jumpAndInvoke(t, compileRoots(t)->windMethod(), stack, before, thunk, after); +} + +class ArgumentList { + public: + ArgumentList(Thread* t, + uintptr_t* array, + unsigned size, + bool* objectMask, + object this_, + const char* spec, + bool indirectObjects, + va_list arguments) + : t(static_cast(t)), + array(array), + objectMask(objectMask), + size(size), + position(0), + protector(this) + { + if (this_) { + addObject(this_); + } + + for (MethodSpecIterator it(t, spec); it.hasNext();) { + switch (*it.next()) { + case 'L': + case '[': + if (indirectObjects) { + object* v = va_arg(arguments, object*); + addObject(v ? *v : 0); + } else { + addObject(va_arg(arguments, object)); + } + break; + + case 'J': + addLong(va_arg(arguments, uint64_t)); + break; + + case 'D': + addLong(doubleToBits(va_arg(arguments, double))); + break; + + case 'F': + addInt(floatToBits(va_arg(arguments, double))); + break; + + default: + addInt(va_arg(arguments, uint32_t)); + break; + } + } + } + + ArgumentList(Thread* t, + uintptr_t* array, + unsigned size, + bool* objectMask, + object this_, + const char* spec, + const jvalue* arguments) + : t(static_cast(t)), + array(array), + objectMask(objectMask), + size(size), + position(0), + protector(this) + { + if (this_) { + addObject(this_); + } + + unsigned index = 0; + for (MethodSpecIterator it(t, spec); it.hasNext();) { + switch (*it.next()) { + case 'L': + case '[': { + object* v = arguments[index++].l; + addObject(v ? *v : 0); + } break; + + case 'J': + addLong(arguments[index++].j); + break; + + case 'D': + addLong(doubleToBits(arguments[index++].d)); + break; + + case 'F': + addInt(floatToBits(arguments[index++].f)); + break; + + default: + addInt(arguments[index++].i); + break; + } + } + } + + ArgumentList(Thread* t, + uintptr_t* array, + unsigned size, + bool* objectMask, + object this_, + const char* spec, + object arguments) + : t(static_cast(t)), + array(array), + objectMask(objectMask), + size(size), + position(0), + protector(this) + { + if (this_) { + addObject(this_); + } + + unsigned index = 0; + for (MethodSpecIterator it(t, spec); it.hasNext();) { + switch (*it.next()) { + case 'L': + case '[': + addObject(objectArrayBody(t, arguments, index++)); + break; + + case 'J': + case 'D': + addLong( + fieldAtOffset(objectArrayBody(t, arguments, index++), 8)); + break; + + default: + addInt(fieldAtOffset(objectArrayBody(t, arguments, index++), + BytesPerWord)); + break; + } + } + } + + void addObject(object v) + { + assertT(t, position < size); + + array[position] = reinterpret_cast(v); + objectMask[position] = true; + ++position; + } + + void addInt(uintptr_t v) + { + assertT(t, position < size); + + array[position] = v; + objectMask[position] = false; + ++position; + } + + void addLong(uint64_t v) + { + assertT(t, position < size - 1); + + memcpy(array + position, &v, 8); + + objectMask[position] = false; + objectMask[position + 1] = false; + + position += 2; + } + + MyThread* t; + uintptr_t* array; + bool* objectMask; + unsigned size; + unsigned position; + + class MyProtector : public Thread::Protector { + public: + MyProtector(ArgumentList* list) : Protector(list->t), list(list) + { + } + + virtual void visit(Heap::Visitor* v) + { + for (unsigned i = 0; i < list->position; ++i) { + if (list->objectMask[i]) { + v->visit(reinterpret_cast(list->array + i)); + } + } + } + + ArgumentList* list; + } protector; +}; + +object invoke(Thread* thread, GcMethod* method, ArgumentList* arguments) +{ + MyThread* t = static_cast(thread); + + if (false) { + PROTECT(t, method); + + compile( + t, + local::codeAllocator(static_cast(t)), + 0, + resolveMethod( + t, roots(t)->appLoader(), "foo/ClassName", "methodName", "()V")); + } + + uintptr_t stackLimit = t->stackLimit; + uintptr_t stackPosition = reinterpret_cast(&t); + if (stackLimit == 0) { + t->stackLimit = stackPosition - t->m->stackSizeInBytes; + } else if (stackPosition < stackLimit) { + throwNew(t, GcStackOverflowError::Type); + } + + THREAD_RESOURCE(t, + uintptr_t, + stackLimit, + static_cast(t)->stackLimit = stackLimit); + + unsigned returnCode = method->returnCode(); + unsigned returnType = fieldType(t, returnCode); + + uint64_t result; + + { + MyThread::CallTrace trace(t, method); + + MyCheckpoint checkpoint(t); + + assertT(t, arguments->position == arguments->size); + + result = vmInvoke( + t, + reinterpret_cast(methodAddress(t, method)), + arguments->array, + arguments->position * BytesPerWord, + t->arch->alignFrameSize(t->arch->argumentFootprint(arguments->position)) + * BytesPerWord, + returnType); + } + + if (t->exception) { + if (UNLIKELY(t->getFlags() & Thread::UseBackupHeapFlag)) { + collect(t, Heap::MinorCollection); + } + + GcThrowable* exception = t->exception; + t->exception = 0; + vm::throw_(t, exception); + } + + object r; + switch (returnCode) { + case ByteField: + case BooleanField: + case CharField: + case ShortField: + case FloatField: + case IntField: + r = makeInt(t, result); + break; + + case LongField: + case DoubleField: + r = makeLong(t, result); + break; + + case ObjectField: + r = reinterpret_cast(result); + break; + + case VoidField: + r = 0; + break; + + default: + abort(t); + } + + return r; +} + +class SignalHandler : public SignalRegistrar::Handler { + public: + typedef GcThrowable* (GcRoots::*ExceptionGetter)(); + SignalHandler(Gc::Type type, ExceptionGetter exc, unsigned fixedSize) + : m(0), type(type), exc(exc), fixedSize(fixedSize) + { + } + + void setException(MyThread* t) { + if (ensure(t, pad(fixedSize) + traceSize(t))) { + t->setFlag(Thread::TracingFlag); + t->exception = makeThrowable(t, type); + t->clearFlag(Thread::TracingFlag); + } else { + // not enough memory available for a new exception and stack + // trace -- use a preallocated instance instead + t->exception = (vm::roots(t)->*exc)(); + } + } + + virtual bool handleSignal(void** ip, + void** frame, + void** stack, + void** thread) + { + MyThread* t = static_cast(m->localThread->get()); + if (t and t->state == Thread::ActiveState) { + if (t->getFlags() & Thread::TryNativeFlag) { + setException(t); + + popResources(t); + + GcContinuation* continuation; + findUnwindTarget(t, ip, frame, stack, &continuation); + + t->trace->targetMethod = 0; + t->trace->nativeMethod = 0; + + transition(t, *ip, *stack, continuation, t->trace); + + *thread = t; + + return true; + } else if (methodForIp(t, *ip)) { + // add one to the IP since findLineNumber will subtract one + // when we make the trace: + MyThread::TraceContext context( + t, + static_cast(*ip) + 1, + static_cast(*stack) - t->arch->frameReturnAddressSize(), + t->continuation, + t->trace); + + setException(t); + + // printTrace(t, t->exception); + + GcContinuation* continuation; + findUnwindTarget(t, ip, frame, stack, &continuation); + + transition(t, *ip, *stack, continuation, t->trace); + + *thread = t; + + return true; + } + } + + if (compileLog) { + fflush(compileLog); + } + + return false; + } + + Machine* m; + Gc::Type type; + ExceptionGetter exc; + unsigned fixedSize; +}; + +bool isThunk(MyThread* t, void* ip); + +bool isVirtualThunk(MyThread* t, void* ip); + +bool isThunkUnsafeStack(MyThread* t, void* ip); + +void boot(MyThread* t, BootImage* image, uint8_t* code); + +class MyProcessor; + +MyProcessor* processor(MyThread* t); + +#ifndef AVIAN_AOT_ONLY +void compileThunks(MyThread* t, FixedAllocator* allocator); +#endif + +class CompilationHandlerList { + public: + CompilationHandlerList(CompilationHandlerList* next, + Processor::CompilationHandler* handler) + : next(next), handler(handler) + { + } + + void dispose(Allocator* allocator) + { + if (next) { + next->dispose(allocator); + } + handler->dispose(); + allocator->free(this, sizeof(*this)); + } + + CompilationHandlerList* next; + Processor::CompilationHandler* handler; +}; + +template +int checkConstant(MyThread* t, size_t expected, T C::*field, const char* name) +{ + size_t actual = reinterpret_cast(&(t->*field)) + - reinterpret_cast(t); + if (expected != actual) { + fprintf(stderr, + "constant mismatch (%s): \n\tconstant says: %d\n\tc++ compiler " + "says: %d\n", + name, + (unsigned)expected, + (unsigned)actual); + return 1; + } + return 0; +} + +class MyProcessor : public Processor { + public: + class Thunk { + public: + Thunk() : start(0), frameSavedOffset(0), length(0) + { + } + + Thunk(uint8_t* start, unsigned frameSavedOffset, unsigned length) + : start(start), frameSavedOffset(frameSavedOffset), length(length) + { + } + + uint8_t* start; + unsigned frameSavedOffset; + unsigned length; + }; + + class ThunkCollection { + public: + Thunk default_; + Thunk defaultVirtual; + Thunk defaultDynamic; + Thunk native; + Thunk aioob; + Thunk stackOverflow; + Thunk table; + }; + + MyProcessor(System* s, + Allocator* allocator, + const char* crashDumpDirectory, + bool useNativeFeatures) + : s(s), + allocator(allocator), + roots(0), + bootImage(0), + heapImage(0), + codeImage(0), + codeImageSize(0), + segFaultHandler(GcNullPointerException::Type, + &GcRoots::nullPointerException, + GcNullPointerException::FixedSize), + divideByZeroHandler(GcArithmeticException::Type, + &GcRoots::arithmeticException, + GcArithmeticException::FixedSize), + codeAllocator(s, Slice(0, 0)), + callTableSize(0), + dynamicIndex(0), + useNativeFeatures(useNativeFeatures), + compilationHandlers(0), + dynamicTable(0), + dynamicTableSize(0) + { + thunkTable[compileMethodIndex] = voidPointer(local::compileMethod); + thunkTable[compileVirtualMethodIndex] = voidPointer(compileVirtualMethod); + thunkTable[linkDynamicMethodIndex] = voidPointer(linkDynamicMethod); + thunkTable[invokeNativeIndex] = voidPointer(invokeNative); + thunkTable[throwArrayIndexOutOfBoundsIndex] + = voidPointer(throwArrayIndexOutOfBounds); + thunkTable[throwStackOverflowIndex] = voidPointer(throwStackOverflow); + + using namespace avian::codegen::runtime; + +#define THUNK(s) thunkTable[s##Index] = voidPointer(s); +#include "thunks.cpp" +#undef THUNK + // Set the dummyIndex entry to a constant which should require the + // maximum number of bytes to represent in assembly code + // (i.e. can't be represented by a smaller number of bytes and + // implicitly sign- or zero-extended). We'll use this property + // later to determine the maximum size of a thunk in the thunk + // table. + thunkTable[dummyIndex] = reinterpret_cast( + static_cast(UINT64_C(0x5555555555555555))); + + signals.setCrashDumpDirectory(crashDumpDirectory); + } + + virtual Thread* makeThread(Machine* m, GcThread* javaThread, Thread* parent) + { + MyThread* t = new (m->heap->allocate(sizeof(MyThread))) MyThread( + m, javaThread, static_cast(parent), useNativeFeatures); + + t->heapImage = heapImage; + t->codeImage = codeImage; + t->thunkTable = thunkTable; + t->dynamicTable = local::dynamicTable(t); + +#if TARGET_BYTES_PER_WORD == BYTES_PER_WORD + + int mismatches + = checkConstant(t, + TARGET_THREAD_EXCEPTION, + &Thread::exception, + "TARGET_THREAD_EXCEPTION") + + checkConstant(t, + TARGET_THREAD_EXCEPTIONSTACKADJUSTMENT, + &MyThread::exceptionStackAdjustment, + "TARGET_THREAD_EXCEPTIONSTACKADJUSTMENT") + + checkConstant(t, + TARGET_THREAD_EXCEPTIONOFFSET, + &MyThread::exceptionOffset, + "TARGET_THREAD_EXCEPTIONOFFSET") + + checkConstant(t, + TARGET_THREAD_EXCEPTIONHANDLER, + &MyThread::exceptionHandler, + "TARGET_THREAD_EXCEPTIONHANDLER") + + checkConstant( + t, TARGET_THREAD_IP, &MyThread::ip, "TARGET_THREAD_IP") + + checkConstant( + t, TARGET_THREAD_STACK, &MyThread::stack, "TARGET_THREAD_STACK") + + checkConstant(t, + TARGET_THREAD_NEWSTACK, + &MyThread::newStack, + "TARGET_THREAD_NEWSTACK") + + checkConstant(t, + TARGET_THREAD_TAILADDRESS, + &MyThread::tailAddress, + "TARGET_THREAD_TAILADDRESS") + + checkConstant(t, + TARGET_THREAD_VIRTUALCALLTARGET, + &MyThread::virtualCallTarget, + "TARGET_THREAD_VIRTUALCALLTARGET") + + checkConstant(t, + TARGET_THREAD_VIRTUALCALLINDEX, + &MyThread::virtualCallIndex, + "TARGET_THREAD_VIRTUALCALLINDEX") + + checkConstant(t, + TARGET_THREAD_HEAPIMAGE, + &MyThread::heapImage, + "TARGET_THREAD_HEAPIMAGE") + + checkConstant(t, + TARGET_THREAD_CODEIMAGE, + &MyThread::codeImage, + "TARGET_THREAD_CODEIMAGE") + + checkConstant(t, + TARGET_THREAD_THUNKTABLE, + &MyThread::thunkTable, + "TARGET_THREAD_THUNKTABLE") + + checkConstant(t, + TARGET_THREAD_DYNAMICTABLE, + &MyThread::dynamicTable, + "TARGET_THREAD_DYNAMICTABLE") + + checkConstant(t, + TARGET_THREAD_STACKLIMIT, + &MyThread::stackLimit, + "TARGET_THREAD_STACKLIMIT"); + + if (mismatches > 0) { + fprintf(stderr, "%d constant mismatches\n", mismatches); + abort(t); + } + + expect(t, TargetClassArrayElementSize == ClassArrayElementSize); + expect(t, TargetClassFixedSize == ClassFixedSize); + expect(t, TargetClassVtable == ClassVtable); + +#endif + + t->init(); + + return t; + } + + virtual GcMethod* makeMethod(vm::Thread* t, + uint8_t vmFlags, + uint8_t returnCode, + uint8_t parameterCount, + uint8_t parameterFootprint, + uint16_t flags, + uint16_t offset, + GcByteArray* name, + GcByteArray* spec, + GcMethodAddendum* addendum, + GcClass* class_, + GcCode* code) + { + if (code) { + code->compiled() = local::defaultThunk(static_cast(t)); + } + + return vm::makeMethod(t, + vmFlags, + returnCode, + parameterCount, + parameterFootprint, + flags, + offset, + 0, + 0, + name, + spec, + addendum, + class_, + code); + } + + virtual GcClass* makeClass(vm::Thread* t, + uint16_t flags, + uint16_t vmFlags, + uint16_t fixedSize, + uint8_t arrayElementSize, + uint8_t arrayDimensions, + GcClass* arrayElementClass, + GcIntArray* objectMask, + GcByteArray* name, + GcByteArray* sourceFile, + GcClass* super, + object interfaceTable, + object virtualTable, + object fieldTable, + object methodTable, + GcClassAddendum* addendum, + GcSingleton* staticTable, + GcClassLoader* loader, + unsigned vtableLength) + { + return vm::makeClass(t, + flags, + vmFlags, + fixedSize, + arrayElementSize, + arrayDimensions, + arrayElementClass, + 0, + objectMask, + name, + sourceFile, + super, + interfaceTable, + virtualTable, + fieldTable, + methodTable, + addendum, + staticTable, + loader, + 0, + vtableLength); + } + + virtual void initVtable(Thread* t, GcClass* c) + { + PROTECT(t, c); + for (int i = c->length() - 1; i >= 0; --i) { + void* thunk + = reinterpret_cast(virtualThunk(static_cast(t), i)); + c->vtable()[i] = thunk; + } + } + + virtual void visitObjects(Thread* vmt, Heap::Visitor* v) + { + MyThread* t = static_cast(vmt); + + if (t == t->m->rootThread) { + v->visit(&roots); + } + + for (MyThread::CallTrace* trace = t->trace; trace; trace = trace->next) { + v->visit(&(trace->continuation)); + v->visit(&(trace->nativeMethod)); + v->visit(&(trace->targetMethod)); + v->visit(&(trace->originalMethod)); + } + + v->visit(&(t->continuation)); + + for (Reference* r = t->reference; r; r = r->next) { + v->visit(&(r->target)); + } + + visitStack(t, v); + } + + virtual void walkStack(Thread* vmt, StackVisitor* v) + { + MyThread* t = static_cast(vmt); + + MyStackWalker walker(t); + walker.walk(v); + } + + virtual int lineNumber(Thread* vmt, GcMethod* method, int ip) + { + return findLineNumber(static_cast(vmt), method, ip); + } + + virtual object* makeLocalReference(Thread* vmt, object o) + { + if (o) { + MyThread* t = static_cast(vmt); + + for (Reference* r = t->reference; r; r = r->next) { + if (r->target == o) { + acquire(t, r); + + return &(r->target); + } + } + + Reference* r = new (t->m->heap->allocate(sizeof(Reference))) + Reference(o, &(t->reference), false); + + acquire(t, r); + + return &(r->target); + } else { + return 0; + } + } + + virtual void disposeLocalReference(Thread* t, object* r) + { + if (r) { + release(t, reinterpret_cast(r)); + } + } + + virtual bool pushLocalFrame(Thread* vmt, unsigned) + { + MyThread* t = static_cast(vmt); + + t->referenceFrame = new (t->m->heap->allocate(sizeof(List))) + List(t->reference, t->referenceFrame); + + return true; + } + + virtual void popLocalFrame(Thread* vmt) + { + MyThread* t = static_cast(vmt); + + List* f = t->referenceFrame; + t->referenceFrame = f->next; + while (t->reference != f->item) { + vm::dispose(t, t->reference); + } + + t->m->heap->free(f, sizeof(List)); + } + + virtual object invokeArray(Thread* t, + GcMethod* method, + object this_, + object arguments) + { + assertT(t, t->exception == 0); + + assertT( + t, + t->state == Thread::ActiveState or t->state == Thread::ExclusiveState); + + assertT(t, ((method->flags() & ACC_STATIC) == 0) xor (this_ == 0)); + + const char* spec = reinterpret_cast(method->spec()->body().begin()); + + unsigned size = method->parameterFootprint(); + THREAD_RUNTIME_ARRAY(t, uintptr_t, array, size); + THREAD_RUNTIME_ARRAY(t, bool, objectMask, size); + ArgumentList list(t, + RUNTIME_ARRAY_BODY(array), + size, + RUNTIME_ARRAY_BODY(objectMask), + this_, + spec, + arguments); + + PROTECT(t, method); + + method = findMethod(t, method, this_); + + compile(static_cast(t), + local::codeAllocator(static_cast(t)), + 0, + method); + + return local::invoke(t, method, &list); + } + + virtual object invokeArray(Thread* t, + GcMethod* method, + object this_, + const jvalue* arguments) + { + assertT(t, t->exception == 0); + + assertT( + t, + t->state == Thread::ActiveState or t->state == Thread::ExclusiveState); + + assertT(t, ((method->flags() & ACC_STATIC) == 0) xor (this_ == 0)); + + const char* spec = reinterpret_cast(method->spec()->body().begin()); + + unsigned size = method->parameterFootprint(); + THREAD_RUNTIME_ARRAY(t, uintptr_t, array, size); + THREAD_RUNTIME_ARRAY(t, bool, objectMask, size); + ArgumentList list(t, + RUNTIME_ARRAY_BODY(array), + size, + RUNTIME_ARRAY_BODY(objectMask), + this_, + spec, + arguments); + + PROTECT(t, method); + + method = findMethod(t, method, this_); + + compile(static_cast(t), + local::codeAllocator(static_cast(t)), + 0, + method); + + return local::invoke(t, method, &list); + } + + virtual object invokeList(Thread* t, + GcMethod* method, + object this_, + bool indirectObjects, + va_list arguments) + { + assertT(t, t->exception == 0); + + assertT( + t, + t->state == Thread::ActiveState or t->state == Thread::ExclusiveState); + + assertT(t, ((method->flags() & ACC_STATIC) == 0) xor (this_ == 0)); + + const char* spec = reinterpret_cast(method->spec()->body().begin()); + + unsigned size = method->parameterFootprint(); + THREAD_RUNTIME_ARRAY(t, uintptr_t, array, size); + THREAD_RUNTIME_ARRAY(t, bool, objectMask, size); + ArgumentList list(t, + RUNTIME_ARRAY_BODY(array), + size, + RUNTIME_ARRAY_BODY(objectMask), + this_, + spec, + indirectObjects, + arguments); + + PROTECT(t, method); + + method = findMethod(t, method, this_); + + compile(static_cast(t), + local::codeAllocator(static_cast(t)), + 0, + method); + + return local::invoke(t, method, &list); + } + + virtual object invokeList(Thread* t, + GcClassLoader* loader, + const char* className, + const char* methodName, + const char* methodSpec, + object this_, + va_list arguments) + { + assertT(t, t->exception == 0); + + assertT( + t, + t->state == Thread::ActiveState or t->state == Thread::ExclusiveState); + + unsigned size = parameterFootprint(t, methodSpec, this_ == 0); + THREAD_RUNTIME_ARRAY(t, uintptr_t, array, size); + THREAD_RUNTIME_ARRAY(t, bool, objectMask, size); + ArgumentList list(t, + RUNTIME_ARRAY_BODY(array), + size, + RUNTIME_ARRAY_BODY(objectMask), + this_, + methodSpec, + false, + arguments); + + GcMethod* method + = resolveMethod(t, loader, className, methodName, methodSpec); + + assertT(t, ((method->flags() & ACC_STATIC) == 0) xor (this_ == 0)); + + PROTECT(t, method); + + compile(static_cast(t), + local::codeAllocator(static_cast(t)), + 0, + method); + + return local::invoke(t, method, &list); + } + + virtual void dispose(Thread* vmt) + { + MyThread* t = static_cast(vmt); + + while (t->reference) { + vm::dispose(t, t->reference); + } + + t->arch->release(); + + t->m->heap->free(t, sizeof(*t)); + } + + virtual void dispose() + { + if (codeAllocator.memory.begin()) { +#ifndef AVIAN_AOT_ONLY + Memory::free(codeAllocator.memory); +#endif + } + + if(compilationHandlers) { + compilationHandlers->dispose(allocator); + } + + signals.unregisterHandler(SignalRegistrar::SegFault); + signals.unregisterHandler(SignalRegistrar::DivideByZero); + signals.setCrashDumpDirectory(0); + + if (dynamicTable) { + allocator->free(dynamicTable, dynamicTableSize); + } + + this->~MyProcessor(); + + allocator->free(this, sizeof(*this)); + } + + virtual object getStackTrace(Thread* vmt, Thread* vmTarget) + { + MyThread* t = static_cast(vmt); + MyThread* target = static_cast(vmTarget); + MyProcessor* p = this; + + class Visitor : public System::ThreadVisitor { + public: + Visitor(MyThread* t, MyProcessor* p, MyThread* target) + : t(t), p(p), target(target), trace(0) + { + } + + virtual void visit(void* ip, void* stack, void* link) + { + MyThread::TraceContext c(target, link); + + if (methodForIp(t, ip)) { + // we caught the thread in Java code - use the register values + c.ip = ip; + c.stack = stack; + c.methodIsMostRecent = true; + } else if (target->transition) { + // we caught the thread in native code while in the middle + // of updating the context fields (MyThread::stack, etc.) + static_cast(c) = *(target->transition); + } else if (isVmInvokeUnsafeStack(ip)) { + // we caught the thread in native code just after returning + // from java code, but before clearing MyThread::stack + // (which now contains a garbage value), and the most recent + // Java frame, if any, can be found in + // MyThread::continuation or MyThread::trace + c.ip = 0; + c.stack = 0; + } else if (target->stack and (not isThunkUnsafeStack(t, ip)) + and (not isVirtualThunk(t, ip))) { + // we caught the thread in a thunk or native code, and the + // saved stack pointer indicates the most recent Java frame + // on the stack + c.ip = getIp(target); + c.stack = target->stack; + } else if (isThunk(t, ip) or isVirtualThunk(t, ip)) { + // we caught the thread in a thunk where the stack register + // indicates the most recent Java frame on the stack + + // On e.g. x86, the return address will have already been + // pushed onto the stack, in which case we use getIp to + // retrieve it. On e.g. ARM, it will be in the + // link register. Note that we can't just check if the link + // argument is null here, since we use ecx/rcx as a + // pseudo-link register on x86 for the purpose of tail + // calls. + c.ip = t->arch->hasLinkRegister() ? link : getIp(t, link, stack); + c.stack = stack; + } else { + // we caught the thread in native code, and the most recent + // Java frame, if any, can be found in + // MyThread::continuation or MyThread::trace + c.ip = 0; + c.stack = 0; + } + + if (ensure(t, traceSize(target))) { + t->setFlag(Thread::TracingFlag); + trace = makeTrace(t, target); + t->clearFlag(Thread::TracingFlag); + } + } + + MyThread* t; + MyProcessor* p; + MyThread* target; + object trace; + } visitor(t, p, target); + + t->m->system->visit(t->systemThread, target->systemThread, &visitor); + + if (UNLIKELY(t->getFlags() & Thread::UseBackupHeapFlag)) { + PROTECT(t, visitor.trace); + + collect(t, Heap::MinorCollection); + } + + return visitor.trace ? visitor.trace : makeObjectArray(t, 0); + } + + virtual void initialize(BootImage* image, Slice code) + { + bootImage = image; + codeAllocator.memory = code; + } + + virtual void addCompilationHandler(CompilationHandler* handler) + { + compilationHandlers + = new (allocator->allocate(sizeof(CompilationHandlerList))) + CompilationHandlerList(compilationHandlers, handler); + } + + virtual void compileMethod(Thread* vmt, + Zone* zone, + GcTriple** constants, + GcTriple** calls, + avian::codegen::DelayedPromise** addresses, + GcMethod* method, + OffsetResolver* resolver, + JavaVM* hostVM) + { + MyThread* t = static_cast(vmt); + BootContext bootContext( + t, *constants, *calls, *addresses, zone, resolver, hostVM); + + compile(t, &codeAllocator, &bootContext, method); + + *constants = bootContext.constants; + *calls = bootContext.calls; + *addresses = bootContext.addresses; + } + + virtual void visitRoots(Thread* t, HeapWalker* w) + { + bootImage->methodTree = w->visitRoot(compileRoots(t)->methodTree()); + bootImage->methodTreeSentinal + = w->visitRoot(compileRoots(t)->methodTreeSentinal()); + bootImage->virtualThunks = w->visitRoot(compileRoots(t)->virtualThunks()); + } + + virtual void normalizeVirtualThunks(Thread* t) + { + GcWordArray* a = compileRoots(t)->virtualThunks(); + for (unsigned i = 0; i < a->length(); i += 2) { + if (a->body()[i]) { + a->body()[i] + -= reinterpret_cast(codeAllocator.memory.begin()); + } + } + } + + virtual unsigned* makeCallTable(Thread* t, HeapWalker* w) + { + bootImage->codeSize = codeAllocator.offset; + bootImage->callCount = callTableSize; + + unsigned* table = static_cast( + t->m->heap->allocate(callTableSize * sizeof(unsigned) * 2)); + + unsigned index = 0; + GcArray* callTable = compileRoots(t)->callTable(); + for (unsigned i = 0; i < callTable->length(); ++i) { + for (GcCallNode* p = cast(t, callTable->body()[i]); p; + p = p->next()) { + table[index++] + = targetVW(p->address() - reinterpret_cast( + codeAllocator.memory.begin())); + table[index++] = targetVW( + w->map()->find(p->target()) + | (static_cast(p->flags()) << TargetBootShift)); + } + } + + return table; + } + + virtual void boot(Thread* t, BootImage* image, uint8_t* code) + { +#ifndef AVIAN_AOT_ONLY + if (codeAllocator.memory.begin() == 0) { + codeAllocator.memory = Memory::allocate(ExecutableAreaSizeInBytes, + Memory::ReadWriteExecute); + + expect(t, codeAllocator.memory.begin()); + } +#endif + + if (image and code) { + local::boot(static_cast(t), image, code); + } else { + roots = makeCompileRoots(t, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + { + GcArray* ct = makeArray(t, 128); + // sequence point, for gc (don't recombine statements) + compileRoots(t)->setCallTable(t, ct); + } + + GcTreeNode* tree = makeTreeNode(t, 0, 0, 0); + compileRoots(t)->setMethodTreeSentinal(t, tree); + compileRoots(t)->setMethodTree(t, tree); + tree->setLeft(t, tree); + tree->setRight(t, tree); + } + +#ifdef AVIAN_AOT_ONLY + thunks = bootThunks; +#else + local::compileThunks(static_cast(t), &codeAllocator); + + if (not(image and code)) { + bootThunks = thunks; + } +#endif + + segFaultHandler.m = t->m; + expect( + t, + signals.registerHandler(SignalRegistrar::SegFault, &segFaultHandler)); + + divideByZeroHandler.m = t->m; + expect(t, + signals.registerHandler(SignalRegistrar::DivideByZero, + ÷ByZeroHandler)); + } + + virtual void callWithCurrentContinuation(Thread* t, object receiver) + { + if (Continuations) { + local::callWithCurrentContinuation(static_cast(t), receiver); + } else { + abort(t); + } + } + + virtual void dynamicWind(Thread* t, object before, object thunk, object after) + { + if (Continuations) { + local::dynamicWind(static_cast(t), before, thunk, after); + } else { + abort(t); + } + } + + virtual void feedResultToContinuation(Thread* t, + GcContinuation* continuation, + object result) + { + if (Continuations) { + callContinuation(static_cast(t), continuation, result, 0); + } else { + abort(t); + } + } + + virtual void feedExceptionToContinuation(Thread* t, + GcContinuation* continuation, + GcThrowable* exception) + { + if (Continuations) { + callContinuation(static_cast(t), continuation, 0, exception); + } else { + abort(t); + } + } + + virtual void walkContinuationBody(Thread* t, + Heap::Walker* w, + object o, + unsigned start) + { + if (Continuations) { + local::walkContinuationBody( + static_cast(t), w, cast(t, o), start); + } else { + abort(t); + } + } + + System* s; + SignalRegistrar signals; + Allocator* allocator; + GcCompileRoots* roots; + BootImage* bootImage; + uintptr_t* heapImage; + uint8_t* codeImage; + unsigned codeImageSize; + SignalHandler segFaultHandler; + SignalHandler divideByZeroHandler; + FixedAllocator codeAllocator; + ThunkCollection thunks; + ThunkCollection bootThunks; + unsigned callTableSize; + unsigned dynamicIndex; + bool useNativeFeatures; + void* thunkTable[dummyIndex + 1]; + CompilationHandlerList* compilationHandlers; + void** dynamicTable; + unsigned dynamicTableSize; +}; + +unsigned& dynamicIndex(MyThread* t) +{ + return static_cast(t->m->processor)->dynamicIndex; +} + +void**& dynamicTable(MyThread* t) +{ + return static_cast(t->m->processor)->dynamicTable; +} + +unsigned& dynamicTableSize(MyThread* t) +{ + return static_cast(t->m->processor)->dynamicTableSize; +} + +const char* stringOrNull(const char* str) +{ + if (str) { + return str; + } else { + return "(null)"; + } +} + +size_t stringOrNullSize(const char* str) +{ + return strlen(stringOrNull(str)); +} + +void logCompile(MyThread* t, + const void* code, + unsigned size, + const char* class_, + const char* name, + const char* spec) +{ + static bool open = false; + if (not open) { + open = true; + const char* path = findProperty(t, "avian.jit.log"); + if (path) { + compileLog = vm::fopen(path, "wb"); + } else if (DebugCompile) { + compileLog = stderr; + } + } + + if (compileLog) { + fprintf(compileLog, + "%p,%p %s.%s%s\n", + code, + static_cast(code) + size, + class_, + name, + spec); + } + + size_t nameLength = stringOrNullSize(class_) + stringOrNullSize(name) + + stringOrNullSize(spec) + 2; + + THREAD_RUNTIME_ARRAY(t, char, completeName, nameLength); + + sprintf(RUNTIME_ARRAY_BODY(completeName), + "%s.%s%s", + stringOrNull(class_), + stringOrNull(name), + stringOrNull(spec)); + + MyProcessor* p = static_cast(t->m->processor); + for (CompilationHandlerList* h = p->compilationHandlers; h; h = h->next) { + h->handler->compiled(code, 0, 0, RUNTIME_ARRAY_BODY(completeName)); + } +} + +void* compileMethod2(MyThread* t, void* ip) +{ + GcCallNode* node = findCallNode(t, ip); + GcMethod* target = node->target(); + + PROTECT(t, node); + PROTECT(t, target); + + t->trace->targetMethod = target; + + THREAD_RESOURCE0(t, static_cast(t)->trace->targetMethod = 0); + + compile(t, codeAllocator(t), 0, target); + + uint8_t* updateIp = static_cast(ip); + + MyProcessor* p = processor(t); + + bool updateCaller = updateIp < p->codeImage + or updateIp >= p->codeImage + p->codeImageSize; + + uintptr_t address; + if (target->flags() & ACC_NATIVE) { + address = useLongJump(t, reinterpret_cast(ip)) + or (not updateCaller) + ? bootNativeThunk(t) + : nativeThunk(t); + } else { + address = methodAddress(t, target); + } + + if (updateCaller) { + avian::codegen::lir::UnaryOperation op; + if (node->flags() & TraceElement::LongCall) { + if (node->flags() & TraceElement::TailCall) { + op = avian::codegen::lir::AlignedLongJump; + } else { + op = avian::codegen::lir::AlignedLongCall; + } + } else if (node->flags() & TraceElement::TailCall) { + op = avian::codegen::lir::AlignedJump; + } else { + op = avian::codegen::lir::AlignedCall; + } + + updateCall(t, op, updateIp, reinterpret_cast(address)); + } + + return reinterpret_cast(address); +} + +bool isThunk(MyProcessor::ThunkCollection* thunks, void* ip) +{ + uint8_t* thunkStart = thunks->default_.start; + uint8_t* thunkEnd = thunks->table.start + (thunks->table.length * ThunkCount); + + return (reinterpret_cast(ip) + >= reinterpret_cast(thunkStart) + and reinterpret_cast(ip) + < reinterpret_cast(thunkEnd)); +} + +bool isThunk(MyThread* t, void* ip) +{ + MyProcessor* p = processor(t); + + return isThunk(&(p->thunks), ip) or isThunk(&(p->bootThunks), ip); +} + +bool isThunkUnsafeStack(MyProcessor::Thunk* thunk, void* ip) +{ + return reinterpret_cast(ip) + >= reinterpret_cast(thunk->start) + and reinterpret_cast(ip) + < reinterpret_cast(thunk->start + + thunk->frameSavedOffset); +} + +bool isThunkUnsafeStack(MyProcessor::ThunkCollection* thunks, void* ip) +{ + const unsigned NamedThunkCount = 6; + + MyProcessor::Thunk table[NamedThunkCount + ThunkCount]; + + table[0] = thunks->default_; + table[1] = thunks->defaultVirtual; + table[2] = thunks->defaultDynamic; + table[3] = thunks->native; + table[4] = thunks->aioob; + table[5] = thunks->stackOverflow; + + for (unsigned i = 0; i < ThunkCount; ++i) { + new (table + NamedThunkCount + i) + MyProcessor::Thunk(thunks->table.start + (i * thunks->table.length), + thunks->table.frameSavedOffset, + thunks->table.length); + } + + for (unsigned i = 0; i < NamedThunkCount + ThunkCount; ++i) { + if (isThunkUnsafeStack(table + i, ip)) { + return true; + } + } + + return false; +} + +bool isVirtualThunk(MyThread* t, void* ip) +{ + GcWordArray* a = compileRoots(t)->virtualThunks(); + for (unsigned i = 0; i < a->length(); i += 2) { + uintptr_t start = a->body()[i]; + uintptr_t end = start + a->body()[i + 1]; + + if (reinterpret_cast(ip) >= start + and reinterpret_cast(ip) < end) { + return true; + } + } + + return false; +} + +bool isThunkUnsafeStack(MyThread* t, void* ip) +{ + MyProcessor* p = processor(t); + + return isThunk(t, ip) and (isThunkUnsafeStack(&(p->thunks), ip) + or isThunkUnsafeStack(&(p->bootThunks), ip)); +} + +GcCallNode* findCallNode(MyThread* t, void* address) +{ + if (DebugCallTable) { + fprintf(stderr, "find call node %p\n", address); + } + + // we must use a version of the call table at least as recent as the + // compiled form of the method containing the specified address (see + // compile(MyThread*, Allocator*, BootContext*, object)): + loadMemoryBarrier(); + + GcArray* table = compileRoots(t)->callTable(); + + intptr_t key = reinterpret_cast(address); + unsigned index = static_cast(key) & (table->length() - 1); + + for (GcCallNode* n = cast(t, table->body()[index]); n; + n = n->next()) { + intptr_t k = n->address(); + + if (k == key) { + return n; + } + } + + return 0; +} + +GcArray* resizeTable(MyThread* t, GcArray* oldTable, unsigned newLength) +{ + PROTECT(t, oldTable); + + GcCallNode* oldNode = 0; + PROTECT(t, oldNode); + + GcArray* newTable = makeArray(t, newLength); + PROTECT(t, newTable); + + for (unsigned i = 0; i < oldTable->length(); ++i) { + for (oldNode = cast(t, oldTable->body()[i]); oldNode; + oldNode = oldNode->next()) { + intptr_t k = oldNode->address(); + + unsigned index = k & (newLength - 1); + + GcCallNode* newNode + = makeCallNode(t, + oldNode->address(), + oldNode->target(), + oldNode->flags(), + cast(t, newTable->body()[index])); + + newTable->setBodyElement(t, index, newNode); + } + } + + return newTable; +} + +GcArray* insertCallNode(MyThread* t, + GcArray* table, + unsigned* size, + GcCallNode* node) +{ + if (DebugCallTable) { + fprintf(stderr, + "insert call node %p\n", + reinterpret_cast(node->address())); + } + + PROTECT(t, table); + PROTECT(t, node); + + ++(*size); + + if (*size >= table->length() * 2) { + table = resizeTable(t, table, table->length() * 2); + } + + intptr_t key = node->address(); + unsigned index = static_cast(key) & (table->length() - 1); + + node->setNext(t, cast(t, table->body()[index])); + table->setBodyElement(t, index, node); + + return table; +} + +GcHashMap* makeClassMap(Thread* t, + unsigned* table, + unsigned count, + uintptr_t* heap) +{ + GcArray* array = makeArray(t, nextPowerOfTwo(count)); + GcHashMap* map = makeHashMap(t, 0, array); + PROTECT(t, map); + + for (unsigned i = 0; i < count; ++i) { + GcClass* c = cast(t, bootObject(heap, table[i])); + hashMapInsert(t, map, c->name(), c, byteArrayHash); + } + + return map; +} + +GcArray* makeStaticTableArray(Thread* t, + unsigned* bootTable, + unsigned bootCount, + unsigned* appTable, + unsigned appCount, + uintptr_t* heap) +{ + GcArray* array = makeArray(t, bootCount + appCount); + + for (unsigned i = 0; i < bootCount; ++i) { + array->setBodyElement( + t, i, cast(t, bootObject(heap, bootTable[i]))->staticTable()); + } + + for (unsigned i = 0; i < appCount; ++i) { + array->setBodyElement( + t, + (bootCount + i), + cast(t, bootObject(heap, appTable[i]))->staticTable()); + } + + return array; +} + +GcHashMap* makeStringMap(Thread* t, + unsigned* table, + unsigned count, + uintptr_t* heap) +{ + GcArray* array = makeArray(t, nextPowerOfTwo(count)); + GcHashMap* map = makeWeakHashMap(t, 0, array)->as(t); + PROTECT(t, map); + + for (unsigned i = 0; i < count; ++i) { + object s = bootObject(heap, table[i]); + hashMapInsert(t, map, s, 0, stringHash); + } + + return map; +} + +GcArray* makeCallTable(MyThread* t, + uintptr_t* heap, + unsigned* calls, + unsigned count, + uintptr_t base) +{ + GcArray* table = makeArray(t, nextPowerOfTwo(count)); + PROTECT(t, table); + + unsigned size = 0; + for (unsigned i = 0; i < count; ++i) { + unsigned address = calls[i * 2]; + unsigned target = calls[(i * 2) + 1]; + + GcCallNode* node + = makeCallNode(t, + base + address, + cast(t, bootObject(heap, target & BootMask)), + target >> BootShift, + 0); + + table = insertCallNode(t, table, &size, node); + } + + return table; +} + +void fixupHeap(MyThread* t UNUSED, + uintptr_t* map, + unsigned size, + uintptr_t* heap) +{ + for (unsigned word = 0; word < size; ++word) { + uintptr_t w = map[word]; + if (w) { + for (unsigned bit = 0; bit < BitsPerWord; ++bit) { + if (w & (static_cast(1) << bit)) { + unsigned index = indexOf(word, bit); + + uintptr_t* p = heap + index; + assertT(t, *p); + + uintptr_t number = *p & BootMask; + uintptr_t mark = *p >> BootShift; + + if (number) { + *p = reinterpret_cast(heap + (number - 1)) | mark; + if (false) { + fprintf(stderr, + "fixup %d: %d 0x%x\n", + index, + static_cast(number), + static_cast(*p)); + } + } else { + *p = mark; + } + } + } + } + } +} + +void resetClassRuntimeState(Thread* t, + GcClass* c, + uintptr_t* heap, + unsigned heapSize) +{ + c->runtimeDataIndex() = 0; + + if (c->arrayElementSize() == 0) { + GcSingleton* staticTable = c->staticTable()->as(t); + if (staticTable) { + for (unsigned i = 0; i < singletonCount(t, staticTable); ++i) { + if (singletonIsObject(t, staticTable, i) + and (reinterpret_cast( + singletonObject(t, staticTable, i)) < heap + or reinterpret_cast(singletonObject( + t, staticTable, i)) > heap + heapSize)) { + singletonObject(t, staticTable, i) = 0; + } + } + } + } + + if (GcArray* mtable = cast(t, c->methodTable())) { + PROTECT(t, mtable); + for (unsigned i = 0; i < mtable->length(); ++i) { + GcMethod* m = cast(t, mtable->body()[i]); + + m->nativeID() = 0; + m->runtimeDataIndex() = 0; + + if (m->vmFlags() & ClassInitFlag) { + c->vmFlags() |= NeedInitFlag; + c->vmFlags() &= ~InitErrorFlag; + } + } + } + + t->m->processor->initVtable(t, c); +} + +void resetRuntimeState(Thread* t, + GcHashMap* map, + uintptr_t* heap, + unsigned heapSize) +{ + for (HashMapIterator it(t, map); it.hasMore();) { + resetClassRuntimeState( + t, cast(t, it.next()->second()), heap, heapSize); + } +} + +void fixupMethods(Thread* t, + GcHashMap* map, + BootImage* image UNUSED, + uint8_t* code) +{ + for (HashMapIterator it(t, map); it.hasMore();) { + GcClass* c = cast(t, it.next()->second()); + + 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 (method->code()) { + assertT(t, + methodCompiled(t, method) + <= static_cast(image->codeSize)); + + method->code()->compiled() = methodCompiled(t, method) + + reinterpret_cast(code); + + if (DebugCompile) { + logCompile(static_cast(t), + reinterpret_cast(methodCompiled(t, method)), + methodCompiledSize(t, method), + reinterpret_cast( + method->class_()->name()->body().begin()), + reinterpret_cast(method->name()->body().begin()), + reinterpret_cast(method->spec()->body().begin())); + } + } + } + } + + t->m->processor->initVtable(t, c); + } +} + +MyProcessor::Thunk thunkToThunk(const BootImage::Thunk& thunk, uint8_t* base) +{ + return MyProcessor::Thunk( + base + thunk.start, thunk.frameSavedOffset, thunk.length); +} + +void findThunks(MyThread* t, BootImage* image, uint8_t* code) +{ + MyProcessor* p = processor(t); + + p->bootThunks.default_ = thunkToThunk(image->thunks.default_, code); + p->bootThunks.defaultVirtual = thunkToThunk(image->thunks.defaultVirtual, code); + p->bootThunks.defaultDynamic = thunkToThunk(image->thunks.defaultDynamic, code); + p->bootThunks.native = thunkToThunk(image->thunks.native, code); + p->bootThunks.aioob = thunkToThunk(image->thunks.aioob, code); + p->bootThunks.stackOverflow = thunkToThunk(image->thunks.stackOverflow, code); + p->bootThunks.table = thunkToThunk(image->thunks.table, code); +} + +void fixupVirtualThunks(MyThread* t, uint8_t* code) +{ + GcWordArray* a = compileRoots(t)->virtualThunks(); + for (unsigned i = 0; i < a->length(); i += 2) { + if (a->body()[i]) { + a->body()[i] += reinterpret_cast(code); + } + } +} + +void boot(MyThread* t, BootImage* image, uint8_t* code) +{ + assertT(t, image->magic == BootImage::Magic); + + unsigned* bootClassTable = reinterpret_cast(image + 1); + unsigned* appClassTable = bootClassTable + image->bootClassCount; + unsigned* stringTable = appClassTable + image->appClassCount; + unsigned* callTable = stringTable + image->stringCount; + + uintptr_t* heapMap = reinterpret_cast( + padWord(reinterpret_cast(callTable + (image->callCount * 2)))); + + unsigned heapMapSizeInWords + = ceilingDivide(heapMapSize(image->heapSize), BytesPerWord); + uintptr_t* heap = heapMap + heapMapSizeInWords; + + MyProcessor* p = static_cast(t->m->processor); + + t->heapImage = p->heapImage = heap; + + if (false) { + fprintf(stderr, + "heap from %p to %p\n", + heap, + heap + ceilingDivide(image->heapSize, BytesPerWord)); + } + + t->codeImage = p->codeImage = code; + p->codeImageSize = image->codeSize; + + if (false) { + fprintf(stderr, "code from %p to %p\n", code, code + image->codeSize); + } + + if (not image->initialized) { + fixupHeap(t, heapMap, heapMapSizeInWords, heap); + } + + t->m->heap->setImmortalHeap(heap, image->heapSize / BytesPerWord); + + t->m->types = reinterpret_cast(bootObject(heap, image->types)); + + t->m->roots = GcRoots::makeZeroed(t); + + roots(t)->setBootLoader( + t, cast(t, bootObject(heap, image->bootLoader))); + roots(t)->setAppLoader( + t, cast(t, bootObject(heap, image->appLoader))); + + p->roots = GcCompileRoots::makeZeroed(t); + + compileRoots(t)->setMethodTree( + t, cast(t, bootObject(heap, image->methodTree))); + compileRoots(t)->setMethodTreeSentinal( + t, cast(t, bootObject(heap, image->methodTreeSentinal))); + + compileRoots(t)->setVirtualThunks( + t, cast(t, bootObject(heap, image->virtualThunks))); + + { + GcHashMap* map + = makeClassMap(t, bootClassTable, image->bootClassCount, heap); + // sequence point, for gc (don't recombine statements) + roots(t)->bootLoader()->setMap(t, map); + } + + roots(t)->bootLoader()->as(t)->finder() + = t->m->bootFinder; + + { + GcHashMap* map = makeClassMap(t, appClassTable, image->appClassCount, heap); + // sequence point, for gc (don't recombine statements) + roots(t)->appLoader()->setMap(t, map); + } + + roots(t)->appLoader()->as(t)->finder() = t->m->appFinder; + + { + GcHashMap* map = makeStringMap(t, stringTable, image->stringCount, heap); + // sequence point, for gc (don't recombine statements) + roots(t)->setStringMap(t, map); + } + + p->callTableSize = image->callCount; + + { + GcArray* ct = makeCallTable(t, + heap, + callTable, + image->callCount, + reinterpret_cast(code)); + // sequence point, for gc (don't recombine statements) + compileRoots(t)->setCallTable(t, ct); + } + + { + GcArray* staticTableArray = makeStaticTableArray(t, + bootClassTable, + image->bootClassCount, + appClassTable, + image->appClassCount, + heap); + // sequence point, for gc (don't recombine statements) + compileRoots(t)->setStaticTableArray(t, staticTableArray); + } + + findThunks(t, image, code); + + if (image->initialized) { + resetRuntimeState(t, + cast(t, roots(t)->bootLoader()->map()), + heap, + image->heapSize); + + resetRuntimeState(t, + cast(t, roots(t)->appLoader()->map()), + heap, + image->heapSize); + + for (unsigned i = 0; i < t->m->types->length(); ++i) { + resetClassRuntimeState( + t, type(t, static_cast(i)), heap, image->heapSize); + } + } else { + fixupVirtualThunks(t, code); + + fixupMethods( + t, cast(t, roots(t)->bootLoader()->map()), image, code); + + fixupMethods( + t, cast(t, roots(t)->appLoader()->map()), image, code); + } + + image->initialized = true; + + GcHashMap* map = makeHashMap(t, 0, 0); + // sequence point, for gc (don't recombine statements) + roots(t)->setBootstrapClassMap(t, map); +} + +intptr_t getThunk(MyThread* t, Thunk thunk) +{ + MyProcessor* p = processor(t); + + return reinterpret_cast(p->thunks.table.start + + (thunk * p->thunks.table.length)); +} + +#ifndef AVIAN_AOT_ONLY +void insertCallNode(MyThread* t, GcCallNode* node) +{ + GcArray* newArray = insertCallNode( + t, compileRoots(t)->callTable(), &(processor(t)->callTableSize), node); + // sequence point, for gc (don't recombine statements) + compileRoots(t)->setCallTable(t, newArray); +} + +BootImage::Thunk thunkToThunk(const MyProcessor::Thunk& thunk, uint8_t* base) +{ + return BootImage::Thunk( + thunk.start - base, thunk.frameSavedOffset, thunk.length); +} + +using avian::codegen::OperandInfo; +namespace lir = avian::codegen::lir; + +void compileCall(MyThread* t, Context* c, ThunkIndex index, bool call = true) +{ + avian::codegen::Assembler* a = c->assembler; + + if (processor(t)->bootImage) { + lir::Memory table(t->arch->thread(), TARGET_THREAD_THUNKTABLE); + lir::RegisterPair scratch(t->arch->scratch()); + a->apply(lir::Move, + OperandInfo(TargetBytesPerWord, lir::Operand::Type::Memory, &table), + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &scratch)); + lir::Memory proc(scratch.low, index * TargetBytesPerWord); + a->apply(lir::Move, + OperandInfo(TargetBytesPerWord, lir::Operand::Type::Memory, &proc), + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &scratch)); + a->apply(call ? lir::Call : lir::Jump, + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &scratch)); + } else { + lir::Constant proc(new (&c->zone) avian::codegen::ResolvedPromise( + reinterpret_cast(t->thunkTable[index]))); + + a->apply(call ? lir::LongCall : lir::LongJump, + OperandInfo(TargetBytesPerWord, lir::Operand::Type::Constant, &proc)); + } +} + +void compileDefaultThunk(MyThread* t, + FixedAllocator* allocator, + MyProcessor::Thunk* thunk, + const char* name, + ThunkIndex thunkIndex, + bool hasTarget) +{ + Context context(t); + avian::codegen::Assembler* a = context.assembler; + + if(hasTarget) { + lir::RegisterPair class_(t->arch->virtualCallTarget()); + lir::Memory virtualCallTargetSrc( + t->arch->stack(), + (t->arch->frameFooterSize() + t->arch->frameReturnAddressSize()) + * TargetBytesPerWord); + + a->apply(lir::Move, + OperandInfo( + TargetBytesPerWord, lir::Operand::Type::Memory, &virtualCallTargetSrc), + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &class_)); + + lir::Memory virtualCallTargetDst(t->arch->thread(), + TARGET_THREAD_VIRTUALCALLTARGET); + + a->apply( + lir::Move, + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &class_), + OperandInfo( + TargetBytesPerWord, lir::Operand::Type::Memory, &virtualCallTargetDst)); + } + + lir::RegisterPair index(t->arch->virtualCallIndex()); + lir::Memory virtualCallIndex(t->arch->thread(), + TARGET_THREAD_VIRTUALCALLINDEX); + + a->apply( + lir::Move, + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &index), + OperandInfo(TargetBytesPerWord, lir::Operand::Type::Memory, &virtualCallIndex)); + + a->saveFrame(TARGET_THREAD_STACK, TARGET_THREAD_IP); + + thunk->frameSavedOffset = a->length(); + + lir::RegisterPair thread(t->arch->thread()); + a->pushFrame(1, TargetBytesPerWord, lir::Operand::Type::RegisterPair, &thread); + + compileCall(t, &context, thunkIndex); + + a->popFrame(t->arch->alignFrameSize(1)); + + lir::RegisterPair result(t->arch->returnLow()); + a->apply(lir::Jump, + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &result)); + + thunk->length = a->endBlock(false)->resolve(0, 0); + + thunk->start = finish( + t, allocator, a, name, thunk->length); +} + +void compileThunks(MyThread* t, FixedAllocator* allocator) +{ + MyProcessor* p = processor(t); + + { + Context context(t); + avian::codegen::Assembler* a = context.assembler; + + a->saveFrame(TARGET_THREAD_STACK, TARGET_THREAD_IP); + + p->thunks.default_.frameSavedOffset = a->length(); + + lir::RegisterPair thread(t->arch->thread()); + a->pushFrame(1, TargetBytesPerWord, lir::Operand::Type::RegisterPair, &thread); + + compileCall(t, &context, compileMethodIndex); + + a->popFrame(t->arch->alignFrameSize(1)); + + lir::RegisterPair result(t->arch->returnLow()); + a->apply(lir::Jump, + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &result)); + + p->thunks.default_.length = a->endBlock(false)->resolve(0, 0); + + p->thunks.default_.start + = finish(t, allocator, a, "default", p->thunks.default_.length); + } + + compileDefaultThunk + (t, allocator, &(p->thunks.defaultVirtual), "defaultVirtual", + compileVirtualMethodIndex, true); + + compileDefaultThunk + (t, allocator, &(p->thunks.defaultDynamic), "defaultDynamic", + linkDynamicMethodIndex, false); + + { + Context context(t); + avian::codegen::Assembler* a = context.assembler; + + a->saveFrame(TARGET_THREAD_STACK, TARGET_THREAD_IP); + + p->thunks.native.frameSavedOffset = a->length(); + + lir::RegisterPair thread(t->arch->thread()); + a->pushFrame(1, TargetBytesPerWord, lir::Operand::Type::RegisterPair, &thread); + + compileCall(t, &context, invokeNativeIndex); + + a->popFrameAndUpdateStackAndReturn(t->arch->alignFrameSize(1), + TARGET_THREAD_NEWSTACK); + + p->thunks.native.length = a->endBlock(false)->resolve(0, 0); + + p->thunks.native.start + = finish(t, allocator, a, "native", p->thunks.native.length); + } + + { + Context context(t); + avian::codegen::Assembler* a = context.assembler; + + a->saveFrame(TARGET_THREAD_STACK, TARGET_THREAD_IP); + + p->thunks.aioob.frameSavedOffset = a->length(); + + lir::RegisterPair thread(t->arch->thread()); + a->pushFrame(1, TargetBytesPerWord, lir::Operand::Type::RegisterPair, &thread); + + compileCall(t, &context, throwArrayIndexOutOfBoundsIndex); + + p->thunks.aioob.length = a->endBlock(false)->resolve(0, 0); + + p->thunks.aioob.start + = finish(t, allocator, a, "aioob", p->thunks.aioob.length); + } + + { + Context context(t); + avian::codegen::Assembler* a = context.assembler; + + a->saveFrame(TARGET_THREAD_STACK, TARGET_THREAD_IP); + + p->thunks.stackOverflow.frameSavedOffset = a->length(); + + lir::RegisterPair thread(t->arch->thread()); + a->pushFrame(1, TargetBytesPerWord, lir::Operand::Type::RegisterPair, &thread); + + compileCall(t, &context, throwStackOverflowIndex); + + p->thunks.stackOverflow.length = a->endBlock(false)->resolve(0, 0); + + p->thunks.stackOverflow.start = finish( + t, allocator, a, "stackOverflow", p->thunks.stackOverflow.length); + } + + { + { + Context context(t); + avian::codegen::Assembler* a = context.assembler; + + a->saveFrame(TARGET_THREAD_STACK, TARGET_THREAD_IP); + + p->thunks.table.frameSavedOffset = a->length(); + + compileCall(t, &context, dummyIndex, false); + + p->thunks.table.length = a->endBlock(false)->resolve(0, 0); + + p->thunks.table.start = static_cast(allocator->allocate( + p->thunks.table.length * ThunkCount, TargetBytesPerWord)); + } + + uint8_t* start = p->thunks.table.start; + +#define THUNK(s) \ + { \ + Context context(t); \ + avian::codegen::Assembler* a = context.assembler; \ + \ + a->saveFrame(TARGET_THREAD_STACK, TARGET_THREAD_IP); \ + \ + p->thunks.table.frameSavedOffset = a->length(); \ + \ + compileCall(t, &context, s##Index, false); \ + \ + expect(t, a->endBlock(false)->resolve(0, 0) <= p->thunks.table.length); \ + \ + a->setDestination(start); \ + a->write(); \ + \ + logCompile(t, start, p->thunks.table.length, 0, #s, 0); \ + \ + start += p->thunks.table.length; \ + } +#include "thunks.cpp" +#undef THUNK + } + + BootImage* image = p->bootImage; + + if (image) { + uint8_t* imageBase = p->codeAllocator.memory.begin(); + + image->thunks.default_ = thunkToThunk(p->thunks.default_, imageBase); + image->thunks.defaultVirtual + = thunkToThunk(p->thunks.defaultVirtual, imageBase); + image->thunks.native = thunkToThunk(p->thunks.native, imageBase); + image->thunks.aioob = thunkToThunk(p->thunks.aioob, imageBase); + image->thunks.stackOverflow + = thunkToThunk(p->thunks.stackOverflow, imageBase); + image->thunks.table = thunkToThunk(p->thunks.table, imageBase); + } +} + +uintptr_t aioobThunk(MyThread* t) +{ + return reinterpret_cast(processor(t)->thunks.aioob.start); +} + +uintptr_t stackOverflowThunk(MyThread* t) +{ + return reinterpret_cast(processor(t)->thunks.stackOverflow.start); +} +#endif // not AVIAN_AOT_ONLY + +MyProcessor* processor(MyThread* t) +{ + return static_cast(t->m->processor); +} + +uintptr_t defaultThunk(MyThread* t) +{ + return reinterpret_cast(processor(t)->thunks.default_.start); +} + +uintptr_t bootDefaultThunk(MyThread* t) +{ + return reinterpret_cast(processor(t)->bootThunks.default_.start); +} + +uintptr_t defaultVirtualThunk(MyThread* t) +{ + return reinterpret_cast(processor(t)->thunks.defaultVirtual.start); +} + +uintptr_t defaultDynamicThunk(MyThread* t) +{ + return reinterpret_cast(processor(t)->thunks.defaultDynamic.start); +} + +uintptr_t nativeThunk(MyThread* t) +{ + return reinterpret_cast(processor(t)->thunks.native.start); +} + +uintptr_t bootNativeThunk(MyThread* t) +{ + return reinterpret_cast(processor(t)->bootThunks.native.start); +} + +bool unresolved(MyThread* t, uintptr_t methodAddress) +{ + return methodAddress == defaultThunk(t) + or methodAddress == bootDefaultThunk(t); +} + +uintptr_t compileVirtualThunk(MyThread* t, + unsigned index, + unsigned* size, + uintptr_t thunk, + const char* baseName) +{ + Context context(t); + avian::codegen::Assembler* a = context.assembler; + + avian::codegen::ResolvedPromise indexPromise(index); + lir::Constant indexConstant(&indexPromise); + lir::RegisterPair indexRegister(t->arch->virtualCallIndex()); + a->apply( + lir::Move, + OperandInfo(TargetBytesPerWord, lir::Operand::Type::Constant, &indexConstant), + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &indexRegister)); + + avian::codegen::ResolvedPromise promise(thunk); + lir::Constant target(&promise); + a->apply(lir::Jump, + OperandInfo(TargetBytesPerWord, lir::Operand::Type::Constant, &target)); + + *size = a->endBlock(false)->resolve(0, 0); + + uint8_t* start = static_cast( + codeAllocator(t)->allocate(*size, TargetBytesPerWord)); + + a->setDestination(start); + a->write(); + + const size_t virtualThunkBaseNameLength = strlen(baseName); + const size_t maxIntStringLength = 10; + + THREAD_RUNTIME_ARRAY(t, + char, + virtualThunkName, + virtualThunkBaseNameLength + maxIntStringLength); + + sprintf(RUNTIME_ARRAY_BODY(virtualThunkName), + "%s%d", + baseName, + index); + + logCompile(t, start, *size, 0, RUNTIME_ARRAY_BODY(virtualThunkName), 0); + + return reinterpret_cast(start); +} + +uintptr_t virtualThunk(MyThread* t, unsigned index) +{ + ACQUIRE(t, t->m->classLock); + + GcWordArray* oldArray = compileRoots(t)->virtualThunks(); + if (oldArray == 0 or oldArray->length() <= index * 2) { + GcWordArray* newArray = makeWordArray(t, nextPowerOfTwo((index + 1) * 2)); + if (compileRoots(t)->virtualThunks()) { + memcpy(newArray->body().begin(), + oldArray->body().begin(), + oldArray->length() * BytesPerWord); + } + compileRoots(t)->setVirtualThunks(t, newArray); + oldArray = newArray; + } + + if (oldArray->body()[index * 2] == 0) { + unsigned size; + uintptr_t thunk = compileVirtualThunk(t, index, &size, defaultVirtualThunk(t), "virtualThunk"); + oldArray->body()[index * 2] = thunk; + oldArray->body()[(index * 2) + 1] = size; + } + + return oldArray->body()[index * 2]; +} + +void compile(MyThread* t, + FixedAllocator* allocator UNUSED, + BootContext* bootContext, + GcMethod* method) +{ + PROTECT(t, method); + + if (bootContext == 0 and method->flags() & ACC_STATIC) { + initClass(t, method->class_()); + } + + if (methodAddress(t, method) != defaultThunk(t)) { + return; + } + + assertT(t, (method->flags() & ACC_NATIVE) == 0); + +#ifdef AVIAN_AOT_ONLY + abort(t); +#else + + // We must avoid acquiring any locks until after the first pass of + // compilation, since this pass may trigger classloading operations + // involving application classloaders and thus the potential for + // deadlock. To make this safe, we use a private clone of the + // method so that we won't be confused if another thread updates the + // original while we're working. + + GcMethod* clone = methodClone(t, method); + + loadMemoryBarrier(); + + if (methodAddress(t, method) != defaultThunk(t)) { + return; + } + + PROTECT(t, clone); + + Context context(t, bootContext, clone); + compile(t, &context); + + { + GcExceptionHandlerTable* ehTable = cast( + t, clone->code()->exceptionHandlerTable()); + + if (ehTable) { + PROTECT(t, ehTable); + + // resolve all exception handler catch types before we acquire + // the class lock: + for (unsigned i = 0; i < ehTable->length(); ++i) { + uint64_t handler = ehTable->body()[i]; + if (exceptionHandlerCatchType(handler)) { + resolveClassInPool(t, clone, exceptionHandlerCatchType(handler) - 1); + } + } + } + } + + ACQUIRE(t, t->m->classLock); + + if (methodAddress(t, method) != defaultThunk(t)) { + return; + } + + finish(t, allocator, &context); + + if (DebugMethodTree) { + fprintf(stderr, + "insert method at %p\n", + reinterpret_cast(methodCompiled(t, clone))); + } + + // We can't update the MethodCode field on the original method + // before it is placed into the method tree, since another thread + // might call the method, from which stack unwinding would fail + // (since there is not yet an entry in the method tree). However, + // we can't insert the original method into the tree before updating + // the MethodCode field on it since we rely on that field to + // determine its position in the tree. Therefore, we insert the + // clone in its place. Later, we'll replace the clone with the + // original to save memory. + + GcTreeNode* newTree = treeInsert(t, + &(context.zone), + compileRoots(t)->methodTree(), + methodCompiled(t, clone), + clone, + compileRoots(t)->methodTreeSentinal(), + compareIpToMethodBounds); + // sequence point, for gc (don't recombine statements) + compileRoots(t)->setMethodTree(t, newTree); + + storeStoreMemoryBarrier(); + + method->setCode(t, clone->code()); + + if (methodVirtual(t, method)) { + method->class_()->vtable()[method->offset()] + = reinterpret_cast(methodCompiled(t, clone)); + } + + // we've compiled the method and inserted it into the tree without + // error, so we ensure that the executable area not be deallocated + // when we dispose of the context: + context.executableAllocator = 0; + + treeUpdate(t, + compileRoots(t)->methodTree(), + methodCompiled(t, clone), + method, + compileRoots(t)->methodTreeSentinal(), + compareIpToMethodBounds); +#endif // not AVIAN_AOT_ONLY +} + +GcCompileRoots* compileRoots(Thread* t) +{ + return processor(static_cast(t))->roots; +} + +avian::util::FixedAllocator* codeAllocator(MyThread* t) +{ + return &(processor(t)->codeAllocator); +} + +Allocator* allocator(MyThread* t) +{ + return processor(t)->allocator; +} + +} // namespace local + +} // namespace + +namespace vm { + +Processor* makeProcessor(System* system, + Allocator* allocator, + const char* crashDumpDirectory, + bool useNativeFeatures) +{ + return new (allocator->allocate(sizeof(local::MyProcessor))) + local::MyProcessor( + system, allocator, crashDumpDirectory, useNativeFeatures); +} + +} // namespace vm diff --git a/sgx-jvm/avian/src/continuations-x86.S b/sgx-jvm/avian/src/continuations-x86.S new file mode 100644 index 0000000000..3304eddfc1 --- /dev/null +++ b/sgx-jvm/avian/src/continuations-x86.S @@ -0,0 +1,184 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifdef __x86_64__ + +#define CONTINUATION_NEXT 8 +#define CONTINUATION_ADDRESS 32 +#define CONTINUATION_RETURN_ADDRESS_OFFSET 40 +#define CONTINUATION_FRAME_POINTER_OFFSET 48 +#define CONTINUATION_LENGTH 56 +#define CONTINUATION_BODY 64 + + // call the next continuation, if any + movq TARGET_THREAD_CONTINUATION(%rbx),%rcx + cmpq $0,%rcx + je LOCAL(vmInvoke_exit) + + // allocate a frame of size (continuation.length * BYTES_PER_WORD) + // + CALLEE_SAVED_REGISTER_FOOTPRINT + movq CONTINUATION_LENGTH(%rcx),%rsi + shlq $3,%rsi + subq %rsi,%rsp + subq $CALLEE_SAVED_REGISTER_FOOTPRINT,%rsp + + // copy the continuation body into the frame + leaq CONTINUATION_BODY(%rcx),%rdi + + movq $0,%r9 + jmp LOCAL(vmInvoke_continuationTest) + +LOCAL(vmInvoke_continuationLoop): + movq (%rdi,%r9,1),%r8 + movq %r8,(%rsp,%r9,1) + addq $8,%r9 + +LOCAL(vmInvoke_continuationTest): + cmpq %rsi,%r9 + jb LOCAL(vmInvoke_continuationLoop) + + // set the return address to vmInvoke_returnAddress + movq CONTINUATION_RETURN_ADDRESS_OFFSET(%rcx),%rdi +#if defined __MINGW32__ || defined __CYGWIN32__ + leaq GLOBAL(vmInvoke_returnAddress)(%rip),%r10 +#else + movq GLOBAL(vmInvoke_returnAddress)@GOTPCREL(%rip),%r10 +#endif + movq %r10,(%rsp,%rdi,1) + +#ifdef AVIAN_USE_FRAME_POINTER + // save the current base pointer in the frame and update it + movq CONTINUATION_FRAME_POINTER_OFFSET(%rcx),%rdi + movq %rbp,(%rsp,%rdi,1) + addq %rsp,%rdi + movq %rdi,%rbp +#endif + + // consume the continuation + movq CONTINUATION_NEXT(%rcx),%rdi + movq %rdi,TARGET_THREAD_CONTINUATION(%rbx) + + // call the continuation unless we're handling an exception + movq TARGET_THREAD_EXCEPTION(%rbx),%rsi + cmpq $0,%rsi + jne LOCAL(vmInvoke_handleException) + jmp *CONTINUATION_ADDRESS(%rcx) + +LOCAL(vmInvoke_handleException): + // we're handling an exception - call the exception handler instead + movq $0,TARGET_THREAD_EXCEPTION(%rbx) + movq TARGET_THREAD_EXCEPTIONSTACKADJUSTMENT(%rbx),%rdi + subq %rdi,%rsp + movq TARGET_THREAD_EXCEPTIONOFFSET(%rbx),%rdi + movq %rsi,(%rsp,%rdi,1) + + jmp *TARGET_THREAD_EXCEPTIONHANDLER(%rbx) + +LOCAL(vmInvoke_exit): + +#elif defined __i386__ + +#define CONTINUATION_NEXT 4 +#define CONTINUATION_ADDRESS 16 +#define CONTINUATION_RETURN_ADDRESS_OFFSET 20 +#define CONTINUATION_FRAME_POINTER_OFFSET 24 +#define CONTINUATION_LENGTH 28 +#define CONTINUATION_BODY 32 + +#ifdef AVIAN_USE_FRAME_POINTER +# define CONTINUATION_ALIGNMENT_PADDING 8 +#else +# define CONTINUATION_ALIGNMENT_PADDING 12 +#endif + + // call the next continuation, if any + movl TARGET_THREAD_CONTINUATION(%ebx),%ecx + cmpl $0,%ecx + je LOCAL(vmInvoke_exit) + + // allocate a frame of size (continuation.length * BYTES_PER_WORD), + // plus stack alignment padding + movl CONTINUATION_LENGTH(%ecx),%esi + shll $2,%esi + leal CONTINUATION_ALIGNMENT_PADDING(%esi),%esi + subl %esi,%esp + + // copy the continuation body into the frame + leal CONTINUATION_BODY(%ecx),%edi + + push %eax + push %edx + + movl $0,%edx + jmp LOCAL(vmInvoke_continuationTest) + +LOCAL(vmInvoke_continuationLoop): + movl (%edi,%edx,1),%eax + movl %eax,8(%esp,%edx,1) + addl $4,%edx + +LOCAL(vmInvoke_continuationTest): + cmpl %esi,%edx + jb LOCAL(vmInvoke_continuationLoop) + + pop %edx + pop %eax + + // set the return address to vmInvoke_returnAddress + movl CONTINUATION_RETURN_ADDRESS_OFFSET(%ecx),%edi +#if defined __MINGW32__ || defined __CYGWIN32__ + movl $GLOBAL(vmInvoke_returnAddress),%esi +#else + call LOCAL(getPC) +# if defined __APPLE__ +LOCAL(vmInvoke_offset): + leal GLOBAL(vmInvoke_returnAddress)-LOCAL(vmInvoke_offset)(%esi),%esi +# else + addl $_GLOBAL_OFFSET_TABLE_,%esi + movl GLOBAL(vmInvoke_returnAddress)@GOT(%esi),%esi +# endif +#endif + movl %esi,(%esp,%edi,1) + +#ifdef AVIAN_USE_FRAME_POINTER + // save the current base pointer in the frame and update it + movl CONTINUATION_FRAME_POINTER_OFFSET(%ecx),%edi + movl %ebp,(%esp,%edi,1) + addl %esp,%edi + movl %edi,%ebp +#endif + + // consume the continuation + movl CONTINUATION_NEXT(%ecx),%edi + movl %edi,TARGET_THREAD_CONTINUATION(%ebx) + + // call the continuation unless we're handling an exception + movl TARGET_THREAD_EXCEPTION(%ebx),%esi + cmpl $0,%esi + jne LOCAL(vmInvoke_handleException) + + jmp *CONTINUATION_ADDRESS(%ecx) + +LOCAL(vmInvoke_handleException): + // we're handling an exception - call the exception handler instead + movl $0,TARGET_THREAD_EXCEPTION(%ebx) + movl TARGET_THREAD_EXCEPTIONSTACKADJUSTMENT(%ebx),%edi + subl %edi,%esp + movl TARGET_THREAD_EXCEPTIONOFFSET(%ebx),%edi + movl %esi,(%esp,%edi,1) + + jmp *TARGET_THREAD_EXCEPTIONHANDLER(%ebx) + +LOCAL(vmInvoke_exit): + +#else +# error unsupported architecture +#endif + diff --git a/sgx-jvm/avian/src/debug-util.cpp b/sgx-jvm/avian/src/debug-util.cpp new file mode 100644 index 0000000000..0cab3e58e8 --- /dev/null +++ b/sgx-jvm/avian/src/debug-util.cpp @@ -0,0 +1,612 @@ +/* Copyright (c) 2008-2013, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include + +#include "debug-util.h" + +namespace avian { +namespace jvm { +namespace debug { + +uint16_t read16(uint8_t* code, unsigned& ip) +{ + uint16_t a = code[ip++]; + uint16_t b = code[ip++]; + return (a << 8) | b; +} + +uint32_t read32(uint8_t* code, unsigned& ip) +{ + uint32_t b = code[ip++]; + uint32_t a = code[ip++]; + uint32_t c = code[ip++]; + uint32_t d = code[ip++]; + return (a << 24) | (b << 16) | (c << 8) | d; +} + +using namespace vm; + +int printInstruction(uint8_t* code, unsigned& ip, const char* prefix) +{ + unsigned startIp = ip; + + uint8_t instr = code[ip++]; + switch (instr) { + case aaload: + return fprintf(stderr, "aaload"); + case aastore: + return fprintf(stderr, "aastore"); + + case aconst_null: + return fprintf(stderr, "aconst_null"); + + case aload: + return fprintf(stderr, "aload %2d", code[ip++]); + case aload_0: + return fprintf(stderr, "aload_0"); + case aload_1: + return fprintf(stderr, "aload_1"); + case aload_2: + return fprintf(stderr, "aload_2"); + case aload_3: + return fprintf(stderr, "aload_3"); + + case anewarray: + return fprintf(stderr, "anewarray %4d", read16(code, ip)); + case areturn: + return fprintf(stderr, "areturn"); + case arraylength: + return fprintf(stderr, "arraylength"); + + case astore: + return fprintf(stderr, "astore %2d", code[ip++]); + case astore_0: + return fprintf(stderr, "astore_0"); + case astore_1: + return fprintf(stderr, "astore_1"); + case astore_2: + return fprintf(stderr, "astore_2"); + case astore_3: + return fprintf(stderr, "astore_3"); + + case athrow: + return fprintf(stderr, "athrow"); + case baload: + return fprintf(stderr, "baload"); + case bastore: + return fprintf(stderr, "bastore"); + + case bipush: + return fprintf(stderr, "bipush %2d", code[ip++]); + case caload: + return fprintf(stderr, "caload"); + case castore: + return fprintf(stderr, "castore"); + case checkcast: + return fprintf(stderr, "checkcast %4d", read16(code, ip)); + case d2f: + return fprintf(stderr, "d2f"); + case d2i: + return fprintf(stderr, "d2i"); + case d2l: + return fprintf(stderr, "d2l"); + case dadd: + return fprintf(stderr, "dadd"); + case daload: + return fprintf(stderr, "daload"); + case dastore: + return fprintf(stderr, "dastore"); + case dcmpg: + return fprintf(stderr, "dcmpg"); + case dcmpl: + return fprintf(stderr, "dcmpl"); + case dconst_0: + return fprintf(stderr, "dconst_0"); + case dconst_1: + return fprintf(stderr, "dconst_1"); + case ddiv: + return fprintf(stderr, "ddiv"); + case dmul: + return fprintf(stderr, "dmul"); + case dneg: + return fprintf(stderr, "dneg"); + case vm::drem: + return fprintf(stderr, "drem"); + case dsub: + return fprintf(stderr, "dsub"); + case vm::dup: + return fprintf(stderr, "dup"); + case dup_x1: + return fprintf(stderr, "dup_x1"); + case dup_x2: + return fprintf(stderr, "dup_x2"); + case vm::dup2: + return fprintf(stderr, "dup2"); + case dup2_x1: + return fprintf(stderr, "dup2_x1"); + case dup2_x2: + return fprintf(stderr, "dup2_x2"); + case f2d: + return fprintf(stderr, "f2d"); + case f2i: + return fprintf(stderr, "f2i"); + case f2l: + return fprintf(stderr, "f2l"); + case fadd: + return fprintf(stderr, "fadd"); + case faload: + return fprintf(stderr, "faload"); + case fastore: + return fprintf(stderr, "fastore"); + case fcmpg: + return fprintf(stderr, "fcmpg"); + case fcmpl: + return fprintf(stderr, "fcmpl"); + case fconst_0: + return fprintf(stderr, "fconst_0"); + case fconst_1: + return fprintf(stderr, "fconst_1"); + case fconst_2: + return fprintf(stderr, "fconst_2"); + case fdiv: + return fprintf(stderr, "fdiv"); + case fmul: + return fprintf(stderr, "fmul"); + case fneg: + return fprintf(stderr, "fneg"); + case frem: + return fprintf(stderr, "frem"); + case fsub: + return fprintf(stderr, "fsub"); + + case getfield: + return fprintf(stderr, "getfield %4d", read16(code, ip)); + case getstatic: + return fprintf(stderr, "getstatic %4d", read16(code, ip)); + case goto_: { + int16_t offset = read16(code, ip); + return fprintf(stderr, "goto %4d", offset + ip - 3); + } + case goto_w: { + int32_t offset = read32(code, ip); + return fprintf(stderr, "goto_w %08x", offset + ip - 5); + } + + case i2b: + return fprintf(stderr, "i2b"); + case i2c: + return fprintf(stderr, "i2c"); + case i2d: + return fprintf(stderr, "i2d"); + case i2f: + return fprintf(stderr, "i2f"); + case i2l: + return fprintf(stderr, "i2l"); + case i2s: + return fprintf(stderr, "i2s"); + case iadd: + return fprintf(stderr, "iadd"); + case iaload: + return fprintf(stderr, "iaload"); + case iand: + return fprintf(stderr, "iand"); + case iastore: + return fprintf(stderr, "iastore"); + case iconst_m1: + return fprintf(stderr, "iconst_m1"); + case iconst_0: + return fprintf(stderr, "iconst_0"); + case iconst_1: + return fprintf(stderr, "iconst_1"); + case iconst_2: + return fprintf(stderr, "iconst_2"); + case iconst_3: + return fprintf(stderr, "iconst_3"); + case iconst_4: + return fprintf(stderr, "iconst_4"); + case iconst_5: + return fprintf(stderr, "iconst_5"); + case idiv: + return fprintf(stderr, "idiv"); + + case if_acmpeq: { + int16_t offset = read16(code, ip); + return fprintf(stderr, "if_acmpeq %4d", offset + ip - 3); + } + case if_acmpne: { + int16_t offset = read16(code, ip); + return fprintf(stderr, "if_acmpne %4d", offset + ip - 3); + } + case if_icmpeq: { + int16_t offset = read16(code, ip); + return fprintf(stderr, "if_icmpeq %4d", offset + ip - 3); + } + case if_icmpne: { + int16_t offset = read16(code, ip); + return fprintf(stderr, "if_icmpne %4d", offset + ip - 3); + } + + case if_icmpgt: { + int16_t offset = read16(code, ip); + return fprintf(stderr, "if_icmpgt %4d", offset + ip - 3); + } + case if_icmpge: { + int16_t offset = read16(code, ip); + return fprintf(stderr, "if_icmpge %4d", offset + ip - 3); + } + case if_icmplt: { + int16_t offset = read16(code, ip); + return fprintf(stderr, "if_icmplt %4d", offset + ip - 3); + } + case if_icmple: { + int16_t offset = read16(code, ip); + return fprintf(stderr, "if_icmple %4d", offset + ip - 3); + } + + case ifeq: { + int16_t offset = read16(code, ip); + return fprintf(stderr, "ifeq %4d", offset + ip - 3); + } + case ifne: { + int16_t offset = read16(code, ip); + return fprintf(stderr, "ifne %4d", offset + ip - 3); + } + case ifgt: { + int16_t offset = read16(code, ip); + return fprintf(stderr, "ifgt %4d", offset + ip - 3); + } + case ifge: { + int16_t offset = read16(code, ip); + return fprintf(stderr, "ifge %4d", offset + ip - 3); + } + case iflt: { + int16_t offset = read16(code, ip); + return fprintf(stderr, "iflt %4d", offset + ip - 3); + } + case ifle: { + int16_t offset = read16(code, ip); + return fprintf(stderr, "ifle %4d", offset + ip - 3); + } + + case ifnonnull: { + int16_t offset = read16(code, ip); + return fprintf(stderr, "ifnonnull %4d", offset + ip - 3); + } + case ifnull: { + int16_t offset = read16(code, ip); + return fprintf(stderr, "ifnull %4d", offset + ip - 3); + } + + case iinc: { + uint8_t a = code[ip++]; + uint8_t b = code[ip++]; + return fprintf(stderr, "iinc %2d %2d", a, b); + } + + case iload: + return fprintf(stderr, "iload %2d", code[ip++]); + case fload: + return fprintf(stderr, "fload %2d", code[ip++]); + + case iload_0: + return fprintf(stderr, "iload_0"); + case fload_0: + return fprintf(stderr, "fload_0"); + case iload_1: + return fprintf(stderr, "iload_1"); + case fload_1: + return fprintf(stderr, "fload_1"); + + case iload_2: + return fprintf(stderr, "iload_2"); + case fload_2: + return fprintf(stderr, "fload_2"); + case iload_3: + return fprintf(stderr, "iload_3"); + case fload_3: + return fprintf(stderr, "fload_3"); + + case imul: + return fprintf(stderr, "imul"); + case ineg: + return fprintf(stderr, "ineg"); + + case instanceof: + return fprintf(stderr, "instanceof %4d", read16(code, ip)); + case invokeinterface: + return fprintf(stderr, "invokeinterface %4d", read16(code, ip)); + case invokespecial: + return fprintf(stderr, "invokespecial %4d", read16(code, ip)); + case invokestatic: + return fprintf(stderr, "invokestatic %4d", read16(code, ip)); + case invokevirtual: + return fprintf(stderr, "invokevirtual %4d", read16(code, ip)); + + case ior: + return fprintf(stderr, "ior"); + case irem: + return fprintf(stderr, "irem"); + case ireturn: + return fprintf(stderr, "ireturn"); + case freturn: + return fprintf(stderr, "freturn"); + case ishl: + return fprintf(stderr, "ishl"); + case ishr: + return fprintf(stderr, "ishr"); + + case istore: + return fprintf(stderr, "istore %2d", code[ip++]); + case fstore: + return fprintf(stderr, "fstore %2d", code[ip++]); + + case istore_0: + return fprintf(stderr, "istore_0"); + case fstore_0: + return fprintf(stderr, "fstore_0"); + case istore_1: + return fprintf(stderr, "istore_1"); + case fstore_1: + return fprintf(stderr, "fstore_1"); + case istore_2: + return fprintf(stderr, "istore_2"); + case fstore_2: + return fprintf(stderr, "fstore_2"); + case istore_3: + return fprintf(stderr, "istore_3"); + case fstore_3: + return fprintf(stderr, "fstore_3"); + + case isub: + return fprintf(stderr, "isub"); + case iushr: + return fprintf(stderr, "iushr"); + case ixor: + return fprintf(stderr, "ixor"); + + case jsr: + return fprintf(stderr, "jsr %4d", read16(code, ip) + startIp); + case jsr_w: + return fprintf(stderr, "jsr_w %08x", read32(code, ip) + startIp); + + case l2d: + return fprintf(stderr, "l2d"); + case l2f: + return fprintf(stderr, "l2f"); + case l2i: + return fprintf(stderr, "l2i"); + case ladd: + return fprintf(stderr, "ladd"); + case laload: + return fprintf(stderr, "laload"); + + case land: + return fprintf(stderr, "land"); + case lastore: + return fprintf(stderr, "lastore"); + + case lcmp: + return fprintf(stderr, "lcmp"); + case lconst_0: + return fprintf(stderr, "lconst_0"); + case lconst_1: + return fprintf(stderr, "lconst_1"); + + case ldc: + return fprintf(stderr, "ldc %4d", read16(code, ip)); + case ldc_w: + return fprintf(stderr, "ldc_w %08x", read32(code, ip)); + case ldc2_w: + return fprintf(stderr, "ldc2_w %4d", read16(code, ip)); + + case ldiv_: + return fprintf(stderr, "ldiv_"); + + case lload: + return fprintf(stderr, "lload %2d", code[ip++]); + case dload: + return fprintf(stderr, "dload %2d", code[ip++]); + + case lload_0: + return fprintf(stderr, "lload_0"); + case dload_0: + return fprintf(stderr, "dload_0"); + case lload_1: + return fprintf(stderr, "lload_1"); + case dload_1: + return fprintf(stderr, "dload_1"); + case lload_2: + return fprintf(stderr, "lload_2"); + case dload_2: + return fprintf(stderr, "dload_2"); + case lload_3: + return fprintf(stderr, "lload_3"); + case dload_3: + return fprintf(stderr, "dload_3"); + + case lmul: + return fprintf(stderr, "lmul"); + case lneg: + return fprintf(stderr, "lneg"); + + case lookupswitch: { + while (ip & 0x3) { + ip++; + } + int32_t default_ = read32(code, ip) + startIp; + int32_t pairCount = read32(code, ip); + fprintf( + stderr, "lookupswitch default: %d pairCount: %d", default_, pairCount); + + for (int i = 0; i < pairCount; i++) { + int32_t k = read32(code, ip); + int32_t d = read32(code, ip) + startIp; + fprintf(stderr, "\n%s key: %2d dest: %d", prefix, k, d); + } + fprintf(stderr, "\n"); + fflush(stderr); + return 0; + } + + case lor: + return fprintf(stderr, "lor"); + case lrem: + return fprintf(stderr, "lrem"); + case lreturn: + return fprintf(stderr, "lreturn"); + case dreturn: + return fprintf(stderr, "dreturn"); + case lshl: + return fprintf(stderr, "lshl"); + case lshr: + return fprintf(stderr, "lshr"); + + case lstore: + return fprintf(stderr, "lstore %2d", code[ip++]); + case dstore: + return fprintf(stderr, "dstore %2d", code[ip++]); + + case lstore_0: + return fprintf(stderr, "lstore_0"); + case dstore_0: + return fprintf(stderr, "dstore_0"); + case lstore_1: + return fprintf(stderr, "lstore_1"); + case dstore_1: + return fprintf(stderr, "dstore_1"); + case lstore_2: + return fprintf(stderr, "lstore_2"); + case dstore_2: + return fprintf(stderr, "dstore_2"); + case lstore_3: + return fprintf(stderr, "lstore_3"); + case dstore_3: + return fprintf(stderr, "dstore_3"); + + case lsub: + return fprintf(stderr, "lsub"); + case lushr: + return fprintf(stderr, "lushr"); + case lxor: + return fprintf(stderr, "lxor"); + + case monitorenter: + return fprintf(stderr, "monitorenter"); + case monitorexit: + return fprintf(stderr, "monitorexit"); + + case multianewarray: { + unsigned type = read16(code, ip); + return fprintf(stderr, "multianewarray %4d %2d", type, code[ip++]); + } + + case new_: + return fprintf(stderr, "new %4d", read16(code, ip)); + + case newarray: + return fprintf(stderr, "newarray %2d", code[ip++]); + + case nop: + return fprintf(stderr, "nop"); + case pop_: + return fprintf(stderr, "pop"); + case pop2: + return fprintf(stderr, "pop2"); + + case putfield: + return fprintf(stderr, "putfield %4d", read16(code, ip)); + case putstatic: + return fprintf(stderr, "putstatic %4d", read16(code, ip)); + + case ret: + return fprintf(stderr, "ret %2d", code[ip++]); + + case return_: + return fprintf(stderr, "return_"); + case saload: + return fprintf(stderr, "saload"); + case sastore: + return fprintf(stderr, "sastore"); + + case sipush: + return fprintf(stderr, "sipush %4d", read16(code, ip)); + + case swap: + return fprintf(stderr, "swap"); + + case tableswitch: { + while (ip & 0x3) { + ip++; + } + int32_t default_ = read32(code, ip) + startIp; + int32_t bottom = read32(code, ip); + int32_t top = read32(code, ip); + fprintf(stderr, + "tableswitch default: %d bottom: %d top: %d", + default_, + bottom, + top); + + for (int i = 0; i < top - bottom + 1; i++) { + int32_t d = read32(code, ip) + startIp; + fprintf(stderr, "%s key: %d dest: %d", prefix, i + bottom, d); + } + return 0; + } + + case wide: { + switch (code[ip++]) { + case aload: + return fprintf(stderr, "wide aload %4d", read16(code, ip)); + + case astore: + return fprintf(stderr, "wide astore %4d", read16(code, ip)); + case iinc: + fprintf(stderr, "wide iinc %4d %4d", read16(code, ip), read16(code, ip)); + case iload: + return fprintf(stderr, "wide iload %4d", read16(code, ip)); + case istore: + return fprintf(stderr, "wide istore %4d", read16(code, ip)); + case lload: + return fprintf(stderr, "wide lload %4d", read16(code, ip)); + case lstore: + return fprintf(stderr, "wide lstore %4d", read16(code, ip)); + case ret: + return fprintf(stderr, "wide ret %4d", read16(code, ip)); + + default: { + fprintf( + stderr, "unknown wide instruction %2d %4d", instr, read16(code, ip)); + } + } + } + + default: { + return fprintf(stderr, "unknown instruction %2d", instr); + } + } + return ip; +} + +void disassembleCode(const char* prefix, uint8_t* code, unsigned length) +{ + unsigned ip = 0; + + while (ip < length) { + fprintf(stderr, "%s%x:\t", prefix, ip); + printInstruction(code, ip, prefix); + fprintf(stderr, "\n"); + } +} + +} // namespace debug +} // namespace jvm +} // namespace avian diff --git a/sgx-jvm/avian/src/debug-util.h b/sgx-jvm/avian/src/debug-util.h new file mode 100644 index 0000000000..bb981baa10 --- /dev/null +++ b/sgx-jvm/avian/src/debug-util.h @@ -0,0 +1,24 @@ +/* Copyright (c) 2008-2013, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include + +namespace avian { +namespace jvm { +namespace debug { + +// print out a single instruction (no newline) +// returns number of characters printed +int printInstruction(uint8_t* code, unsigned& ip, const char* prefix = ""); +void disassembleCode(const char* prefix, uint8_t* code, unsigned length); + +} // namespace debug +} // namespace jvm +} // namespace avian diff --git a/sgx-jvm/avian/src/embed.cpp b/sgx-jvm/avian/src/embed.cpp new file mode 100644 index 0000000000..60687d899e --- /dev/null +++ b/sgx-jvm/avian/src/embed.cpp @@ -0,0 +1,146 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include +#include +#include +#include +#include +#include + +#include "avian/embed.h" + +#ifdef __x86_64__ +#define BINARY_LOADER(x) _binary_loader_##x +#else +#define BINARY_LOADER(x) binary_loader_##x +#endif + +extern "C" const uint8_t BINARY_LOADER(start)[]; +extern "C" const uint8_t BINARY_LOADER(end)[]; + +__declspec(noreturn) void printUsage(const wchar_t* executableName) +{ + wprintf(L"Usage: %s destination.exe classes.jar package.Main\n", + executableName); + exit(0); +} + +void writeDestinationFile(const wchar_t* filename) +{ + if (FILE* file = _wfopen(filename, L"wb")) { + size_t count = BINARY_LOADER(end) - BINARY_LOADER(start); + if (count == fwrite(BINARY_LOADER(start), + sizeof(BINARY_LOADER(start)[0]), + count, + file)) { + fclose(file); + return; + } + } + + fprintf(stderr, "Unable to write to destination file\n"); + exit(EXIT_FAILURE); +} + +void readFile(std::vector* jarFile, const wchar_t* fileName) +{ + if (FILE* file = _wfopen(fileName, L"rb")) { + fseek(file, 0, SEEK_END); + jarFile->resize(ftell(file)); + fseek(file, 0, SEEK_SET); + fread(&jarFile->at(0), 1, jarFile->size(), file); + fclose(file); + } +} + +bool mkStringSection(std::vector* stringSection, + const std::vector& strings, + int first, + int last) +{ + stringSection->clear(); + for (int i = first; i <= last; ++i) { + const std::wstring& s = strings.at(i); + stringSection->push_back(s.size()); + stringSection->insert(stringSection->end(), s.begin(), s.end()); + } + + // pad to 16 entries + for (int i = last - first; i < 15; ++i) + stringSection->push_back(0); + + return stringSection->size() > 16; +} + +void writeStringResources(HANDLE hDest, + const std::vector& strings) +{ + for (unsigned i = 0; i < strings.size(); i += 16) { + std::vector stringSection; + + if (mkStringSection(&stringSection, + strings, + i, + std::min(i + 15, strings.size() - 1))) + UpdateResourceW(hDest, + reinterpret_cast(RT_STRING), + reinterpret_cast(MAKEINTRESOURCE((i >> 4) + 1)), + LANG_NEUTRAL, + &stringSection.at(0), + sizeof(wchar_t) * stringSection.size()); + } +} + +int wmain(int argc, wchar_t* argv[]) +{ + if (argc != 4) + printUsage(argv[0]); + + const wchar_t* destinationName = argv[1]; + const wchar_t* classesName = argv[2]; + const wchar_t* mainClassName = argv[3]; + + writeDestinationFile(destinationName); + + if (HANDLE hDest = BeginUpdateResourceW(destinationName, TRUE)) { + std::vector strings; + strings.resize(RESID_MAIN_CLASS + 1); + strings.at(RESID_MAIN_CLASS) = mainClassName; + + writeStringResources(hDest, strings); + + std::vector jarFile; + readFile(&jarFile, classesName); + UpdateResourceW(hDest, + reinterpret_cast(RT_RCDATA), + RESID_BOOT_JAR, + LANG_NEUTRAL, + &jarFile.at(0), + jarFile.size()); + + EndUpdateResource(hDest, FALSE); + } + + return 0; +} + +#ifndef _MSC_VER +extern "C" int _CRT_glob; +extern "C" void __wgetmainargs(int*, wchar_t***, wchar_t***, int, int*); + +int main() +{ + wchar_t** enpv, **argv; + int argc, si = 0; + __wgetmainargs(&argc, &argv, &enpv, _CRT_glob, &si); + return wmain(argc, argv); +} +#endif diff --git a/sgx-jvm/avian/src/embedded-loader.cpp b/sgx-jvm/avian/src/embedded-loader.cpp new file mode 100644 index 0000000000..7d1df49e47 --- /dev/null +++ b/sgx-jvm/avian/src/embedded-loader.cpp @@ -0,0 +1,104 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include +#include +#include + +#include "avian/embed.h" +#include "avian/common.h" +#include "jni.h" + +extern "C" { +// since we aren't linking against libstdc++, we must implement this +// ourselves: +void __cxa_pure_virtual(void) +{ + abort(); +} + +AVIAN_EXPORT const uint8_t* bootJar(unsigned* size) +{ + if (HRSRC hResInfo = FindResourceW( + NULL, RESID_BOOT_JAR, reinterpret_cast(RT_RCDATA))) { + if (HGLOBAL hRes = LoadResource(NULL, hResInfo)) { + *size = SizeofResource(NULL, hResInfo); + return (const uint8_t*)LockResource(hRes); + } + } + + fprintf(stderr, "boot.jar resource not found\n"); + + *size = 0; + return NULL; +} +} // extern "C" + +static void getMainClass(char* pName, int maxLen) +{ + if (0 == LoadString(NULL, RESID_MAIN_CLASS, pName, maxLen)) { + fprintf(stderr, "Main class not specified\n"); + strcpy(pName, "Main"); + } +} + +int main(int ac, const char** av) +{ + JavaVMInitArgs vmArgs; + vmArgs.version = JNI_VERSION_1_2; + vmArgs.nOptions = 1; + vmArgs.ignoreUnrecognized = JNI_TRUE; + + JavaVMOption options[1]; + vmArgs.options = options; + + options[0].optionString = const_cast("-Xbootclasspath:[bootJar]"); + + JavaVM* vm; + void* env; + JNI_CreateJavaVM(&vm, &env, &vmArgs); + JNIEnv* e = static_cast(env); + + char mainClass[256]; + getMainClass(mainClass, sizeof(mainClass)); + + jclass c = e->FindClass(mainClass); + 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); + } else + fprintf(stderr, "Couldn't create array\n"); + } else + fprintf(stderr, "java.lang.String not found\n"); + } else + fprintf(stderr, "main method not found\n"); + } else + fprintf(stderr, "Main class not found\n"); + + int exitCode = 0; + if (e->ExceptionCheck()) { + exitCode = -1; + e->ExceptionDescribe(); + e->ExceptionClear(); + } + + vm->DestroyJavaVM(); + + return exitCode; +} diff --git a/sgx-jvm/avian/src/finder.cpp b/sgx-jvm/avian/src/finder.cpp new file mode 100644 index 0000000000..e16df51184 --- /dev/null +++ b/sgx-jvm/avian/src/finder.cpp @@ -0,0 +1,1104 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include +#include +#include +#include +#include +#include + +#include "avian/zlib-custom.h" +#include "avian/finder.h" +#include "avian/lzma.h" +#include "avian/append.h" + +using namespace vm; +using namespace avian::util; + +namespace { + +const bool DebugFind = false; +const bool DebugStat = false; + +class Element { + public: + class Iterator { + public: + virtual const char* next(size_t* size) = 0; + virtual void dispose() = 0; + }; + + Element() : next(0) + { + } + + virtual Iterator* iterator() = 0; + virtual System::Region* find(const char* name) = 0; + virtual System::FileType stat(const char* name, + size_t* length, + bool tryDirectory) = 0; + virtual const char* urlPrefix() = 0; + virtual const char* sourceUrl() = 0; + virtual void dispose() = 0; + + Element* next; +}; + +class DirectoryElement : public Element { + public: + class Iterator : public Element::Iterator { + public: + Iterator(System* s, Alloc* allocator, const char* name, unsigned skip) + : s(s), + allocator(allocator), + name(name), + skip(skip), + directory(0), + last(0), + it(0) + { + if (not s->success(s->open(&directory, name))) { + directory = 0; + } + } + + virtual const char* next(size_t* size) + { + if (it) { + const char* v = it->next(size); + if (v) { + return v; + } else { + it->dispose(); + it = 0; + } + } + + if (last) { + allocator->free(last, strlen(last) + 1); + } + + if (directory) { + for (const char* v = directory->next(); v; v = directory->next()) { + if (v[0] != '.') { + last = append(allocator, name, "/", v); + size_t length; + if (s->stat(last, &length) == System::TypeDirectory) { + it = new (allocator->allocate(sizeof(Iterator))) + Iterator(s, allocator, last, skip); + it->name = last; + } + const char* result = last + skip; + *size = strlen(result); + return result; + } + } + } + + return 0; + } + + virtual void dispose() + { + directory->dispose(); + allocator->free(this, sizeof(*this)); + } + + System* s; + Alloc* allocator; + const char* name; + unsigned skip; + System::Directory* directory; + const char* last; + Iterator* it; + }; + + DirectoryElement(System* s, Alloc* allocator, const char* name) + : s(s), + allocator(allocator), + originalName(name), + name(s->toAbsolutePath(allocator, name)), + urlPrefix_(append(allocator, "file:", this->name, "/")), + sourceUrl_(append(allocator, "file:", this->name)) + { + } + + virtual Element::Iterator* iterator() + { + return new (allocator->allocate(sizeof(Iterator))) + Iterator(s, allocator, name, strlen(name) + 1); + } + + virtual System::Region* find(const char* name) + { + const char* file = append(allocator, this->name, "/", name); + System::Region* region; + System::Status status = s->map(®ion, file); + allocator->free(file, strlen(file) + 1); + + if (s->success(status)) { + if (DebugFind) { + fprintf(stderr, "found %s in %s\n", name, this->name); + } + return region; + } else { + if (DebugFind) { + fprintf(stderr, "%s not found in %s\n", name, this->name); + } + return 0; + } + } + + virtual System::FileType stat(const char* name, size_t* length, bool) + { + const char* file = append(allocator, this->name, "/", name); + System::FileType type = s->stat(file, length); + if (DebugStat) { + fprintf(stderr, "stat %s in %s: %d\n", name, this->name, type); + } + allocator->free(file, strlen(file) + 1); + return type; + } + + virtual const char* urlPrefix() + { + return urlPrefix_; + } + + virtual const char* sourceUrl() + { + return sourceUrl_; + } + + virtual void dispose() + { + allocator->free(originalName, strlen(originalName) + 1); + allocator->free(name, strlen(name) + 1); + allocator->free(urlPrefix_, strlen(urlPrefix_) + 1); + allocator->free(sourceUrl_, strlen(sourceUrl_) + 1); + allocator->free(this, sizeof(*this)); + } + + System* s; + Alloc* allocator; + const char* originalName; + const char* name; + const char* urlPrefix_; + const char* sourceUrl_; +}; + +class PointerRegion : public System::Region { + public: + PointerRegion(System* s, + Alloc* allocator, + const uint8_t* start, + size_t length, + bool freePointer = false) + : s(s), + allocator(allocator), + start_(start), + length_(length), + freePointer(freePointer) + { + } + + virtual const uint8_t* start() + { + return start_; + } + + virtual size_t length() + { + return length_; + } + + virtual void dispose() + { + if (freePointer) { + allocator->free(start_, length_); + } + allocator->free(this, sizeof(*this)); + } + + System* s; + Alloc* allocator; + const uint8_t* start_; + size_t length_; + bool freePointer; +}; + +class DataRegion : public System::Region { + public: + DataRegion(System* s, Alloc* allocator, size_t length) + : s(s), allocator(allocator), length_(length) + { + } + + virtual const uint8_t* start() + { + return data; + } + + virtual size_t length() + { + return length_; + } + + virtual void dispose() + { + allocator->free(this, sizeof(*this) + length_); + } + + System* s; + Alloc* allocator; + size_t length_; + uint8_t data[0]; +}; + +class JarIndex { + public: + enum CompressionMethod { Stored = 0, Deflated = 8 }; + + class Entry { + public: + Entry(uint32_t hash, const uint8_t* entry) : hash(hash), entry(entry) + { + } + + uint32_t hash; + const uint8_t* entry; + }; + + JarIndex(System* s, Alloc* allocator, unsigned capacity) + : s(s), + allocator(allocator), + capacity(capacity), + position(0), + nodes(static_cast*>( + allocator->allocate(sizeof(List) * capacity))) + { + memset(table, 0, sizeof(List*) * capacity); + } + + static JarIndex* make(System* s, Alloc* allocator, unsigned capacity) + { + return new (allocator->allocate(sizeof(JarIndex) + + (sizeof(List*) * capacity))) + JarIndex(s, allocator, capacity); + } + + static JarIndex* open(System* s, Alloc* allocator, System::Region* region) + { + JarIndex* index = make(s, allocator, 32); + + const uint8_t* start = region->start(); + const uint8_t* end = start + region->length(); + const uint8_t* p = end - CentralDirectorySearchStart; + // Find end of central directory record + while (p > start) { + if (signature(p) == CentralDirectorySignature) { + p = region->start() + centralDirectoryOffset(p); + + while (p < end) { + if (signature(p) == EntrySignature) { + index = index->add(Entry( + hash(Slice(fileName(p), fileNameLength(p))), p)); + + p = endOfEntry(p); + } else { + return index; + } + } + } else { + p--; + } + } + + return index; + } + + JarIndex* add(const Entry& entry) + { + if (position < capacity) { + unsigned i = entry.hash & (capacity - 1); + table[i] = new (nodes + (position++)) List(entry, table[i]); + return this; + } else { + JarIndex* index = make(s, allocator, capacity * 2); + for (unsigned i = 0; i < capacity; ++i) { + index->add(nodes[i].item); + } + index->add(entry); + dispose(); + return index; + } + } + + List* findNode(const char* name) + { + size_t length = strlen(name); + unsigned i = hash(name) & (capacity - 1); + for (List* n = table[i]; n; n = n->next) { + const uint8_t* p = n->item.entry; + if (equal(name, length, fileName(p), fileNameLength(p))) { + return n; + } + } + return 0; + } + + System::Region* find(const char* name, const uint8_t* start) + { + List* n = findNode(name); + if (n) { + const uint8_t* p = n->item.entry; + switch (compressionMethod(p)) { + case Stored: { + return new (allocator->allocate(sizeof(PointerRegion))) + PointerRegion(s, + allocator, + fileData(start + localHeaderOffset(p)), + compressedSize(p)); + } break; + + case Deflated: { + DataRegion* region = new ( + allocator->allocate(sizeof(DataRegion) + uncompressedSize(p))) + DataRegion(s, allocator, uncompressedSize(p)); + + z_stream zStream; + memset(&zStream, 0, sizeof(z_stream)); + + zStream.next_in + = const_cast(fileData(start + localHeaderOffset(p))); + zStream.avail_in = compressedSize(p); + zStream.next_out = region->data; + zStream.avail_out = region->length(); + + // -15 means max window size and raw deflate (no zlib wrapper) + int r = inflateInit2(&zStream, -15); + expect(s, r == Z_OK); + + r = inflate(&zStream, Z_FINISH); + expect(s, r == Z_STREAM_END); + + inflateEnd(&zStream); + + return region; + } break; + + default: + abort(s); + } + } + + return 0; + } + + System::FileType stat(const char* name, size_t* length, bool tryDirectory) + { + List* node = findNode(name); + if (node) { + *length = uncompressedSize(node->item.entry); + return System::TypeFile; + } else if (tryDirectory) { + *length = 0; + + // try again with '/' appended + size_t length = strlen(name); + RUNTIME_ARRAY(char, n, length + 2); + memcpy(RUNTIME_ARRAY_BODY(n), name, length); + RUNTIME_ARRAY_BODY(n)[length] = '/'; + RUNTIME_ARRAY_BODY(n)[length + 1] = 0; + + node = findNode(RUNTIME_ARRAY_BODY(n)); + if (node) { + return System::TypeDirectory; + } else { + return System::TypeDoesNotExist; + } + } else { + *length = 0; + return System::TypeDoesNotExist; + } + } + + void dispose() + { + allocator->free(nodes, sizeof(List) * capacity); + allocator->free(this, sizeof(*this) + (sizeof(List*) * capacity)); + } + + System* s; + Alloc* allocator; + unsigned capacity; + unsigned position; + + List* nodes; + List* table[0]; +}; + +class JarElement : public Element { + public: + class Iterator : public Element::Iterator { + public: + Iterator(System* s, Alloc* allocator, JarIndex* index) + : s(s), allocator(allocator), index(index), position(0) + { + } + + virtual const char* next(size_t* size) + { + if (position < index->position) { + List* n = index->nodes + (position++); + *size = fileNameLength(n->item.entry); + return reinterpret_cast(fileName(n->item.entry)); + } else { + return 0; + } + } + + virtual void dispose() + { + allocator->free(this, sizeof(*this)); + } + + System* s; + Alloc* allocator; + JarIndex* index; + unsigned position; + }; + + JarElement(System* s, + Alloc* allocator, + const char* name, + bool canonicalizePath = true) + : s(s), + allocator(allocator), + originalName(name), + name(name and canonicalizePath ? s->toAbsolutePath(allocator, name) + : name), + urlPrefix_(this->name ? append(allocator, "jar:file:", this->name, "!/") + : 0), + sourceUrl_(this->name ? append(allocator, "file:", this->name) : 0), + region(0), + index(0) + { + } + + JarElement(System* s, + Alloc* allocator, + const uint8_t* jarData, + unsigned jarLength) + : s(s), + allocator(allocator), + originalName(0), + name(0), + urlPrefix_(name ? append(allocator, "jar:file:", name, "!/") : 0), + sourceUrl_(name ? append(allocator, "file:", name) : 0), + region(new (allocator->allocate(sizeof(PointerRegion))) + PointerRegion(s, allocator, jarData, jarLength)), + index(JarIndex::open(s, allocator, region)) + { + } + + virtual Element::Iterator* iterator() + { + init(); + + return new (allocator->allocate(sizeof(Iterator))) + Iterator(s, allocator, index); + } + + virtual void init() + { + if (index == 0) { + System::Region* r; + if (s->success(s->map(&r, name))) { + region = r; + index = JarIndex::open(s, allocator, r); + } + } + } + + virtual System::Region* find(const char* name) + { + init(); + + while (*name == '/') + name++; + + System::Region* r = (index ? index->find(name, region->start()) : 0); + if (DebugFind) { + if (r) { + fprintf(stderr, "found %s in %s\n", name, this->name); + } else { + fprintf(stderr, "%s not found in %s\n", name, this->name); + } + } + return r; + } + + virtual System::FileType stat(const char* name, + size_t* length, + bool tryDirectory) + { + init(); + + while (*name == '/') + name++; + + System::FileType type = (index ? index->stat(name, length, tryDirectory) + : System::TypeDoesNotExist); + if (DebugStat) { + fprintf(stderr, "stat %s in %s: %d\n", name, this->name, type); + } + return type; + } + + virtual const char* urlPrefix() + { + return urlPrefix_; + } + + virtual const char* sourceUrl() + { + return sourceUrl_; + } + + virtual void dispose() + { + dispose(sizeof(*this)); + } + + virtual void dispose(unsigned size) + { + if (name) { + if (originalName != name) { + allocator->free(originalName, strlen(originalName) + 1); + } + allocator->free(name, strlen(name) + 1); + allocator->free(urlPrefix_, strlen(urlPrefix_) + 1); + allocator->free(sourceUrl_, strlen(sourceUrl_) + 1); + } + if (index) { + index->dispose(); + } + if (region) { + region->dispose(); + } + allocator->free(this, size); + } + + System* s; + Alloc* allocator; + const char* originalName; + const char* name; + const char* urlPrefix_; + const char* sourceUrl_; + System::Region* region; + JarIndex* index; +}; + +class BuiltinElement : public JarElement { + public: + BuiltinElement(System* s, + Alloc* allocator, + const char* name, + const char* libraryName) + : JarElement(s, allocator, name, false), + library(0), + libraryName(libraryName ? copy(allocator, libraryName) : 0) + { + } + + virtual void init() + { + if (index == 0) { + if (s->success(s->load(&library, libraryName))) { + bool lzma = strncmp("lzma.", name, 5) == 0; + const char* symbolName = lzma ? name + 5 : name; + + void* p = library->resolve(symbolName); + if (p) { + uint8_t* (*function)(size_t*); + memcpy(&function, &p, BytesPerWord); + + size_t size = 0; + uint8_t* data = function(&size); + if (data) { + bool freePointer; + if (lzma) { +#ifdef AVIAN_USE_LZMA + size_t outSize; + data = decodeLZMA(s, allocator, data, size, &outSize); + size = outSize; + freePointer = true; +#else + abort(s); +#endif + } else { + freePointer = false; + } + region = new (allocator->allocate(sizeof(PointerRegion))) + PointerRegion(s, allocator, data, size, freePointer); + index = JarIndex::open(s, allocator, region); + } else if (DebugFind) { + fprintf( + stderr, "%s in %s returned null\n", symbolName, libraryName); + } + } else if (DebugFind) { + fprintf(stderr, "unable to find %s in %s\n", symbolName, libraryName); + } + } else if (DebugFind) { + fprintf(stderr, "unable to load %s\n", libraryName); + } + } + } + + virtual const char* urlPrefix() + { + return "avianvmresource:"; + } + + virtual const char* sourceUrl() + { + return 0; + } + + virtual void dispose() + { + if (library) { + library->disposeAll(); + } + if (libraryName) { + allocator->free(libraryName, strlen(libraryName) + 1); + } + JarElement::dispose(sizeof(*this)); + } + + System::Library* library; + const char* libraryName; +}; + +void add(Element** first, Element** last, Element* e) +{ + if (*last) { + (*last)->next = e; + } else { + *first = e; + } + *last = e; +} + +unsigned baseName(const char* name, char fileSeparator) +{ + const char* p = name; + const char* last = 0; + while (*p) { + if (*p == fileSeparator) { + last = p; + } + ++p; + } + + return last ? (last + 1) - name : 0; +} + +void add(System* s, + Element** first, + Element** last, + Alloc* allocator, + const char* name, + unsigned nameLength, + const char* bootLibrary); + +void addTokens(System* s, + Element** first, + Element** last, + Alloc* allocator, + const char* jarName, + unsigned jarNameBase, + const char* tokens, + unsigned tokensLength, + const char* bootLibrary) +{ + for (Tokenizer t(String(tokens, tokensLength), ' '); t.hasMore();) { + String token(t.next()); + + RUNTIME_ARRAY(char, n, jarNameBase + token.length + 1); + memcpy(RUNTIME_ARRAY_BODY(n), jarName, jarNameBase); + memcpy(RUNTIME_ARRAY_BODY(n) + jarNameBase, token.text, token.length); + RUNTIME_ARRAY_BODY(n)[jarNameBase + token.length] = 0; + + add(s, + first, + last, + allocator, + RUNTIME_ARRAY_BODY(n), + jarNameBase + token.length, + bootLibrary); + } +} + +bool continuationLine(const uint8_t* base, + unsigned total, + size_t* start, + size_t* length) +{ + return readLine(base, total, start, length) and *length > 0 + and base[*start] == ' '; +} + +void addJar(System* s, + Element** first, + Element** last, + Alloc* allocator, + const char* name, + const char* bootLibrary) +{ + if (DebugFind) { + fprintf(stderr, "add jar %s\n", name); + } + + JarElement* e = new (allocator->allocate(sizeof(JarElement))) + JarElement(s, allocator, name); + + unsigned nameBase = baseName(name, s->fileSeparator()); + + add(first, last, e); + + System::Region* region = e->find("META-INF/MANIFEST.MF"); + if (region) { + size_t start = 0; + size_t length; + while (readLine(region->start(), region->length(), &start, &length)) { + unsigned multilineTotal = 0; + + const unsigned PrefixLength = 12; + if (length > PrefixLength + and strncmp("Class-Path: ", + reinterpret_cast(region->start() + start), + PrefixLength) == 0) { + { + size_t nextStart = start + length; + size_t nextLength; + while (continuationLine( + region->start(), region->length(), &nextStart, &nextLength)) { + multilineTotal += nextLength; + nextStart += nextLength; + } + } + + const char* line = reinterpret_cast(region->start() + start + + PrefixLength); + + unsigned lineLength = length - PrefixLength; + + if (multilineTotal) { + RUNTIME_ARRAY(char, n, (length - PrefixLength) + multilineTotal + 1); + + memcpy(RUNTIME_ARRAY_BODY(n), line, lineLength); + + unsigned offset = lineLength; + { + size_t nextStart = start + length; + size_t nextLength; + while (continuationLine( + region->start(), region->length(), &nextStart, &nextLength)) { + unsigned continuationLength = nextLength - 1; + + memcpy(RUNTIME_ARRAY_BODY(n) + offset, + region->start() + nextStart + 1, + continuationLength); + + offset += continuationLength; + nextStart += nextLength; + } + } + + addTokens(s, + first, + last, + allocator, + name, + nameBase, + RUNTIME_ARRAY_BODY(n), + offset, + bootLibrary); + } else { + addTokens(s, + first, + last, + allocator, + name, + nameBase, + line, + lineLength, + bootLibrary); + } + } + + start += length + multilineTotal; + } + + region->dispose(); + } +} + +void add(System* s, + Element** first, + Element** last, + Alloc* allocator, + const char* token, + unsigned tokenLength, + const char* bootLibrary) +{ + if (*token == '[' and token[tokenLength - 1] == ']') { + char* name = static_cast(allocator->allocate(tokenLength - 1)); + memcpy(name, token + 1, tokenLength - 1); + name[tokenLength - 2] = 0; + + if (DebugFind) { + fprintf(stderr, "add builtin %s\n", name); + } + + add(first, + last, + new (allocator->allocate(sizeof(BuiltinElement))) + BuiltinElement(s, allocator, name, bootLibrary)); + } else { + char* name = static_cast(allocator->allocate(tokenLength + 1)); + memcpy(name, token, tokenLength); + name[tokenLength] = 0; + + size_t length; + switch (s->stat(name, &length)) { + case System::TypeFile: { + addJar(s, first, last, allocator, name, bootLibrary); + } break; + + case System::TypeDirectory: { + if (DebugFind) { + fprintf(stderr, "add directory %s\n", name); + } + + add(first, + last, + new (allocator->allocate(sizeof(DirectoryElement))) + DirectoryElement(s, allocator, name)); + } break; + + default: { + if (DebugFind) { + fprintf(stderr, "ignore nonexistent %s\n", name); + } + + allocator->free(name, strlen(name) + 1); + } break; + } + } +} + +Element* parsePath(System* s, + Alloc* allocator, + const char* path, + const char* bootLibrary) +{ + Element* first = 0; + Element* last = 0; + for (Tokenizer t(path, s->pathSeparator()); t.hasMore();) { + String token(t.next()); + + add(s, &first, &last, allocator, token.text, token.length, bootLibrary); + } + + return first; +} + +class MyIterator : public Finder::IteratorImp { + public: + MyIterator(System* s, Alloc* allocator, Element* path) + : s(s), + allocator(allocator), + e(path ? path->next : 0), + it(path ? path->iterator() : 0) + { + } + + virtual const char* next(size_t* size) + { + while (it) { + const char* v = it->next(size); + if (v) { + return v; + } else { + it->dispose(); + if (e) { + it = e->iterator(); + e = e->next; + } else { + it = 0; + } + } + } + return 0; + } + + virtual void dispose() + { + if (it) + it->dispose(); + allocator->free(this, sizeof(*this)); + } + + System* s; + Alloc* allocator; + Element* e; + Element::Iterator* it; +}; + +class MyFinder : public Finder { + public: + MyFinder(System* system, + Alloc* allocator, + const char* path, + const char* bootLibrary) + : system(system), + allocator(allocator), + path_(parsePath(system, allocator, path, bootLibrary)), + pathString(copy(allocator, path)) + { + } + + MyFinder(System* system, + Alloc* allocator, + const uint8_t* jarData, + unsigned jarLength) + : system(system), + allocator(allocator), + path_(new (allocator->allocate(sizeof(JarElement))) + JarElement(system, allocator, jarData, jarLength)), + pathString(0) + { + } + + virtual IteratorImp* iterator() + { + return new (allocator->allocate(sizeof(MyIterator))) + MyIterator(system, allocator, path_); + } + + virtual System::Region* find(const char* name) + { + for (Element* e = path_; e; e = e->next) { + System::Region* r = e->find(name); + if (r) { + return r; + } + } + + return 0; + } + + virtual System::FileType stat(const char* name, + size_t* length, + bool tryDirectory) + { + for (Element* e = path_; e; e = e->next) { + System::FileType type = e->stat(name, length, tryDirectory); + if (type != System::TypeDoesNotExist) { + return type; + } + } + + return System::TypeDoesNotExist; + } + + virtual const char* urlPrefix(const char* name) + { + void* finderElementPtr = NULL; + return nextUrlPrefix(name, finderElementPtr); + } + + virtual const char* nextUrlPrefix(const char* name, void*& finderElementPtr) + { + Element*& e = reinterpret_cast(finderElementPtr); + e = e ? e->next : path_; + for (; e; e = e->next) { + size_t length; + System::FileType type = e->stat(name, &length, true); + if (type != System::TypeDoesNotExist) { + return e->urlPrefix(); + } + } + + return 0; + } + + virtual const char* sourceUrl(const char* name) + { + for (Element* e = path_; e; e = e->next) { + size_t length; + System::FileType type = e->stat(name, &length, true); + if (type != System::TypeDoesNotExist) { + return e->sourceUrl(); + } + } + + return 0; + } + + virtual const char* path() + { + return pathString; + } + + virtual void dispose() + { + for (Element* e = path_; e;) { + Element* t = e; + e = e->next; + t->dispose(); + } + if (pathString) { + allocator->free(pathString, strlen(pathString) + 1); + } + allocator->free(this, sizeof(*this)); + } + + System* system; + Alloc* allocator; + Element* path_; + const char* pathString; +}; + +} // namespace + +namespace vm { + +AVIAN_EXPORT Finder* makeFinder(System* s, + Alloc* a, + const char* path, + const char* bootLibrary) +{ + return new (a->allocate(sizeof(MyFinder))) MyFinder(s, a, path, bootLibrary); +} + +Finder* makeFinder(System* s, + Alloc* a, + const uint8_t* jarData, + size_t jarLength) +{ + return new (a->allocate(sizeof(MyFinder))) MyFinder(s, a, jarData, jarLength); +} + +} // namespace vm diff --git a/sgx-jvm/avian/src/heap/CMakeLists.txt b/sgx-jvm/avian/src/heap/CMakeLists.txt new file mode 100644 index 0000000000..5c6ae4177d --- /dev/null +++ b/sgx-jvm/avian/src/heap/CMakeLists.txt @@ -0,0 +1,2 @@ + +add_library(avian_heap heap.cpp) \ No newline at end of file diff --git a/sgx-jvm/avian/src/heap/heap.cpp b/sgx-jvm/avian/src/heap/heap.cpp new file mode 100644 index 0000000000..6799452725 --- /dev/null +++ b/sgx-jvm/avian/src/heap/heap.cpp @@ -0,0 +1,2206 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include +#include +#include "avian/common.h" +#include "avian/arch.h" + +#include + +using namespace vm; +using namespace avian::util; + +namespace { + +namespace local { + +const unsigned Top = ~static_cast(0); + +const unsigned InitialGen2CapacityInBytes = 4 * 1024 * 1024; +const unsigned InitialTenuredFixieCeilingInBytes = 4 * 1024 * 1024; + +const bool Verbose = false; +const bool Verbose2 = false; +const bool Debug = false; +const bool DebugFixies = false; + +#ifdef NDEBUG +const bool DebugAllocation = false; +#else +const bool DebugAllocation = true; +#endif + +#define ACQUIRE(x) MutexLock MAKE_NAME(monitorLock_)(x) + +class MutexLock { + public: + MutexLock(System::Mutex* m) : m(m) + { + m->acquire(); + } + + ~MutexLock() + { + m->release(); + } + + private: + System::Mutex* m; +}; + +class Context; + +Aborter* getAborter(Context* c); + +void* tryAllocate(Context* c, size_t size); +void* allocate(Context* c, size_t size); +void* allocate(Context* c, size_t size, bool limit); +void free(Context* c, const void* p, size_t size); + +#ifdef USE_ATOMIC_OPERATIONS +inline void markBitAtomic(uintptr_t* map, unsigned i) +{ + uintptr_t* p = map + wordOf(i); + uintptr_t v = static_cast(1) << bitOf(i); + for (uintptr_t old = *p; not atomicCompareAndSwap(p, old, old | v); + old = *p) { + } +} +#endif // USE_ATOMIC_OPERATIONS + +inline void* get(void* o, unsigned offsetInWords) +{ + return maskAlignedPointer( + fieldAtOffset(o, offsetInWords * BytesPerWord)); +} + +inline void** getp(void* o, unsigned offsetInWords) +{ + return &fieldAtOffset(o, offsetInWords * BytesPerWord); +} + +inline void set(void** o, void* value) +{ + *o = reinterpret_cast( + reinterpret_cast(value) + | (reinterpret_cast(*o) & (~PointerMask))); +} + +inline void set(void* o, unsigned offsetInWords, void* value) +{ + set(getp(o, offsetInWords), value); +} + +class Segment { + public: + class Map { + public: + class Iterator { + public: + Map* map; + unsigned index; + unsigned limit; + + Iterator(Map* map, unsigned start, unsigned end) : map(map) + { + assertT(map->segment->context, map->bitsPerRecord == 1); + assertT(map->segment->context, map->segment); + assertT(map->segment->context, start <= map->segment->position()); + + if (end > map->segment->position()) + end = map->segment->position(); + + index = map->indexOf(start); + limit = map->indexOf(end); + + if ((end - start) % map->scale) + ++limit; + } + + bool hasMore() + { + unsigned word = wordOf(index); + unsigned bit = bitOf(index); + unsigned wordLimit = wordOf(limit); + unsigned bitLimit = bitOf(limit); + + for (; word <= wordLimit and (word < wordLimit or bit < bitLimit); + ++word) { + uintptr_t w = map->data[word]; + if (2) { + for (; bit < BitsPerWord and (word < wordLimit or bit < bitLimit); + ++bit) { + if (w & (static_cast(1) << bit)) { + index = ::indexOf(word, bit); + // printf("hit at index %d\n", index); + return true; + } else { + // printf("miss at index %d\n", indexOf(word, + // bit)); + } + } + } + bit = 0; + } + + index = limit; + + return false; + } + + unsigned next() + { + assertT(map->segment->context, hasMore()); + assertT(map->segment->context, map->segment); + + return (index++) * map->scale; + } + }; + + Segment* segment; + Map* child; + uintptr_t* data; + unsigned bitsPerRecord; + unsigned scale; + bool clearNewData; + + Map(Segment* segment, + uintptr_t* data, + unsigned bitsPerRecord, + unsigned scale, + Map* child, + bool clearNewData) + : segment(segment), + child(child), + data(data), + bitsPerRecord(bitsPerRecord), + scale(scale), + clearNewData(clearNewData) + { + } + + Map(Segment* segment, + unsigned bitsPerRecord, + unsigned scale, + Map* child, + bool clearNewData) + : segment(segment), + child(child), + data(0), + bitsPerRecord(bitsPerRecord), + scale(scale), + clearNewData(clearNewData) + { + } + + void init() + { + assertT(segment->context, bitsPerRecord); + assertT(segment->context, scale); + assertT(segment->context, powerOfTwo(scale)); + + if (data == 0) { + data = segment->data + segment->capacity() + + calculateOffset(segment->capacity()); + } + + if (clearNewData) { + memset(data, 0, size() * BytesPerWord); + } + + if (child) { + child->init(); + } + } + + unsigned calculateOffset(unsigned capacity) + { + unsigned n = 0; + if (child) + n += child->calculateFootprint(capacity); + return n; + } + + static unsigned calculateSize(Context* c UNUSED, + unsigned capacity, + unsigned scale, + unsigned bitsPerRecord) + { + unsigned result = ceilingDivide( + ceilingDivide(capacity, scale) * bitsPerRecord, BitsPerWord); + assertT(c, result); + return result; + } + + unsigned calculateSize(unsigned capacity) + { + return calculateSize(segment->context, capacity, scale, bitsPerRecord); + } + + unsigned size() + { + return calculateSize(segment->capacity()); + } + + unsigned calculateFootprint(unsigned capacity) + { + unsigned n = calculateSize(capacity); + if (child) + n += child->calculateFootprint(capacity); + return n; + } + + void replaceWith(Map* m) + { + assertT(segment->context, bitsPerRecord == m->bitsPerRecord); + assertT(segment->context, scale == m->scale); + + data = m->data; + + m->segment = 0; + m->data = 0; + + if (child) + child->replaceWith(m->child); + } + + unsigned indexOf(unsigned segmentIndex) + { + return (segmentIndex / scale) * bitsPerRecord; + } + + unsigned indexOf(void* p) + { + assertT(segment->context, segment->almostContains(p)); + assertT(segment->context, segment->capacity()); + return indexOf(segment->indexOf(p)); + } + + void clearBit(unsigned i) + { + assertT(segment->context, wordOf(i) < size()); + + vm::clearBit(data, i); + } + + void setBit(unsigned i) + { + assertT(segment->context, wordOf(i) < size()); + + vm::markBit(data, i); + } + + void clearOnlyIndex(unsigned index) + { + clearBits(data, bitsPerRecord, index); + } + + void clearOnly(unsigned segmentIndex) + { + clearOnlyIndex(indexOf(segmentIndex)); + } + + void clearOnly(void* p) + { + clearOnlyIndex(indexOf(p)); + } + + void clear(void* p) + { + clearOnly(p); + if (child) + child->clear(p); + } + + void setOnlyIndex(unsigned index, unsigned v = 1) + { + setBits(data, bitsPerRecord, index, v); + } + + void setOnly(unsigned segmentIndex, unsigned v = 1) + { + setOnlyIndex(indexOf(segmentIndex), v); + } + + void setOnly(void* p, unsigned v = 1) + { + setOnlyIndex(indexOf(p), v); + } + + void set(void* p, unsigned v = 1) + { + setOnly(p, v); + assertT(segment->context, get(p) == v); + if (child) + child->set(p, v); + } + +#ifdef USE_ATOMIC_OPERATIONS + void markAtomic(void* p) + { + assertT(segment->context, bitsPerRecord == 1); + markBitAtomic(data, indexOf(p)); + assertT(segment->context, getBit(data, indexOf(p))); + if (child) + child->markAtomic(p); + } +#endif + + unsigned get(void* p) + { + return getBits(data, bitsPerRecord, indexOf(p)); + } + }; + + Context* context; + uintptr_t* data; + unsigned position_; + unsigned capacity_; + Map* map; + + Segment(Context* context, + Map* map, + unsigned desired, + unsigned minimum, + int64_t available = INT64_MAX) + : context(context), data(0), position_(0), capacity_(0), map(map) + { + if (desired) { + if (minimum == 0) { + minimum = 1; + } + + assertT(context, desired >= minimum); + + capacity_ = desired; + + if (static_cast(footprint(capacity_)) > available) { + unsigned top = capacity_; + unsigned bottom = minimum; + unsigned target = available; + while (true) { + if (static_cast(footprint(capacity_)) > target) { + if (bottom == capacity_) { + break; + } else if (static_cast(footprint(capacity_ - 1)) + <= target) { + --capacity_; + break; + } + top = capacity_; + capacity_ = avg(bottom, capacity_); + } else if (static_cast(footprint(capacity_)) < target) { + if (top == capacity_ + or static_cast(footprint(capacity_ + 1)) >= target) { + break; + } + bottom = capacity_; + capacity_ = avg(top, capacity_); + } else { + break; + } + } + } + + while (data == 0) { + data = static_cast(local::allocate( + context, (footprint(capacity_)) * BytesPerWord, false)); + + if (data == 0) { + if (capacity_ > minimum) { + capacity_ = avg(minimum, capacity_); + if (capacity_ == 0) { + break; + } + } else { + data = static_cast(local::allocate( + context, (footprint(capacity_)) * BytesPerWord)); + } + } + } + + if (map) { + map->init(); + } + } + } + + Segment(Context* context, + Map* map, + uintptr_t* data, + unsigned position, + unsigned capacity) + : context(context), + data(data), + position_(position), + capacity_(capacity), + map(map) + { + if (map) { + map->init(); + } + } + + unsigned footprint(unsigned capacity) + { + return capacity + + (map and capacity ? map->calculateFootprint(capacity) : 0); + } + + unsigned capacity() + { + return capacity_; + } + + unsigned position() + { + return position_; + } + + unsigned remaining() + { + return capacity() - position(); + } + + void replaceWith(Segment* s) + { + if (data) { + free(context, data, (footprint(capacity())) * BytesPerWord); + } + data = s->data; + s->data = 0; + + position_ = s->position_; + s->position_ = 0; + + capacity_ = s->capacity_; + s->capacity_ = 0; + + if (s->map) { + if (map) { + map->replaceWith(s->map); + s->map = 0; + } else { + abort(context); + } + } else { + assertT(context, map == 0); + } + } + + bool contains(void* p) + { + return position() and p >= data and p < data + position(); + } + + bool almostContains(void* p) + { + return contains(p) or p == data + position(); + } + + void* get(unsigned offset) + { + assertT(context, offset <= position()); + return data + offset; + } + + unsigned indexOf(void* p) + { + assertT(context, almostContains(p)); + return static_cast(p) - data; + } + + void* allocate(unsigned size) + { + assertT(context, size); + assertT(context, position() + size <= capacity()); + + void* p = data + position(); + position_ += size; + return p; + } + + void dispose() + { + if (data) { + free(context, data, (footprint(capacity())) * BytesPerWord); + } + data = 0; + map = 0; + } +}; + +class Fixie { + public: + static const unsigned HasMask = 1 << 0; + static const unsigned Marked = 1 << 1; + static const unsigned Dirty = 1 << 2; + static const unsigned Dead = 1 << 3; + + Fixie(Context* c, unsigned size, bool hasMask, Fixie** handle, bool immortal) + : age(immortal ? FixieTenureThreshold + 1 : 0), + flags(hasMask ? HasMask : 0), + size(size), + next(0), + handle(0) + { + memset(mask(), 0, maskSize(size, hasMask)); + add(c, handle); + if (DebugFixies) { + fprintf(stderr, "make fixie %p of size %d\n", this, totalSize()); + } + } + + bool immortal() + { + return age == FixieTenureThreshold + 1; + } + + void add(Context* c UNUSED, Fixie** handle) + { + assertT(c, this->handle == 0); + assertT(c, next == 0); + + this->handle = handle; + if (handle) { + next = *handle; + if (next) + next->handle = &next; + *handle = this; + } else { + next = 0; + } + } + + void remove(Context* c UNUSED) + { + if (handle) { + assertT(c, *handle == this); + *handle = next; + } + if (next) { + next->handle = handle; + } + next = 0; + handle = 0; + } + + void move(Context* c, Fixie** handle) + { + if (DebugFixies) { + fprintf(stderr, "move fixie %p\n", this); + } + + remove(c); + add(c, handle); + } + + void** body() + { + return static_cast(static_cast(body_)); + } + + uintptr_t* mask() + { + return body_ + size; + } + + static unsigned maskSize(unsigned size, bool hasMask) + { + return hasMask * ceilingDivide(size, BitsPerWord) * BytesPerWord; + } + + static unsigned totalSize(unsigned size, bool hasMask) + { + return sizeof(Fixie) + (size * BytesPerWord) + maskSize(size, hasMask); + } + + unsigned totalSize() + { + return totalSize(size, hasMask()); + } + + bool hasMask() + { + return (flags & HasMask) != 0; + } + + bool marked() + { + return (flags & Marked) != 0; + } + + void marked(bool v) + { + if (v) { + flags |= Marked; + } else { + flags &= ~Marked; + } + } + + bool dirty() + { + return (flags & Dirty) != 0; + } + + void dirty(bool v) + { + if (v) { + flags |= Dirty; + } else { + flags &= ~Dirty; + } + } + + bool dead() + { + return (flags & Dead) != 0; + } + + void dead(bool v) + { + if (v) { + flags |= Dead; + } else { + flags &= ~Dead; + } + } + + // be sure to update e.g. TargetFixieSizeInBytes in bootimage.cpp if + // you add/remove/change fields in this class: + + uint16_t age; + uint16_t flags; + uint32_t size; + Fixie* next; + Fixie** handle; + uintptr_t body_[0]; +}; + +Fixie* fixie(void* body) +{ + return static_cast(body) - 1; +} + +void free(Context* c, Fixie** fixies, bool resetImmortal = false); + +class Context { + public: + Context(System* system, unsigned limit) + : system(system), + client(0), + count(0), + limit(limit), + lock(0), + immortalHeapStart(0), + immortalHeapEnd(0), + ageMap(&gen1, max(1, log(TenureThreshold)), 1, 0, false), + gen1(this, &ageMap, 0, 0), + nextAgeMap(&nextGen1, max(1, log(TenureThreshold)), 1, 0, false), + nextGen1(this, &nextAgeMap, 0, 0), + pointerMap(&gen2, 1, 1, 0, true), + pageMap(&gen2, + 1, + LikelyPageSizeInBytes / BytesPerWord, + &pointerMap, + true), + heapMap(&gen2, 1, pageMap.scale * 1024, &pageMap, true), + gen2(this, &heapMap, 0, 0), + nextPointerMap(&nextGen2, 1, 1, 0, true), + nextPageMap(&nextGen2, + 1, + LikelyPageSizeInBytes / BytesPerWord, + &nextPointerMap, + true), + nextHeapMap(&nextGen2, 1, nextPageMap.scale * 1024, &nextPageMap, true), + nextGen2(this, &nextHeapMap, 0, 0), + gen2Base(0), + incomingFootprint(0), + pendingAllocation(0), + tenureFootprint(0), + gen1Padding(0), + tenurePadding(0), + gen2Padding(0), + fixieTenureFootprint(0), + untenuredFixieFootprint(0), + tenuredFixieFootprint(0), + tenuredFixieCeiling(InitialTenuredFixieCeilingInBytes), + mode(Heap::MinorCollection), + fixies(0), + tenuredFixies(0), + dirtyTenuredFixies(0), + markedFixies(0), + visitedFixies(0), + lastCollectionTime(system->now()), + totalCollectionTime(0), + totalTime(0), + limitWasExceeded(false) + { + if (not system->success(system->make(&lock))) { + system->abort(); + } + } + + void dispose() + { + gen1.dispose(); + nextGen1.dispose(); + gen2.dispose(); + nextGen2.dispose(); + lock->dispose(); + } + + void disposeFixies() + { + free(this, &tenuredFixies, true); + free(this, &dirtyTenuredFixies, true); + free(this, &fixies, true); + } + + System* system; + Heap::Client* client; + + unsigned count; + unsigned limit; + + System::Mutex* lock; + + uintptr_t* immortalHeapStart; + uintptr_t* immortalHeapEnd; + + Segment::Map ageMap; + Segment gen1; + + Segment::Map nextAgeMap; + Segment nextGen1; + + Segment::Map pointerMap; + Segment::Map pageMap; + Segment::Map heapMap; + Segment gen2; + + Segment::Map nextPointerMap; + Segment::Map nextPageMap; + Segment::Map nextHeapMap; + Segment nextGen2; + + unsigned gen2Base; + + unsigned incomingFootprint; + int pendingAllocation; + unsigned tenureFootprint; + unsigned gen1Padding; + unsigned tenurePadding; + unsigned gen2Padding; + + unsigned fixieTenureFootprint; + unsigned untenuredFixieFootprint; + unsigned tenuredFixieFootprint; + unsigned tenuredFixieCeiling; + + Heap::CollectionType mode; + + Fixie* fixies; + Fixie* tenuredFixies; + Fixie* dirtyTenuredFixies; + Fixie* markedFixies; + Fixie* visitedFixies; + + int64_t lastCollectionTime; + int64_t totalCollectionTime; + int64_t totalTime; + + bool limitWasExceeded; +}; + +const char* segment(Context* c, void* p) +{ + if (c->gen1.contains(p)) { + return "gen1"; + } else if (c->nextGen1.contains(p)) { + return "nextGen1"; + } else if (c->gen2.contains(p)) { + return "gen2"; + } else if (c->nextGen2.contains(p)) { + return "nextGen2"; + } else { + return "none"; + } +} + +inline Aborter* getAborter(Context* c) +{ + return c->system; +} + +inline unsigned minimumNextGen1Capacity(Context* c) +{ + return c->gen1.position() - c->tenureFootprint + c->incomingFootprint + + c->gen1Padding; +} + +inline unsigned minimumNextGen2Capacity(Context* c) +{ + return c->gen2.position() + c->tenureFootprint + c->tenurePadding + + c->gen2Padding; +} + +inline bool oversizedGen2(Context* c) +{ + return c->gen2.capacity() > (InitialGen2CapacityInBytes / BytesPerWord) + and c->gen2.position() < (c->gen2.capacity() / 4); +} + +inline void initNextGen1(Context* c) +{ + new (&(c->nextAgeMap)) + Segment::Map(&(c->nextGen1), max(1, log(TenureThreshold)), 1, 0, false); + + unsigned minimum = minimumNextGen1Capacity(c); + unsigned desired = minimum; + + new (&(c->nextGen1)) Segment(c, &(c->nextAgeMap), desired, minimum); + + if (Verbose2) { + fprintf(stderr, + "init nextGen1 to %d bytes\n", + c->nextGen1.capacity() * BytesPerWord); + } +} + +inline void initNextGen2(Context* c) +{ + new (&(c->nextPointerMap)) Segment::Map(&(c->nextGen2), 1, 1, 0, true); + + new (&(c->nextPageMap)) Segment::Map(&(c->nextGen2), + 1, + LikelyPageSizeInBytes / BytesPerWord, + &(c->nextPointerMap), + true); + + new (&(c->nextHeapMap)) Segment::Map( + &(c->nextGen2), 1, c->pageMap.scale * 1024, &(c->nextPageMap), true); + + unsigned minimum = minimumNextGen2Capacity(c); + unsigned desired = minimum; + + if (not oversizedGen2(c)) { + desired *= 2; + } + + if (desired < InitialGen2CapacityInBytes / BytesPerWord) { + desired = InitialGen2CapacityInBytes / BytesPerWord; + } + + new (&(c->nextGen2)) Segment( + c, + &(c->nextHeapMap), + desired, + minimum, + static_cast(c->limit / BytesPerWord) + - (static_cast(c->count / BytesPerWord) + - c->gen2.footprint(c->gen2.capacity()) + - c->gen1.footprint(c->gen1.capacity()) + c->pendingAllocation)); + + if (Verbose2) { + fprintf(stderr, + "init nextGen2 to %d bytes\n", + c->nextGen2.capacity() * BytesPerWord); + } +} + +inline bool fresh(Context* c, void* o) +{ + return c->nextGen1.contains(o) or c->nextGen2.contains(o) + or (c->gen2.contains(o) and c->gen2.indexOf(o) >= c->gen2Base); +} + +inline bool wasCollected(Context* c, void* o) +{ + return o and (not fresh(c, o)) and fresh(c, get(o, 0)); +} + +inline void* follow(Context* c UNUSED, void* o) +{ + assertT(c, wasCollected(c, o)); + return fieldAtOffset(o, 0); +} + +inline void*& parent(Context* c UNUSED, void* o) +{ + assertT(c, wasCollected(c, o)); + return fieldAtOffset(o, BytesPerWord); +} + +inline uintptr_t* bitset(Context* c UNUSED, void* o) +{ + assertT(c, wasCollected(c, o)); + return &fieldAtOffset(o, BytesPerWord * 2); +} + +void free(Context* c, Fixie** fixies, bool resetImmortal) +{ + for (Fixie** p = fixies; *p;) { + Fixie* f = *p; + + if (f->immortal()) { + if (resetImmortal) { + if (DebugFixies) { + fprintf(stderr, "reset immortal fixie %p\n", f); + } + *p = f->next; + memset(f->mask(), 0, Fixie::maskSize(f->size, f->hasMask())); + f->next = 0; + f->handle = 0; + f->marked(false); + f->dirty(false); + } else { + p = &(f->next); + } + } else { + *p = f->next; + if (DebugFixies) { + fprintf(stderr, "free fixie %p\n", f); + } + free(c, f, f->totalSize()); + } + } +} + +void kill(Fixie* fixies) +{ + for (Fixie* f = fixies; f; f = f->next) { + if (!f->immortal()) { + f->dead(true); + } + } +} + +void killFixies(Context* c) +{ + assertT(c, c->markedFixies == 0); + + if (c->mode == Heap::MajorCollection) { + kill(c->tenuredFixies); + kill(c->dirtyTenuredFixies); + } + kill(c->fixies); +} + +void sweepFixies(Context* c) +{ + assertT(c, c->markedFixies == 0); + + if (c->mode == Heap::MajorCollection) { + free(c, &(c->tenuredFixies), true); + free(c, &(c->dirtyTenuredFixies), true); + + c->tenuredFixieFootprint = 0; + } + free(c, &(c->fixies)); + + c->untenuredFixieFootprint = 0; + + while (c->visitedFixies) { + Fixie* f = c->visitedFixies; + f->remove(c); + + if (not f->immortal()) { + ++f->age; + if (f->age > FixieTenureThreshold) { + f->age = FixieTenureThreshold; + } else if (static_cast(f->age + 1) == FixieTenureThreshold) { + c->fixieTenureFootprint += f->totalSize(); + } + } + + if (f->age >= FixieTenureThreshold) { + if (DebugFixies) { + fprintf(stderr, "tenure fixie %p (dirty: %d)\n", f, f->dirty()); + } + + if (not f->immortal()) { + c->tenuredFixieFootprint += f->totalSize(); + } + + if (f->dirty()) { + f->add(c, &(c->dirtyTenuredFixies)); + } else { + f->add(c, &(c->tenuredFixies)); + } + } else { + c->untenuredFixieFootprint += f->totalSize(); + + f->add(c, &(c->fixies)); + } + + f->marked(false); + } + + c->tenuredFixieCeiling + = max(c->tenuredFixieFootprint * 2, InitialTenuredFixieCeilingInBytes); +} + +inline void* copyTo(Context* c, Segment* s, void* o, unsigned size) +{ + assertT(c, s->remaining() >= size); + void* dst = s->allocate(size); + c->client->copy(o, dst); + return dst; +} + +bool immortalHeapContains(Context* c, void* p) +{ + return p < c->immortalHeapEnd and p >= c->immortalHeapStart; +} + +void* copy2(Context* c, void* o) +{ + unsigned size = c->client->copiedSizeInWords(o); + + if (c->gen2.contains(o)) { + assertT(c, c->mode == Heap::MajorCollection); + + return copyTo(c, &(c->nextGen2), o, size); + } else if (c->gen1.contains(o)) { + unsigned age = c->ageMap.get(o); + if (age == TenureThreshold) { + if (c->mode == Heap::MinorCollection) { + assertT(c, c->gen2.remaining() >= size); + + if (c->gen2Base == Top) { + c->gen2Base = c->gen2.position(); + } + + return copyTo(c, &(c->gen2), o, size); + } else { + return copyTo(c, &(c->nextGen2), o, size); + } + } else { + o = copyTo(c, &(c->nextGen1), o, size); + + c->nextAgeMap.setOnly(o, age + 1); + if (age + 1 == TenureThreshold) { + c->tenureFootprint += size; + } + + return o; + } + } else { + assertT(c, not c->nextGen1.contains(o)); + assertT(c, not c->nextGen2.contains(o)); + assertT(c, not immortalHeapContains(c, o)); + + o = copyTo(c, &(c->nextGen1), o, size); + + c->nextAgeMap.clear(o); + + return o; + } +} + +void* copy(Context* c, void* o) +{ + void* r = copy2(c, o); + + if (Debug) { + fprintf(stderr, + "copy %p (%s) to %p (%s)\n", + o, + segment(c, o), + r, + segment(c, r)); + } + + // leave a pointer to the copy in the original + fieldAtOffset(o, 0) = r; + + return r; +} + +void* update3(Context* c, void* o, bool* needsVisit) +{ + if (c->client->isFixed(o)) { + Fixie* f = fixie(o); + if ((not f->marked()) and (c->mode == Heap::MajorCollection + or f->age < FixieTenureThreshold)) { + if (DebugFixies) { + fprintf(stderr, "mark fixie %p\n", f); + } + f->marked(true); + f->dead(false); + f->move(c, &(c->markedFixies)); + } + *needsVisit = false; + return o; + } else if (immortalHeapContains(c, o)) { + *needsVisit = false; + return o; + } else if (wasCollected(c, o)) { + *needsVisit = false; + return follow(c, o); + } else { + *needsVisit = true; + return copy(c, o); + } +} + +void* update2(Context* c, void* o, bool* needsVisit) +{ + if (c->mode == Heap::MinorCollection and c->gen2.contains(o)) { + *needsVisit = false; + return o; + } + + return update3(c, o, needsVisit); +} + +void markDirty(Context* c, Fixie* f) +{ + if (not f->dirty()) { +#ifdef USE_ATOMIC_OPERATIONS + ACQUIRE(c->lock); +#endif + + if (not f->dirty()) { + f->dirty(true); + f->move(c, &(c->dirtyTenuredFixies)); + } + } +} + +void markClean(Context* c, Fixie* f) +{ + if (f->dirty()) { + f->dirty(false); + if (f->immortal()) { + f->remove(c); + } else { + f->move(c, &(c->tenuredFixies)); + } + } +} + +void updateHeapMap(Context* c, + void* p, + void* target, + unsigned offset, + void* result) +{ + Segment* seg; + Segment::Map* map; + + if (c->mode == Heap::MinorCollection) { + seg = &(c->gen2); + map = &(c->heapMap); + } else { + seg = &(c->nextGen2); + map = &(c->nextHeapMap); + } + + if (not(immortalHeapContains(c, result) + or (c->client->isFixed(result) + and fixie(result)->age >= FixieTenureThreshold) + or seg->contains(result))) { + if (target and c->client->isFixed(target)) { + Fixie* f = fixie(target); + assertT(c, offset == 0 or f->hasMask()); + + if (static_cast(f->age + 1) >= FixieTenureThreshold) { + if (DebugFixies) { + fprintf(stderr, + "dirty fixie %p at %d (%p): %p\n", + f, + offset, + f->body() + offset, + result); + } + + f->dirty(true); + markBit(f->mask(), offset); + } + } else if (seg->contains(p)) { + if (Debug) { + fprintf(stderr, + "mark %p (%s) at %p (%s)\n", + result, + segment(c, result), + p, + segment(c, p)); + } + + map->set(p); + } + } +} + +void* update(Context* c, + void** p, + void* target, + unsigned offset, + bool* needsVisit) +{ + if (maskAlignedPointer(*p) == 0) { + *needsVisit = false; + return 0; + } + + void* result = update2(c, maskAlignedPointer(*p), needsVisit); + + if (result) { + updateHeapMap(c, p, target, offset, result); + } + + return result; +} + +const uintptr_t BitsetExtensionBit + = (static_cast(1) << (BitsPerWord - 1)); + +void bitsetInit(uintptr_t* p) +{ + memset(p, 0, BytesPerWord); +} + +void bitsetClear(uintptr_t* p, unsigned start, unsigned end) +{ + if (end < BitsPerWord - 1) { + // do nothing + } else if (start < BitsPerWord - 1) { + memset(p + 1, 0, (wordOf(end + (BitsPerWord * 2) + 1)) * BytesPerWord); + } else { + unsigned startWord = wordOf(start + (BitsPerWord * 2) + 1); + unsigned endWord = wordOf(end + (BitsPerWord * 2) + 1); + if (endWord > startWord) { + memset(p + startWord + 1, 0, (endWord - startWord) * BytesPerWord); + } + } +} + +void bitsetSet(uintptr_t* p, unsigned i, bool v) +{ + if (i >= BitsPerWord - 1) { + i += (BitsPerWord * 2) + 1; + if (v) { + p[0] |= BitsetExtensionBit; + if (p[2] <= wordOf(i) - 3) + p[2] = wordOf(i) - 2; + } + } + + if (v) { + markBit(p, i); + } else { + clearBit(p, i); + } +} + +bool bitsetHasMore(uintptr_t* p) +{ + switch (*p) { + case 0: + return false; + + case BitsetExtensionBit: { + uintptr_t length = p[2]; + uintptr_t word = wordOf(p[1]); + for (; word < length; ++word) { + if (p[word + 3]) { + p[1] = indexOf(word, 0); + return true; + } + } + p[1] = indexOf(word, 0); + return false; + } + + default: + return true; + } +} + +unsigned bitsetNext(Context* c, uintptr_t* p) +{ + bool more UNUSED = bitsetHasMore(p); + assertT(c, more); + + switch (*p) { + case 0: + abort(c); + + case BitsetExtensionBit: { + uintptr_t i = p[1]; + uintptr_t word = wordOf(i); + assertT(c, word < p[2]); + for (uintptr_t bit = bitOf(i); bit < BitsPerWord; ++bit) { + if (p[word + 3] & (static_cast(1) << bit)) { + p[1] = indexOf(word, bit) + 1; + bitsetSet(p, p[1] + BitsPerWord - 2, false); + return p[1] + BitsPerWord - 2; + } + } + abort(c); + } + + default: { + for (unsigned i = 0; i < BitsPerWord - 1; ++i) { + if (*p & (static_cast(1) << i)) { + bitsetSet(p, i, false); + return i; + } + } + abort(c); + } + } +} + +void collect(Context* c, void** p, void* target, unsigned offset) +{ + void* original = maskAlignedPointer(*p); + void* parent_ = 0; + + if (Debug) { + fprintf(stderr, + "update %p (%s) at %p (%s)\n", + maskAlignedPointer(*p), + segment(c, *p), + p, + segment(c, p)); + } + + bool needsVisit; + local::set(p, update(c, maskAlignedPointer(p), target, offset, &needsVisit)); + + if (Debug) { + fprintf(stderr, + " result: %p (%s) (visit? %d)\n", + maskAlignedPointer(*p), + segment(c, *p), + needsVisit); + } + + if (not needsVisit) + return; + +visit : { + void* copy = follow(c, original); + + class Walker : public Heap::Walker { + public: + Walker(Context* c, void* copy, uintptr_t* bitset) + : c(c), + copy(copy), + bitset(bitset), + first(0), + second(0), + last(0), + visits(0), + total(0) + { + } + + virtual bool visit(unsigned offset) + { + if (Debug) { + fprintf(stderr, + " update %p (%s) at %p - offset %d from %p (%s)\n", + get(copy, offset), + segment(c, get(copy, offset)), + getp(copy, offset), + offset, + copy, + segment(c, copy)); + } + + bool needsVisit; + void* childCopy + = update(c, getp(copy, offset), copy, offset, &needsVisit); + + if (Debug) { + fprintf(stderr, + " result: %p (%s) (visit? %d)\n", + childCopy, + segment(c, childCopy), + needsVisit); + } + + ++total; + + if (total == 3) { + bitsetInit(bitset); + } + + if (needsVisit) { + ++visits; + + if (visits == 1) { + first = offset; + } else if (visits == 2) { + second = offset; + } + } else { + local::set(copy, offset, childCopy); + } + + if (visits > 1 and total > 2 and (second or needsVisit)) { + bitsetClear(bitset, last, offset); + last = offset; + + if (second) { + bitsetSet(bitset, second, true); + second = 0; + } + + if (needsVisit) { + bitsetSet(bitset, offset, true); + } + } + + return true; + } + + Context* c; + void* copy; + uintptr_t* bitset; + unsigned first; + unsigned second; + unsigned last; + unsigned visits; + unsigned total; + } walker(c, copy, bitset(c, original)); + + if (Debug) { + fprintf(stderr, "walk %p (%s)\n", copy, segment(c, copy)); + } + + c->client->walk(copy, &walker); + + if (walker.visits) { + // descend + if (walker.visits > 1) { + parent(c, original) = parent_; + parent_ = original; + } + + original = get(copy, walker.first); + local::set(copy, walker.first, follow(c, original)); + goto visit; + } else { + // ascend + original = parent_; + } +} + + if (original) { + void* copy = follow(c, original); + + class Walker : public Heap::Walker { + public: + Walker(Context* c, uintptr_t* bitset) + : c(c), bitset(bitset), next(0), total(0) + { + } + + virtual bool visit(unsigned offset) + { + switch (++total) { + case 1: + return true; + + case 2: + next = offset; + return true; + + case 3: + next = bitsetNext(c, bitset); + return false; + + default: + abort(c); + } + } + + Context* c; + uintptr_t* bitset; + unsigned next; + unsigned total; + } walker(c, bitset(c, original)); + + if (Debug) { + fprintf(stderr, "scan %p\n", copy); + } + + c->client->walk(copy, &walker); + + assertT(c, walker.total > 1); + + if (walker.total == 3 and bitsetHasMore(bitset(c, original))) { + parent_ = original; + } else { + parent_ = parent(c, original); + } + + if (Debug) { + fprintf(stderr, + " next is %p (%s) at %p - offset %d from %p (%s)\n", + get(copy, walker.next), + segment(c, get(copy, walker.next)), + getp(copy, walker.next), + walker.next, + copy, + segment(c, copy)); + } + + original = get(copy, walker.next); + local::set(copy, walker.next, follow(c, original)); + goto visit; + } else { + return; + } +} + +void collect(Context* c, void** p) +{ + collect(c, p, 0, 0); +} + +void collect(Context* c, void* target, unsigned offset) +{ + collect(c, getp(target, offset), target, offset); +} + +void visitDirtyFixies(Context* c, Fixie** p) +{ + while (*p) { + Fixie* f = *p; + + bool wasDirty UNUSED = false; + bool clean = true; + uintptr_t* mask = f->mask(); + + unsigned word = 0; + unsigned bit = 0; + unsigned wordLimit = wordOf(f->size); + unsigned bitLimit = bitOf(f->size); + + if (DebugFixies) { + fprintf(stderr, "clean fixie %p\n", f); + } + + for (; word <= wordLimit and (word < wordLimit or bit < bitLimit); ++word) { + if (mask[word]) { + for (; bit < BitsPerWord and (word < wordLimit or bit < bitLimit); + ++bit) { + unsigned index = indexOf(word, bit); + + if (getBit(mask, index)) { + wasDirty = true; + + clearBit(mask, index); + + if (DebugFixies) { + fprintf(stderr, + "clean fixie %p at %d (%p)\n", + f, + index, + f->body() + index); + } + + collect(c, f->body(), index); + + if (getBit(mask, index)) { + clean = false; + } + } + } + bit = 0; + } + } + + if (DebugFixies) { + fprintf(stderr, "done cleaning fixie %p\n", f); + } + + assertT(c, wasDirty); + + if (clean) { + markClean(c, f); + } else { + p = &(f->next); + } + } +} + +void visitMarkedFixies(Context* c) +{ + while (c->markedFixies) { + Fixie* f = c->markedFixies; + f->remove(c); + + if (DebugFixies) { + fprintf(stderr, "visit fixie %p\n", f); + } + + class Walker : public Heap::Walker { + public: + Walker(Context* c, void** p) : c(c), p(p) + { + } + + virtual bool visit(unsigned offset) + { + local::collect(c, p, offset); + return true; + } + + Context* c; + void** p; + } w(c, f->body()); + + c->client->walk(f->body(), &w); + + f->move(c, &(c->visitedFixies)); + } +} + +void collect(Context* c, + Segment::Map* map, + unsigned start, + unsigned end, + bool* dirty, + bool expectDirty UNUSED) +{ + bool wasDirty UNUSED = false; + for (Segment::Map::Iterator it(map, start, end); it.hasMore();) { + wasDirty = true; + if (map->child) { + assertT(c, map->scale > 1); + unsigned s = it.next(); + unsigned e = s + map->scale; + + map->clearOnly(s); + bool childDirty = false; + collect(c, map->child, s, e, &childDirty, true); + if (childDirty) { + map->setOnly(s); + *dirty = true; + } + } else { + assertT(c, map->scale == 1); + void** p = reinterpret_cast(map->segment->get(it.next())); + + map->clearOnly(p); + if (c->nextGen1.contains(*p)) { + map->setOnly(p); + *dirty = true; + } else { + collect(c, p); + + if (not c->gen2.contains(*p)) { + map->setOnly(p); + *dirty = true; + } + } + } + } + + assertT(c, wasDirty or not expectDirty); +} + +void collect2(Context* c) +{ + c->gen2Base = Top; + c->tenureFootprint = 0; + c->fixieTenureFootprint = 0; + c->gen1Padding = 0; + c->tenurePadding = 0; + + if (c->mode == Heap::MajorCollection) { + c->gen2Padding = 0; + } + + if (c->mode == Heap::MinorCollection and c->gen2.position()) { + unsigned start = 0; + unsigned end = start + c->gen2.position(); + bool dirty; + collect(c, &(c->heapMap), start, end, &dirty, false); + } + + if (c->mode == Heap::MinorCollection) { + visitDirtyFixies(c, &(c->dirtyTenuredFixies)); + } + + class Visitor : public Heap::Visitor { + public: + Visitor(Context* c) : c(c) + { + } + + virtual void visit(void* p) + { + local::collect(c, static_cast(p)); + visitMarkedFixies(c); + } + + Context* c; + } v(c); + + c->client->visitRoots(&v); +} + +bool limitExceeded(Context* c, int pendingAllocation) +{ + unsigned count = c->count + pendingAllocation + - (c->gen2.remaining() * BytesPerWord); + + if (Verbose) { + if (count > c->limit) { + if (not c->limitWasExceeded) { + c->limitWasExceeded = true; + fprintf(stderr, "heap limit %d exceeded: %d\n", c->limit, count); + } + } else if (c->limitWasExceeded) { + c->limitWasExceeded = false; + fprintf( + stderr, "heap limit %d no longer exceeded: %d\n", c->limit, count); + } + } + + return count > c->limit; +} + +void collect(Context* c) +{ + if (limitExceeded(c, c->pendingAllocation) or oversizedGen2(c) + or c->tenureFootprint + c->tenurePadding > c->gen2.remaining() + or c->fixieTenureFootprint + c->tenuredFixieFootprint + > c->tenuredFixieCeiling) { + if (Verbose) { + if (limitExceeded(c, c->pendingAllocation)) { + fprintf(stderr, "low memory causes "); + } else if (oversizedGen2(c)) { + fprintf(stderr, "oversized gen2 causes "); + } else if (c->tenureFootprint + c->tenurePadding > c->gen2.remaining()) { + fprintf(stderr, "undersized gen2 causes "); + } else { + fprintf(stderr, "fixie ceiling causes "); + } + } + + c->mode = Heap::MajorCollection; + } + + int64_t then; + if (Verbose) { + if (c->mode == Heap::MajorCollection) { + fprintf(stderr, "major collection\n"); + } else { + fprintf(stderr, "minor collection\n"); + } + + then = c->system->now(); + } + + initNextGen1(c); + + if (c->mode == Heap::MajorCollection) { + initNextGen2(c); + } + + collect2(c); + + c->gen1.replaceWith(&(c->nextGen1)); + if (c->mode == Heap::MajorCollection) { + c->gen2.replaceWith(&(c->nextGen2)); + } + + sweepFixies(c); + + if (Verbose) { + int64_t now = c->system->now(); + int64_t collection = now - then; + int64_t run = then - c->lastCollectionTime; + c->totalCollectionTime += collection; + c->totalTime += collection + run; + c->lastCollectionTime = now; + + fprintf(stderr, + " - collect: %4dms; " + "total: %4dms; " + "run: %4dms; " + "total: %4dms\n", + static_cast(collection), + static_cast(c->totalCollectionTime), + static_cast(run), + static_cast(c->totalTime - c->totalCollectionTime)); + + fprintf(stderr, + " - gen1: %8d/%8d bytes\n", + c->gen1.position() * BytesPerWord, + c->gen1.capacity() * BytesPerWord); + + fprintf(stderr, + " - gen2: %8d/%8d bytes\n", + c->gen2.position() * BytesPerWord, + c->gen2.capacity() * BytesPerWord); + + fprintf(stderr, + " - untenured fixies: %8d bytes\n", + c->untenuredFixieFootprint); + + fprintf(stderr, + " - tenured fixies: %8d bytes\n", + c->tenuredFixieFootprint); + } +} + +void* allocate(Context* c, size_t size, bool limit) +{ + ACQUIRE(c->lock); + + if (DebugAllocation) { + size = pad(size) + 2 * BytesPerWord; + } + + if ((not limit) or size + c->count < c->limit) { + void* p = c->system->tryAllocate(size); + if (p) { + c->count += size; + + if (DebugAllocation) { + static_cast(p)[0] = 0x22377322; + static_cast(p)[(size / BytesPerWord) - 1] = 0x22377322; + return static_cast(p) + 1; + } else { + return p; + } + } + } + return 0; +} + +void* tryAllocate(Context* c, size_t size) +{ + return allocate(c, size, true); +} + +void* allocate(Context* c, size_t size) +{ + void* p = allocate(c, size, false); + expect(c->system, p); + + return p; +} + +void free(Context* c, const void* p, size_t size) +{ + ACQUIRE(c->lock); + + if (DebugAllocation) { + size = pad(size) + 2 * BytesPerWord; + + memset(const_cast(p), 0xFE, size - (2 * BytesPerWord)); + + p = static_cast(p) - 1; + + expect(c->system, static_cast(p)[0] == 0x22377322); + + expect(c->system, + static_cast(p)[(size / BytesPerWord) - 1] + == 0x22377322); + } + + expect(c->system, c->count >= size); + + c->system->free(p); + c->count -= size; +} + +void free_(Context* c, const void* p, size_t size) +{ + free(c, p, size); +} + +class MyHeap : public Heap { + public: + MyHeap(System* system, unsigned limit) : c(system, limit) + { + } + + virtual void setClient(Heap::Client* client) + { + assertT(&c, c.client == 0); + c.client = client; + } + + virtual void setImmortalHeap(uintptr_t* start, unsigned sizeInWords) + { + c.immortalHeapStart = start; + c.immortalHeapEnd = start + sizeInWords; + } + + virtual unsigned remaining() + { + return c.limit - c.count; + } + + virtual unsigned limit() + { + return c.limit; + } + + virtual bool limitExceeded(int pendingAllocation = 0) + { + return local::limitExceeded(&c, pendingAllocation); + } + + virtual void* tryAllocate(size_t size) + { + return local::tryAllocate(&c, size); + } + + virtual void* allocate(size_t size) + { + return local::allocate(&c, size); + } + + virtual void free(const void* p, size_t size) + { + free_(&c, p, size); + } + + virtual void collect(CollectionType type, + unsigned incomingFootprint, + int pendingAllocation) + { + c.mode = type; + c.incomingFootprint = incomingFootprint; + c.pendingAllocation = pendingAllocation; + + local::collect(&c); + } + + virtual unsigned fixedFootprint(unsigned sizeInWords, bool objectMask) + { + return Fixie::totalSize(sizeInWords, objectMask); + } + + void* allocateFixed(Alloc* allocator, + unsigned sizeInWords, + bool objectMask, + Fixie** handle, + bool immortal) + { + expect(&c, not limitExceeded()); + + unsigned total = Fixie::totalSize(sizeInWords, objectMask); + void* p = allocator->allocate(total); + + expect(&c, not limitExceeded()); + + return (new (p) Fixie(&c, sizeInWords, objectMask, handle, immortal)) + ->body(); + } + + virtual void* allocateFixed(Alloc* allocator, + unsigned sizeInWords, + bool objectMask) + { + return allocateFixed( + allocator, sizeInWords, objectMask, &(c.fixies), false); + } + + virtual void* allocateImmortalFixed(Alloc* allocator, + unsigned sizeInWords, + bool objectMask) + { + return allocateFixed(allocator, sizeInWords, objectMask, 0, true); + } + + bool needsMark(void* p) + { + assertT(&c, c.client->isFixed(p) or (not immortalHeapContains(&c, p))); + + if (c.client->isFixed(p)) { + return fixie(p)->age >= FixieTenureThreshold; + } else { + return c.gen2.contains(p) or c.nextGen2.contains(p); + } + } + + bool targetNeedsMark(void* target) + { + return target and not c.gen2.contains(target) + and not c.nextGen2.contains(target) + and not immortalHeapContains(&c, target) + and not(c.client->isFixed(target) + and fixie(target)->age >= FixieTenureThreshold); + } + + virtual void mark(void* p, unsigned offset, unsigned count) + { + if (needsMark(p)) { +#ifndef USE_ATOMIC_OPERATIONS + ACQUIRE(c.lock); +#endif + + if (c.client->isFixed(p)) { + Fixie* f = fixie(p); + assertT(&c, offset == 0 or f->hasMask()); + + bool dirty = false; + for (unsigned i = 0; i < count; ++i) { + void** target = static_cast(p) + offset + i; + if (targetNeedsMark(maskAlignedPointer(*target))) { + if (DebugFixies) { + fprintf(stderr, + "dirty fixie %p at %d (%p): %p\n", + f, + offset, + f->body() + offset, + maskAlignedPointer(*target)); + } + + dirty = true; +#ifdef USE_ATOMIC_OPERATIONS + markBitAtomic(f->mask(), offset + i); +#else + markBit(f->mask(), offset + i); +#endif + assertT(&c, getBit(f->mask(), offset + i)); + } + } + + if (dirty) + markDirty(&c, f); + } else { + Segment::Map* map; + if (c.gen2.contains(p)) { + map = &(c.heapMap); + } else { + assertT(&c, c.nextGen2.contains(p)); + map = &(c.nextHeapMap); + } + + for (unsigned i = 0; i < count; ++i) { + void** target = static_cast(p) + offset + i; + if (targetNeedsMark(maskAlignedPointer(*target))) { +#ifdef USE_ATOMIC_OPERATIONS + map->markAtomic(target); +#else + map->set(target); +#endif + } + } + } + } + } + + virtual void pad(void* p) + { + if (c.gen1.contains(p)) { + if (c.ageMap.get(p) == TenureThreshold) { + ++c.tenurePadding; + } else { + ++c.gen1Padding; + } + } else if (c.gen2.contains(p)) { + ++c.gen2Padding; + } else { + ++c.gen1Padding; + } + } + + virtual void* follow(void* p) + { + if (p == 0 or c.client->isFixed(p)) { + return p; + } else if (wasCollected(&c, p)) { + if (Debug) { + fprintf(stderr, + "follow %p (%s) to %p (%s)\n", + p, + segment(&c, p), + local::follow(&c, p), + segment(&c, local::follow(&c, p))); + } + + return local::follow(&c, p); + } else { + return p; + } + } + + virtual void postVisit() + { + killFixies(&c); + } + + virtual Status status(void* p) + { + p = maskAlignedPointer(p); + + if (p == 0) { + return Null; + } else if (c.client->isFixed(p)) { + Fixie* f = fixie(p); + return f->dead() ? Unreachable : (static_cast(f->age + 1) + < FixieTenureThreshold + ? Reachable + : Tenured); + } else if (c.nextGen1.contains(p)) { + return Reachable; + } else if (c.nextGen2.contains(p) or immortalHeapContains(&c, p) + or (c.gen2.contains(p) + and (c.mode == Heap::MinorCollection + or c.gen2.indexOf(p) >= c.gen2Base))) { + return Tenured; + } else if (wasCollected(&c, p)) { + return Reachable; + } else { + return Unreachable; + } + } + + virtual CollectionType collectionType() + { + return c.mode; + } + + virtual void disposeFixies() + { + c.disposeFixies(); + } + + virtual void dispose() + { + c.dispose(); + assertT(&c, c.count == 0); + c.system->free(this); + } + + Context c; +}; + +} // namespace local + +} // namespace + +namespace vm { + +Heap* makeHeap(System* system, unsigned limit) +{ + return new (system->tryAllocate(sizeof(local::MyHeap))) + local::MyHeap(system, limit); +} + +} // namespace vm diff --git a/sgx-jvm/avian/src/heapdump.cpp b/sgx-jvm/avian/src/heapdump.cpp new file mode 100644 index 0000000000..813d8cfd84 --- /dev/null +++ b/sgx-jvm/avian/src/heapdump.cpp @@ -0,0 +1,115 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/machine.h" +#include "avian/heapwalk.h" + +using namespace vm; + +namespace { + +namespace local { + +enum { Root, Size, ClassName, Push, Pop }; + +void write1(FILE* out, uint8_t v) +{ + size_t n UNUSED = fwrite(&v, 1, 1, out); +} + +void write4(FILE* out, uint32_t v) +{ + uint8_t b[] = {static_cast(v >> 24), + static_cast((v >> 16) & 0xFF), + static_cast((v >> 8) & 0xFF), + static_cast(v & 0xFF)}; + + size_t n UNUSED = fwrite(b, 4, 1, out); +} + +void writeString(FILE* out, int8_t* p, unsigned size) +{ + write4(out, size); + size_t n UNUSED = fwrite(p, size, 1, out); +} + +unsigned objectSize(Thread* t, object o) +{ + return extendedSize(t, o, baseSize(t, o, objectClass(t, o))); +} + +} // namespace local + +} // namespace + +namespace vm { + +void dumpHeap(Thread* t, FILE* out) +{ + class Visitor : public HeapVisitor { + public: + Visitor(Thread* t, FILE* out) : t(t), out(out), nextNumber(1) + { + } + + virtual void root() + { + write1(out, local::Root); + } + + virtual unsigned visitNew(object p) + { + if (p) { + unsigned number = nextNumber++; + local::write4(out, number); + + local::write1(out, local::Size); + local::write4(out, local::objectSize(t, p)); + + if (objectClass(t, p) == type(t, GcClass::Type)) { + GcByteArray* name = static_cast(p)->name(); + if (name) { + local::write1(out, local::ClassName); + local::writeString(out, name->body().begin(), name->length() - 1); + } + } + + return number; + } else { + return 0; + } + } + + virtual void visitOld(object, unsigned number) + { + local::write4(out, number); + } + + virtual void push(object, unsigned, unsigned) + { + local::write1(out, local::Push); + } + + virtual void pop() + { + local::write1(out, local::Pop); + } + + Thread* t; + FILE* out; + unsigned nextNumber; + } visitor(t, out); + + HeapWalker* w = makeHeapWalker(t, &visitor); + w->visitAllRoots(); + w->dispose(); +} + +} // namespace vm diff --git a/sgx-jvm/avian/src/heapwalk.cpp b/sgx-jvm/avian/src/heapwalk.cpp new file mode 100644 index 0000000000..24efa50246 --- /dev/null +++ b/sgx-jvm/avian/src/heapwalk.cpp @@ -0,0 +1,335 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/machine.h" +#include "avian/heapwalk.h" + +using namespace vm; + +namespace { + +namespace local { + +const uintptr_t PointerShift = log(BytesPerWord); + +class Context; + +class Set : public HeapMap { + public: + class Entry { + public: + object value; + uint32_t number; + int next; + }; + + static unsigned footprint(unsigned capacity) + { + return sizeof(Set) + pad(sizeof(int) * capacity) + + pad(sizeof(Set::Entry) * capacity); + } + + Set(Context* context, unsigned capacity) + : context(context), + index(reinterpret_cast(reinterpret_cast(this) + + sizeof(Set))), + entries(reinterpret_cast(reinterpret_cast(index) + + pad(sizeof(int) * capacity))), + size(0), + capacity(capacity) + { + } + + virtual int find(object value); + + virtual void dispose(); + + Context* context; + int* index; + Entry* entries; + unsigned size; + unsigned capacity; +}; + +class Stack { + public: + class Entry { + public: + object value; + int offset; + }; + + static const unsigned Capacity = 4096; + + Stack(Stack* next) : next(next), entryCount(0) + { + } + + Stack* next; + unsigned entryCount; + Entry entries[Capacity]; +}; + +class Context { + public: + Context(Thread* thread) : thread(thread), objects(0), stack(0) + { + } + + void dispose() + { + if (objects) { + objects->dispose(); + } + + while (stack) { + Stack* dead = stack; + stack = dead->next; + thread->m->heap->free(dead, sizeof(Stack)); + } + } + + Thread* thread; + Set* objects; + Stack* stack; +}; + +void push(Context* c, object p, int offset) +{ + if (c->stack == 0 or c->stack->entryCount == Stack::Capacity) { + c->stack = new (c->thread->m->heap->allocate(sizeof(Stack))) + Stack(c->stack); + } + Stack::Entry* e = c->stack->entries + (c->stack->entryCount++); + e->value = p; + e->offset = offset; +} + +bool pop(Context* c, object* p, int* offset) +{ + if (c->stack) { + if (c->stack->entryCount == 0) { + if (c->stack->next) { + Stack* dead = c->stack; + c->stack = dead->next; + c->thread->m->heap->free(dead, sizeof(Stack)); + } else { + return false; + } + } + Stack::Entry* e = c->stack->entries + (--c->stack->entryCount); + *p = e->value; + *offset = e->offset; + return true; + } else { + return false; + } +} + +unsigned hash(object p, unsigned capacity) +{ + return (reinterpret_cast(p) >> PointerShift) & (capacity - 1); +} + +Set::Entry* find(Context* c, object p) +{ + if (c->objects == 0) + return 0; + + for (int i = c->objects->index[hash(p, c->objects->capacity)]; i >= 0;) { + Set::Entry* e = c->objects->entries + i; + if (e->value == p) { + return e; + } + i = e->next; + } + + return 0; +} + +int Set::find(object value) +{ + Set::Entry* e = local::find(context, value); + if (e) { + return e->number; + } else { + return -1; + } +} + +void Set::dispose() +{ + context->thread->m->heap->free(this, footprint(capacity)); +} + +Set::Entry* add(Context* c UNUSED, Set* set, object p, uint32_t number) +{ + assertT(c->thread, set->size < set->capacity); + + unsigned index = hash(p, set->capacity); + + int offset = set->size++; + Set::Entry* e = set->entries + offset; + e->value = p; + e->number = number; + e->next = set->index[index]; + set->index[index] = offset; + return e; +} + +Set::Entry* add(Context* c, object p) +{ + if (c->objects == 0 or c->objects->size == c->objects->capacity) { + unsigned capacity; + if (c->objects) { + capacity = c->objects->capacity * 2; + } else { + capacity = 4096; // must be power of two + } + + Set* set = new (c->thread->m->heap->allocate(Set::footprint(capacity))) + Set(c, capacity); + + memset(set->index, 0xFF, sizeof(int) * capacity); + + if (c->objects) { + for (unsigned i = 0; i < c->objects->capacity; ++i) { + for (int j = c->objects->index[i]; j >= 0;) { + Set::Entry* e = c->objects->entries + j; + add(c, set, e->value, e->number); + j = e->next; + } + } + + c->thread->m->heap->free(c->objects, + Set::footprint(c->objects->capacity)); + } + + c->objects = set; + } + + return add(c, c->objects, p, 0); +} + +inline object get(object o, unsigned offsetInWords) +{ + return static_cast(maskAlignedPointer( + fieldAtOffset(o, offsetInWords * BytesPerWord))); +} + +unsigned walk(Context* c, HeapVisitor* v, object p) +{ + Thread* t = c->thread; + object root = p; + int nextChildOffset; + + v->root(); + +visit : { + Set::Entry* e = find(c, p); + if (e) { + v->visitOld(p, e->number); + } else { + e = add(c, p); + e->number = v->visitNew(p); + + nextChildOffset = walkNext(t, p, -1); + if (nextChildOffset != -1) { + goto children; + } + } +} + + goto pop; + +children : { + v->push(p, find(c, p)->number, nextChildOffset); + push(c, p, nextChildOffset); + p = get(p, nextChildOffset); + goto visit; +} + +pop : { + if (pop(c, &p, &nextChildOffset)) { + v->pop(); + nextChildOffset = walkNext(t, p, nextChildOffset); + if (nextChildOffset >= 0) { + goto children; + } else { + goto pop; + } + } +} + + return find(c, root)->number; +} + +class MyHeapWalker : public HeapWalker { + public: + MyHeapWalker(Thread* t, HeapVisitor* v) : context(t), visitor(v) + { + add(&context, 0)->number = v->visitNew(0); + } + + virtual unsigned visitRoot(object root) + { + return walk(&context, visitor, root); + } + + virtual void visitAllRoots() + { + class Visitor : public Heap::Visitor { + public: + Visitor(Context* c, HeapVisitor* v) : c(c), v(v) + { + } + + virtual void visit(void* p) + { + walk(c, + v, + static_cast(maskAlignedPointer(*static_cast(p)))); + } + + Context* c; + HeapVisitor* v; + } v(&context, visitor); + + visitRoots(context.thread->m, &v); + } + + virtual HeapMap* map() + { + return context.objects; + } + + virtual void dispose() + { + context.dispose(); + context.thread->m->heap->free(this, sizeof(MyHeapWalker)); + } + + Context context; + HeapVisitor* visitor; +}; + +} // namespace local + +} // namespace + +namespace vm { + +HeapWalker* makeHeapWalker(Thread* t, HeapVisitor* v) +{ + return new (t->m->heap->allocate(sizeof(local::MyHeapWalker))) + local::MyHeapWalker(t, v); +} + +} // namespace vm diff --git a/sgx-jvm/avian/src/i386.S b/sgx-jvm/avian/src/i386.S new file mode 100644 index 0000000000..47acd677e3 --- /dev/null +++ b/sgx-jvm/avian/src/i386.S @@ -0,0 +1,145 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/types.h" + +#define LOCAL(x) .L##x + +#if defined __APPLE__ \ + || ((defined __MINGW32__ || defined __CYGWIN32__) && ! defined __x86_64__) +# define GLOBAL(x) _##x +#else +# define GLOBAL(x) x +#endif + +.text + +#define CHECKPOINT_THREAD 4 +#define CHECKPOINT_STACK 24 +#define CHECKPOINT_BASE 28 + +.globl GLOBAL(vmNativeCall) +GLOBAL(vmNativeCall): + pushl %ebp + movl %esp,%ebp + + // 8(%ebp): function + // 12(%ebp): stack + // 16(%ebp): stackSize + // 20(%ebp): returnType + + // reserve space for arguments + movl 16(%ebp),%ecx + + subl %ecx,%esp + +//# ifdef __APPLE__ + // align to a 16 byte boundary + andl $0xFFFFFFF0,%esp +//# endif + + // copy arguments into place + movl $0,%ecx + jmp LOCAL(test) + +LOCAL(loop): + movl %ecx,%eax + movl %ecx,%edx + addl %esp,%edx + addl 12(%ebp),%eax + movl (%eax),%eax + movl %eax,(%edx) + addl $4,%ecx + +LOCAL(test): + cmpl 16(%ebp),%ecx + jb LOCAL(loop) + + // call function + call *8(%ebp) + + // handle return value based on expected type + movl 20(%ebp),%ecx + +LOCAL(void): + cmpl $VOID_TYPE,%ecx + jne LOCAL(int64) + jmp LOCAL(exit) + +LOCAL(int64): + cmpl $INT64_TYPE,%ecx + jne LOCAL(float) + jmp LOCAL(exit) + +LOCAL(float): + cmpl $FLOAT_TYPE,%ecx + jne LOCAL(double) + fstps 8(%ebp) + movl 8(%ebp),%eax + jmp LOCAL(exit) + +LOCAL(double): + cmpl $DOUBLE_TYPE,%ecx + jne LOCAL(exit) + fstpl 8(%ebp) + movl 8(%ebp),%eax + movl 12(%ebp),%edx + +LOCAL(exit): + movl %ebp,%esp + popl %ebp + ret + +.globl GLOBAL(vmJump) +GLOBAL(vmJump): + movl 4(%esp),%esi + movl 8(%esp),%ebp + movl 16(%esp),%ebx + movl 20(%esp),%eax + movl 24(%esp),%edx + movl 12(%esp),%esp + jmp *%esi + +#define VMRUN_FRAME_SIZE 24 + +.globl GLOBAL(vmRun) +GLOBAL(vmRun): + // 8(%ebp): function + // 12(%ebp): arguments + // 16(%ebp): checkpoint + pushl %ebp + movl %esp,%ebp + subl $VMRUN_FRAME_SIZE,%esp + + movl %ebx,8(%esp) + movl %esi,12(%esp) + movl %edi,16(%esp) + + movl 12(%ebp),%eax + movl %eax,4(%esp) + + movl 16(%ebp),%ecx + movl CHECKPOINT_THREAD(%ecx),%eax + movl %eax,0(%esp) + + movl %esp,CHECKPOINT_STACK(%ecx) + + call *8(%ebp) + +.globl GLOBAL(vmRun_returnAddress) +GLOBAL(vmRun_returnAddress): + + movl 8(%esp),%ebx + movl 12(%esp),%esi + movl 16(%esp),%edi + + addl $VMRUN_FRAME_SIZE,%esp + popl %ebp + ret diff --git a/sgx-jvm/avian/src/i386.masm b/sgx-jvm/avian/src/i386.masm new file mode 100644 index 0000000000..e44c82c2c5 --- /dev/null +++ b/sgx-jvm/avian/src/i386.masm @@ -0,0 +1,134 @@ +comment # + Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. + + ORIGIN: https://github.com/gkvas/avian/tree/wince +# + +.586 +.MODEL FLAT, C + +VOID_TYPE equ 0 +INT8_TYPE equ 1 +INT16_TYPE equ 2 +INT32_TYPE equ 3 +INT64_TYPE equ 4 +FLOAT_TYPE equ 5 +DOUBLE_TYPE equ 6 +POINTER_TYPE equ 7 + +CHECKPOINT_THREAD equ 4 +CHECKPOINT_STACK equ 24 +CHECKPOINT_BASE equ 28 + +_TEXT SEGMENT + +public C vmNativeCall +vmNativeCall: + push ebp + mov ebp,esp + mov ecx,ds:dword ptr[16+ebp] + sub esp,ecx + mov ecx,0 + jmp Ltest + +Lloop: + mov eax,ecx + mov edx,ecx + add edx,esp + add eax,ds:dword ptr[12+ebp] + mov eax,ds:dword ptr[eax] + mov ds:dword ptr[edx],eax + add ecx,4 + +Ltest: + cmp ecx,ds:dword ptr[16+ebp] + jb Lloop + call dword ptr[8+ebp] + mov ecx,ds:dword ptr[20+ebp] + +Lvoid: + cmp ecx,offset VOID_TYPE + jne Lint64 + jmp Lexit + +Lint64: + cmp ecx,offset INT64_TYPE + jne Lfloat + jmp Lexit + +Lfloat: + cmp ecx,offset FLOAT_TYPE + jne Ldouble + fstp ds:dword ptr[8+ebp] + mov eax,ds:dword ptr[8+ebp] + jmp Lexit + +Ldouble: + cmp ecx,offset DOUBLE_TYPE + jne Lexit + fstp ds:qword ptr[8+ebp] + mov eax,ds:dword ptr[8+ebp] + mov edx,ds:dword ptr[12+ebp] + +Lexit: + mov esp,ebp + pop ebp + ret + +public C vmJump +vmJump: + mov esi,ds:dword ptr[4+esp] + mov ebp,ds:dword ptr[8+esp] + mov ebx,ds:dword ptr[16+esp] + mov eax,ds:dword ptr[20+esp] + mov edx,ds:dword ptr[24+esp] + mov esp,ds:dword ptr[12+esp] + jmp esi + +VMRUN_FRAME_SIZE equ 24 + +public C vmRun_ +vmRun_: + ; 8(%ebp): function + ; 12(%ebp): arguments + ; 16(%ebp): checkpoint + push ebp + mov ebp,esp + sub esp,offset VMRUN_FRAME_SIZE + + mov ds:dword ptr[8+esp],ebx + mov ds:dword ptr[12+esp],esi + mov ds:dword ptr[16+esp],edi + + mov eax,ds:dword ptr[12+ebp] + mov ds:dword ptr[4+esp],eax + + mov ecx,ds:dword ptr[16+ebp] + mov eax,ds:dword ptr[CHECKPOINT_THREAD+ecx] + mov ds:dword ptr[0+esp],eax + + mov ds:dword ptr[CHECKPOINT_STACK+ecx],esp + + call dword ptr[8+ebp] + +public C vmRun_returnAddress +vmRun_returnAddress: + + mov ebx,ds:dword ptr[8+esp] + mov esi,ds:dword ptr[12+esp] + mov edi,ds:dword ptr[16+esp] + + add esp,offset VMRUN_FRAME_SIZE + pop ebp + ret + +_TEXT ENDS +END \ No newline at end of file diff --git a/sgx-jvm/avian/src/interpret.cpp b/sgx-jvm/avian/src/interpret.cpp new file mode 100644 index 0000000000..12ddc9e0bb --- /dev/null +++ b/sgx-jvm/avian/src/interpret.cpp @@ -0,0 +1,3609 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/common.h" +#include +#include +#include "avian/constants.h" +#include "avian/machine.h" +#include "avian/processor.h" +#include "avian/process.h" +#include "avian/arch.h" + +#include +#include +#include + +using namespace vm; +using namespace avian::system; + +namespace local { + +const unsigned FrameBaseOffset = 0; +const unsigned FrameNextOffset = 1; +const unsigned FrameMethodOffset = 2; +const unsigned FrameIpOffset = 3; +const unsigned FrameFootprint = 4; + +class Thread : public vm::Thread { + public: + Thread(Machine* m, GcThread* javaThread, vm::Thread* parent) + : vm::Thread(m, javaThread, parent), + ip(0), + sp(0), + frame(-1), + code(0), + stackPointers(0) + { + } + + unsigned ip; + unsigned sp; + int frame; + GcCode* code; + List* stackPointers; + uintptr_t stack[0]; +}; + +inline void pushObject(Thread* t, object o) +{ + if (DebugStack) { + fprintf(stderr, "push object %p at %d\n", o, t->sp); + } + + assertT(t, t->sp + 1 < stackSizeInWords(t) / 2); + t->stack[(t->sp * 2)] = ObjectTag; + t->stack[(t->sp * 2) + 1] = reinterpret_cast(o); + ++t->sp; +} + +inline void pushInt(Thread* t, uint32_t v) +{ + if (DebugStack) { + fprintf(stderr, "push int %d at %d\n", v, t->sp); + } + + assertT(t, t->sp + 1 < stackSizeInWords(t) / 2); + t->stack[(t->sp * 2)] = IntTag; + t->stack[(t->sp * 2) + 1] = v; + ++t->sp; +} + +inline void pushFloat(Thread* t, float v) +{ + pushInt(t, floatToBits(v)); +} + +inline void pushLong(Thread* t, uint64_t v) +{ + if (DebugStack) { + fprintf(stderr, "push long %" LLD " at %d\n", v, t->sp); + } + + pushInt(t, v >> 32); + pushInt(t, v & 0xFFFFFFFF); +} + +inline void pushDouble(Thread* t, double v) +{ + uint64_t w = doubleToBits(v); + pushLong(t, w); +} + +inline object popObject(Thread* t) +{ + if (DebugStack) { + fprintf(stderr, + "pop object %p at %d\n", + reinterpret_cast(t->stack[((t->sp - 1) * 2) + 1]), + t->sp - 1); + } + + assertT(t, t->stack[(t->sp - 1) * 2] == ObjectTag); + return reinterpret_cast(t->stack[((--t->sp) * 2) + 1]); +} + +inline uint32_t popInt(Thread* t) +{ + if (DebugStack) { + fprintf(stderr, + "pop int %" LD " at %d\n", + t->stack[((t->sp - 1) * 2) + 1], + t->sp - 1); + } + + assertT(t, t->stack[(t->sp - 1) * 2] == IntTag); + return t->stack[((--t->sp) * 2) + 1]; +} + +inline float popFloat(Thread* t) +{ + return bitsToFloat(popInt(t)); +} + +inline uint64_t popLong(Thread* t) +{ + if (DebugStack) { + fprintf(stderr, + "pop long %" LLD " at %d\n", + (static_cast(t->stack[((t->sp - 2) * 2) + 1]) << 32) + | static_cast(t->stack[((t->sp - 1) * 2) + 1]), + t->sp - 2); + } + + uint64_t a = popInt(t); + uint64_t b = popInt(t); + return (b << 32) | a; +} + +inline double popDouble(Thread* t) +{ + uint64_t v = popLong(t); + return bitsToDouble(v); +} + +inline object peekObject(Thread* t, unsigned index) +{ + if (DebugStack) { + fprintf(stderr, + "peek object %p at %d\n", + reinterpret_cast(t->stack[(index * 2) + 1]), + index); + } + + assertT(t, index < stackSizeInWords(t) / 2); + assertT(t, t->stack[index * 2] == ObjectTag); + return reinterpret_cast(t->stack[(index * 2) + 1]); +} + +inline uint32_t peekInt(Thread* t, unsigned index) +{ + if (DebugStack) { + fprintf( + stderr, "peek int %" LD " at %d\n", t->stack[(index * 2) + 1], index); + } + + assertT(t, index < stackSizeInWords(t) / 2); + assertT(t, t->stack[index * 2] == IntTag); + return t->stack[(index * 2) + 1]; +} + +inline uint64_t peekLong(Thread* t, unsigned index) +{ + if (DebugStack) { + fprintf(stderr, + "peek long %" LLD " at %d\n", + (static_cast(t->stack[(index * 2) + 1]) << 32) + | static_cast(t->stack[((index + 1) * 2) + 1]), + index); + } + + return (static_cast(peekInt(t, index)) << 32) + | static_cast(peekInt(t, index + 1)); +} + +inline void pokeObject(Thread* t, unsigned index, object value) +{ + if (DebugStack) { + fprintf(stderr, "poke object %p at %d\n", value, index); + } + + t->stack[index * 2] = ObjectTag; + t->stack[(index * 2) + 1] = reinterpret_cast(value); +} + +inline void pokeInt(Thread* t, unsigned index, uint32_t value) +{ + if (DebugStack) { + fprintf(stderr, "poke int %d at %d\n", value, index); + } + + t->stack[index * 2] = IntTag; + t->stack[(index * 2) + 1] = value; +} + +inline void pokeLong(Thread* t, unsigned index, uint64_t value) +{ + if (DebugStack) { + fprintf(stderr, "poke long %" LLD " at %d\n", value, index); + } + + pokeInt(t, index, value >> 32); + pokeInt(t, index + 1, value & 0xFFFFFFFF); +} + +inline object* pushReference(Thread* t, object o) +{ + if (o) { + expect(t, t->sp + 1 < stackSizeInWords(t) / 2); + pushObject(t, o); + return reinterpret_cast(t->stack + ((t->sp - 1) * 2) + 1); + } else { + return 0; + } +} + +inline int frameNext(Thread* t, int frame) +{ + return peekInt(t, frame + FrameNextOffset); +} + +inline GcMethod* frameMethod(Thread* t, int frame) +{ + return cast(t, peekObject(t, frame + FrameMethodOffset)); +} + +inline unsigned frameIp(Thread* t, int frame) +{ + return peekInt(t, frame + FrameIpOffset); +} + +inline unsigned frameBase(Thread* t, int frame) +{ + return peekInt(t, frame + FrameBaseOffset); +} + +inline object localObject(Thread* t, unsigned index) +{ + return peekObject(t, frameBase(t, t->frame) + index); +} + +inline uint32_t localInt(Thread* t, unsigned index) +{ + return peekInt(t, frameBase(t, t->frame) + index); +} + +inline uint64_t localLong(Thread* t, unsigned index) +{ + return peekLong(t, frameBase(t, t->frame) + index); +} + +inline void setLocalObject(Thread* t, unsigned index, object value) +{ + pokeObject(t, frameBase(t, t->frame) + index, value); +} + +inline void setLocalInt(Thread* t, unsigned index, uint32_t value) +{ + pokeInt(t, frameBase(t, t->frame) + index, value); +} + +inline void setLocalLong(Thread* t, unsigned index, uint64_t value) +{ + pokeLong(t, frameBase(t, t->frame) + index, value); +} + +void pushFrame(Thread* t, GcMethod* method) +{ + PROTECT(t, method); + + unsigned parameterFootprint = method->parameterFootprint(); + unsigned base = t->sp - parameterFootprint; + unsigned locals = parameterFootprint; + + if (method->flags() & ACC_SYNCHRONIZED) { + // Try to acquire the monitor before doing anything else. + // Otherwise, if we were to push the frame first, we risk trying + // to release a monitor we never successfully acquired when we try + // to pop the frame back off. + if (method->flags() & ACC_STATIC) { + acquire(t, getJClass(t, method->class_())); + } else { + acquire(t, peekObject(t, base)); + } + } + + if (t->frame >= 0) { + pokeInt(t, t->frame + FrameIpOffset, t->ip); + } + t->ip = 0; + + if ((method->flags() & ACC_NATIVE) == 0) { + t->code = method->code(); + + locals = t->code->maxLocals(); + + memset(t->stack + ((base + parameterFootprint) * 2), + 0, + (locals - parameterFootprint) * BytesPerWord * 2); + } + + unsigned frame = base + locals; + pokeInt(t, frame + FrameNextOffset, t->frame); + t->frame = frame; + + t->sp = frame + FrameFootprint; + + pokeInt(t, frame + FrameBaseOffset, base); + pokeObject(t, frame + FrameMethodOffset, method); + pokeInt(t, t->frame + FrameIpOffset, 0); +} + +void popFrame(Thread* t) +{ + GcMethod* method = frameMethod(t, t->frame); + + if (method->flags() & ACC_SYNCHRONIZED) { + if (method->flags() & ACC_STATIC) { + release(t, getJClass(t, method->class_())); + } else { + release(t, peekObject(t, frameBase(t, t->frame))); + } + } + + t->sp = frameBase(t, t->frame); + t->frame = frameNext(t, t->frame); + if (t->frame >= 0) { + t->code = frameMethod(t, t->frame)->code(); + t->ip = frameIp(t, t->frame); + } else { + t->code = 0; + t->ip = 0; + } +} + +class MyStackWalker : public Processor::StackWalker { + public: + MyStackWalker(Thread* t, int frame) : t(t), frame(frame) + { + } + + virtual void walk(Processor::StackVisitor* v) + { + for (int frame = this->frame; frame >= 0; frame = frameNext(t, frame)) { + MyStackWalker walker(t, frame); + if (not v->visit(&walker)) { + break; + } + } + } + + virtual GcMethod* method() + { + return frameMethod(t, frame); + } + + virtual int ip() + { + return frameIp(t, frame); + } + + virtual unsigned count() + { + unsigned count = 0; + for (int frame = this->frame; frame >= 0; frame = frameNext(t, frame)) { + ++count; + } + return count; + } + + Thread* t; + int frame; +}; + +inline void checkStack(Thread* t, GcMethod* method) +{ + if (UNLIKELY(t->sp + method->parameterFootprint() + + method->code()->maxLocals() + FrameFootprint + + method->code()->maxStack() > stackSizeInWords(t) / 2)) { + throwNew(t, GcStackOverflowError::Type); + } +} + +void pushResult(Thread* t, unsigned returnCode, uint64_t result, bool indirect) +{ + switch (returnCode) { + case ByteField: + case BooleanField: + if (DebugRun) { + fprintf(stderr, "result: %d\n", static_cast(result)); + } + pushInt(t, static_cast(result)); + break; + + case CharField: + if (DebugRun) { + fprintf(stderr, "result: %d\n", static_cast(result)); + } + pushInt(t, static_cast(result)); + break; + + case ShortField: + if (DebugRun) { + fprintf(stderr, "result: %d\n", static_cast(result)); + } + pushInt(t, static_cast(result)); + break; + + case FloatField: + case IntField: + if (DebugRun) { + fprintf(stderr, "result: %d\n", static_cast(result)); + } + pushInt(t, result); + break; + + case DoubleField: + case LongField: + if (DebugRun) { + fprintf(stderr, "result: %" LLD "\n", result); + } + pushLong(t, result); + break; + + case ObjectField: + if (indirect) { + if (DebugRun) { + fprintf( + stderr, + "result: %p at %p\n", + static_cast(result) == 0 + ? 0 + : *reinterpret_cast(static_cast(result)), + reinterpret_cast(static_cast(result))); + } + pushObject( + t, + static_cast(result) == 0 + ? 0 + : *reinterpret_cast(static_cast(result))); + } else { + if (DebugRun) { + fprintf(stderr, "result: %p\n", reinterpret_cast(result)); + } + pushObject(t, reinterpret_cast(result)); + } + break; + + case VoidField: + break; + + default: + abort(t); + } +} + +void marshalArguments(Thread* t, + uintptr_t* args, + uint8_t* types, + unsigned sp, + GcMethod* method, + bool fastCallingConvention) +{ + MethodSpecIterator it( + t, reinterpret_cast(method->spec()->body().begin())); + + unsigned argOffset = 0; + unsigned typeOffset = 0; + + while (it.hasNext()) { + unsigned type = fieldType(t, fieldCode(t, *it.next())); + if (types) { + types[typeOffset++] = type; + } + + switch (type) { + case INT8_TYPE: + case INT16_TYPE: + case INT32_TYPE: + case FLOAT_TYPE: + args[argOffset++] = peekInt(t, sp++); + break; + + case DOUBLE_TYPE: + case INT64_TYPE: { + uint64_t v = peekLong(t, sp); + memcpy(args + argOffset, &v, 8); + argOffset += fastCallingConvention ? 2 : (8 / BytesPerWord); + sp += 2; + } break; + + case POINTER_TYPE: { + if (fastCallingConvention) { + args[argOffset++] = reinterpret_cast(peekObject(t, sp++)); + } else { + object* v = reinterpret_cast(t->stack + ((sp++) * 2) + 1); + if (*v == 0) { + v = 0; + } + args[argOffset++] = reinterpret_cast(v); + } + } break; + + default: + abort(t); + } + } +} + +unsigned invokeNativeSlow(Thread* t, GcMethod* method, void* function) +{ + PROTECT(t, method); + + pushFrame(t, method); + + unsigned footprint = method->parameterFootprint() + 1; + if (method->flags() & ACC_STATIC) { + ++footprint; + } + unsigned count = method->parameterCount() + 2; + + THREAD_RUNTIME_ARRAY(t, uintptr_t, args, footprint); + unsigned argOffset = 0; + THREAD_RUNTIME_ARRAY(t, uint8_t, types, count); + unsigned typeOffset = 0; + + RUNTIME_ARRAY_BODY(args)[argOffset++] = reinterpret_cast(t); + RUNTIME_ARRAY_BODY(types)[typeOffset++] = POINTER_TYPE; + + GcJclass* jclass = 0; + PROTECT(t, jclass); + + unsigned sp; + if (method->flags() & ACC_STATIC) { + sp = frameBase(t, t->frame); + jclass = getJClass(t, method->class_()); + RUNTIME_ARRAY_BODY(args)[argOffset++] + = reinterpret_cast(&jclass); + } else { + sp = frameBase(t, t->frame); + object* v = reinterpret_cast(t->stack + ((sp++) * 2) + 1); + if (*v == 0) { + v = 0; + } + RUNTIME_ARRAY_BODY(args)[argOffset++] = reinterpret_cast(v); + } + RUNTIME_ARRAY_BODY(types)[typeOffset++] = POINTER_TYPE; + + marshalArguments(t, + RUNTIME_ARRAY_BODY(args) + argOffset, + RUNTIME_ARRAY_BODY(types) + typeOffset, + sp, + method, + false); + + unsigned returnCode = method->returnCode(); + unsigned returnType = fieldType(t, returnCode); + uint64_t result; + + if (DebugRun) { + signed char *cname = method->class_() && method->class_()->name() ? method->class_()->name()->body().begin() : (signed char*) "?"; + signed char *mname = method->name() ? method->name()->body().begin() : (signed char*) "?"; + fprintf(stderr, + "invoke native method %s.%s\n", + cname, mname); + } + + { + ENTER(t, Thread::IdleState); + + bool noThrow = t->checkpoint->noThrow; + t->checkpoint->noThrow = true; + THREAD_RESOURCE(t, bool, noThrow, t->checkpoint->noThrow = noThrow); + + result = vm::dynamicCall(function, + RUNTIME_ARRAY_BODY(args), + RUNTIME_ARRAY_BODY(types), + count, + footprint * BytesPerWord, + returnType); + } + + if (DebugRun) { + fprintf(stderr, + "return from native method %s.%s\n", + frameMethod(t, t->frame)->class_()->name()->body().begin(), + frameMethod(t, t->frame)->name()->body().begin()); + } + + popFrame(t); + + if (UNLIKELY(t->exception)) { + GcThrowable* exception = t->exception; + t->exception = 0; + throw_(t, exception); + } + + pushResult(t, returnCode, result, true); + + return returnCode; +} + +unsigned invokeNative(Thread* t, GcMethod* method) +{ + PROTECT(t, method); + + resolveNative(t, method); + + GcNative* native = getMethodRuntimeData(t, method)->native(); + if (native->fast()) { + pushFrame(t, method); + + uint64_t result; + { + THREAD_RESOURCE0(t, popFrame(static_cast(t))); + + unsigned footprint = method->parameterFootprint(); + THREAD_RUNTIME_ARRAY(t, uintptr_t, args, footprint); + unsigned sp = frameBase(t, t->frame); + unsigned argOffset = 0; + if ((method->flags() & ACC_STATIC) == 0) { + RUNTIME_ARRAY_BODY(args)[argOffset++] + = reinterpret_cast(peekObject(t, sp++)); + } + + marshalArguments( + t, RUNTIME_ARRAY_BODY(args) + argOffset, 0, sp, method, true); + + if(method->returnCode() != VoidField) { + result = reinterpret_cast(native->function())( + t, method, RUNTIME_ARRAY_BODY(args)); + } + else { + result = 0; + reinterpret_cast(native->function())( + t, method, RUNTIME_ARRAY_BODY(args)); + } + } + + pushResult(t, method->returnCode(), result, false); + + return method->returnCode(); + } else { + return invokeNativeSlow(t, method, native->function()); + } +} + +inline void store(Thread* t, unsigned index) +{ + memcpy(t->stack + ((frameBase(t, t->frame) + index) * 2), + t->stack + ((--t->sp) * 2), + BytesPerWord * 2); +} + +bool isNaN(double v) +{ + return fpclassify(v) == FP_NAN; +} + +bool isNaN(float v) +{ + return fpclassify(v) == FP_NAN; +} + +uint64_t findExceptionHandler(Thread* t, GcMethod* method, unsigned ip) +{ + PROTECT(t, method); + + GcExceptionHandlerTable* eht = cast( + t, method->code()->exceptionHandlerTable()); + + if (eht) { + for (unsigned i = 0; i < eht->length(); ++i) { + uint64_t eh = eht->body()[i]; + + if (ip - 1 >= exceptionHandlerStart(eh) + and ip - 1 < exceptionHandlerEnd(eh)) { + GcClass* catchType = 0; + if (exceptionHandlerCatchType(eh)) { + GcThrowable* e = t->exception; + t->exception = 0; + PROTECT(t, e); + + PROTECT(t, eht); + catchType = resolveClassInPool( + t, method, exceptionHandlerCatchType(eh) - 1); + + if (catchType) { + eh = eht->body()[i]; + t->exception = e; + } else { + // can't find what we're supposed to catch - move on. + continue; + } + } + + if (exceptionMatch(t, catchType, t->exception)) { + return eh; + } + } + } + } + + return 0; +} + +uint64_t findExceptionHandler(Thread* t, int frame) +{ + return findExceptionHandler(t, frameMethod(t, frame), frameIp(t, frame)); +} + +void pushField(Thread* t, object target, GcField* field) +{ + switch (field->code()) { + case ByteField: + case BooleanField: + pushInt(t, fieldAtOffset(target, field->offset())); + break; + + case CharField: + case ShortField: + pushInt(t, fieldAtOffset(target, field->offset())); + break; + + case FloatField: + case IntField: + pushInt(t, fieldAtOffset(target, field->offset())); + break; + + case DoubleField: + case LongField: + pushLong(t, fieldAtOffset(target, field->offset())); + break; + + case ObjectField: + pushObject(t, fieldAtOffset(target, field->offset())); + break; + + default: + abort(t); + } +} + +void safePoint(Thread* t) +{ + if (UNLIKELY(t->m->exclusive)) { + ENTER(t, Thread::IdleState); + } +} + +object interpret3(Thread* t, const int base) +{ + unsigned instruction = nop; + unsigned& ip = t->ip; + unsigned& sp = t->sp; + int& frame = t->frame; + GcCode*& code = t->code; + GcMethod* method = 0; + PROTECT(t, method); + GcThrowable*& exception = t->exception; + uintptr_t* stack = t->stack; + + code = frameMethod(t, frame)->code(); + + if (UNLIKELY(exception)) { + goto throw_; + } + +loop: + instruction = code->body()[ip++]; + + if (DebugRun) { + GcMethod *method_ = frameMethod(t, frame); + signed char *cname = method_->class_() && method_->class_()->name() ? method_->class_()->name()->body().begin() : (signed char*) "?"; + signed char *mname = method_->name() ? method_->name()->body().begin() : (signed char*) "?"; + fprintf(stderr, + "ip: %d; instruction: 0x%x in %s.%s ", + ip - 1, + instruction, cname, mname); + + int line = findLineNumber(t, frameMethod(t, frame), ip); + switch (line) { + case NativeLine: + fprintf(stderr, "(native)\n"); + break; + case UnknownLine: + fprintf(stderr, "(unknown line)\n"); + break; + default: + fprintf(stderr, "(line %d)\n", line); + } + } + + switch (instruction) { + case aaload: { + int32_t index = popInt(t); + object array = popObject(t); + + if (LIKELY(array)) { + if (LIKELY(index >= 0 + and static_cast(index) + < objectArrayLength(t, array))) { + pushObject(t, objectArrayBody(t, array, index)); + } else { + exception = makeThrowable(t, + GcArrayIndexOutOfBoundsException::Type, + "%d not in [0,%d)", + index, + objectArrayLength(t, array)); + goto throw_; + } + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + goto throw_; + } + } + goto loop; + + case aastore: { + object value = popObject(t); + int32_t index = popInt(t); + object array = popObject(t); + + if (LIKELY(array)) { + if (LIKELY(index >= 0 + and static_cast(index) + < objectArrayLength(t, array))) { + setField(t, array, ArrayBody + (index * BytesPerWord), value); + } else { + exception = makeThrowable(t, + GcArrayIndexOutOfBoundsException::Type, + "%d not in [0,%d)", + index, + objectArrayLength(t, array)); + goto throw_; + } + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + goto throw_; + } + } + goto loop; + + case aconst_null: { + pushObject(t, 0); + } + goto loop; + + case aload: { + pushObject(t, localObject(t, code->body()[ip++])); + } + goto loop; + + case aload_0: { + pushObject(t, localObject(t, 0)); + } + goto loop; + + case aload_1: { + pushObject(t, localObject(t, 1)); + } + goto loop; + + case aload_2: { + pushObject(t, localObject(t, 2)); + } + goto loop; + + case aload_3: { + pushObject(t, localObject(t, 3)); + } + goto loop; + + case anewarray: { + int32_t count = popInt(t); + + if (LIKELY(count >= 0)) { + uint16_t index = codeReadInt16(t, code, ip); + + GcClass* class_ = resolveClassInPool(t, frameMethod(t, frame), index - 1); + + pushObject(t, makeObjectArray(t, class_, count)); + } else { + exception + = makeThrowable(t, GcNegativeArraySizeException::Type, "%d", count); + goto throw_; + } + } + goto loop; + + case areturn: { + object result = popObject(t); + if (frame > base) { + popFrame(t); + pushObject(t, result); + goto loop; + } else { + return result; + } + } + goto loop; + + case arraylength: { + object array = popObject(t); + if (LIKELY(array)) { + pushInt(t, fieldAtOffset(array, BytesPerWord)); + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + goto throw_; + } + } + goto loop; + + case astore: { + store(t, code->body()[ip++]); + } + goto loop; + + case astore_0: { + store(t, 0); + } + goto loop; + + case astore_1: { + store(t, 1); + } + goto loop; + + case astore_2: { + store(t, 2); + } + goto loop; + + case astore_3: { + store(t, 3); + } + goto loop; + + case athrow: { + exception = cast(t, popObject(t)); + if (UNLIKELY(exception == 0)) { + exception = makeThrowable(t, GcNullPointerException::Type); + } + } + goto throw_; + + case baload: { + int32_t index = popInt(t); + object array = popObject(t); + + if (LIKELY(array)) { + if (objectClass(t, array) == type(t, GcBooleanArray::Type)) { + GcBooleanArray* a = cast(t, array); + if (LIKELY(index >= 0 + and static_cast(index) < a->length())) { + pushInt(t, a->body()[index]); + } else { + exception = makeThrowable(t, + GcArrayIndexOutOfBoundsException::Type, + "%d not in [0,%d)", + index, + a->length()); + goto throw_; + } + } else { + GcByteArray* a = cast(t, array); + if (LIKELY(index >= 0 + and static_cast(index) < a->length())) { + pushInt(t, a->body()[index]); + } else { + exception = makeThrowable(t, + GcArrayIndexOutOfBoundsException::Type, + "%d not in [0,%d)", + index, + a->length()); + goto throw_; + } + } + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + goto throw_; + } + } + goto loop; + + case bastore: { + int8_t value = popInt(t); + int32_t index = popInt(t); + object array = popObject(t); + + if (LIKELY(array)) { + if (objectClass(t, array) == type(t, GcBooleanArray::Type)) { + GcBooleanArray* a = cast(t, array); + if (LIKELY(index >= 0 + and static_cast(index) < a->length())) { + a->body()[index] = value; + } else { + exception = makeThrowable(t, + GcArrayIndexOutOfBoundsException::Type, + "%d not in [0,%d)", + index, + a->length()); + goto throw_; + } + } else { + GcByteArray* a = cast(t, array); + if (LIKELY(index >= 0 + and static_cast(index) < a->length())) { + a->body()[index] = value; + } else { + exception = makeThrowable(t, + GcArrayIndexOutOfBoundsException::Type, + "%d not in [0,%d)", + index, + a->length()); + goto throw_; + } + } + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + goto throw_; + } + } + goto loop; + + case bipush: { + pushInt(t, static_cast(code->body()[ip++])); + } + goto loop; + + case caload: { + int32_t index = popInt(t); + object array = popObject(t); + + if (LIKELY(array)) { + GcCharArray* a = cast(t, array); + if (LIKELY(index >= 0 and static_cast(index) < a->length())) { + pushInt(t, a->body()[index]); + } else { + exception = makeThrowable(t, + GcArrayIndexOutOfBoundsException::Type, + "%d not in [0,%d)", + index, + a->length()); + goto throw_; + } + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + goto throw_; + } + } + goto loop; + + case castore: { + uint16_t value = popInt(t); + int32_t index = popInt(t); + object array = popObject(t); + + if (LIKELY(array)) { + GcCharArray* a = cast(t, array); + if (LIKELY(index >= 0 and static_cast(index) < a->length())) { + a->body()[index] = value; + } else { + exception = makeThrowable(t, + GcArrayIndexOutOfBoundsException::Type, + "%d not in [0,%d)", + index, + a->length()); + goto throw_; + } + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + goto throw_; + } + } + goto loop; + + case checkcast: { + uint16_t index = codeReadInt16(t, code, ip); + + if (peekObject(t, sp - 1)) { + GcClass* class_ = resolveClassInPool(t, frameMethod(t, frame), index - 1); + if (UNLIKELY(exception)) + goto throw_; + + if (not instanceOf(t, class_, peekObject(t, sp - 1))) { + exception = makeThrowable( + t, + GcClassCastException::Type, + "%s as %s", + objectClass(t, peekObject(t, sp - 1))->name()->body().begin(), + class_->name()->body().begin()); + goto throw_; + } + } + } + goto loop; + + case d2f: { + pushFloat(t, static_cast(popDouble(t))); + } + goto loop; + + case d2i: { + double f = popDouble(t); + switch (fpclassify(f)) { + case FP_NAN: + pushInt(t, 0); + break; + case FP_INFINITE: + pushInt(t, signbit(f) ? INT32_MIN : INT32_MAX); + break; + default: + pushInt(t, + f >= INT32_MAX + ? INT32_MAX + : (f <= INT32_MIN ? INT32_MIN : static_cast(f))); + break; + } + } + goto loop; + + case d2l: { + double f = popDouble(t); + switch (fpclassify(f)) { + case FP_NAN: + pushLong(t, 0); + break; + case FP_INFINITE: + pushLong(t, signbit(f) ? INT64_MIN : INT64_MAX); + break; + default: + pushLong(t, + f >= INT64_MAX + ? INT64_MAX + : (f <= INT64_MIN ? INT64_MIN : static_cast(f))); + break; + } + } + goto loop; + + case dadd: { + double b = popDouble(t); + double a = popDouble(t); + + pushDouble(t, a + b); + } + goto loop; + + case daload: { + int32_t index = popInt(t); + object array = popObject(t); + + if (LIKELY(array)) { + GcDoubleArray* a = cast(t, array); + if (LIKELY(index >= 0 and static_cast(index) < a->length())) { + pushLong(t, a->body()[index]); + } else { + exception = makeThrowable(t, + GcArrayIndexOutOfBoundsException::Type, + "%d not in [0,%d)", + index, + a->length()); + goto throw_; + } + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + goto throw_; + } + } + goto loop; + + case dastore: { + double value = popDouble(t); + int32_t index = popInt(t); + object array = popObject(t); + + if (LIKELY(array)) { + GcDoubleArray* a = cast(t, array); + if (LIKELY(index >= 0 and static_cast(index) < a->length())) { + memcpy(&a->body()[index], &value, sizeof(uint64_t)); + } else { + exception = makeThrowable(t, + GcArrayIndexOutOfBoundsException::Type, + "%d not in [0,%d)", + index, + a->length()); + goto throw_; + } + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + goto throw_; + } + } + goto loop; + + case dcmpg: { + double b = popDouble(t); + double a = popDouble(t); + + if (isNaN(a) or isNaN(b)) { + pushInt(t, 1); + } + if (a < b) { + pushInt(t, static_cast(-1)); + } else if (a > b) { + pushInt(t, 1); + } else if (a == b) { + pushInt(t, 0); + } else { + pushInt(t, 1); + } + } + goto loop; + + case dcmpl: { + double b = popDouble(t); + double a = popDouble(t); + + if (isNaN(a) or isNaN(b)) { + pushInt(t, static_cast(-1)); + } + if (a < b) { + pushInt(t, static_cast(-1)); + } else if (a > b) { + pushInt(t, 1); + } else if (a == b) { + pushInt(t, 0); + } else { + pushInt(t, static_cast(-1)); + } + } + goto loop; + + case dconst_0: { + pushDouble(t, 0); + } + goto loop; + + case dconst_1: { + pushDouble(t, 1); + } + goto loop; + + case ddiv: { + double b = popDouble(t); + double a = popDouble(t); + + pushDouble(t, a / b); + } + goto loop; + + case dmul: { + double b = popDouble(t); + double a = popDouble(t); + + pushDouble(t, a * b); + } + goto loop; + + case dneg: { + double a = popDouble(t); + + pushDouble(t, -a); + } + goto loop; + + case vm::drem: { + double b = popDouble(t); + double a = popDouble(t); + + pushDouble(t, fmod(a, b)); + } + goto loop; + + case dsub: { + double b = popDouble(t); + double a = popDouble(t); + + pushDouble(t, a - b); + } + goto loop; + + case vm::dup: { + if (DebugStack) { + fprintf(stderr, "dup\n"); + } + + memcpy(stack + ((sp)*2), stack + ((sp - 1) * 2), BytesPerWord * 2); + ++sp; + } + goto loop; + + case dup_x1: { + if (DebugStack) { + fprintf(stderr, "dup_x1\n"); + } + + memcpy(stack + ((sp)*2), stack + ((sp - 1) * 2), BytesPerWord * 2); + memcpy(stack + ((sp - 1) * 2), stack + ((sp - 2) * 2), BytesPerWord * 2); + memcpy(stack + ((sp - 2) * 2), stack + ((sp)*2), BytesPerWord * 2); + ++sp; + } + goto loop; + + case dup_x2: { + if (DebugStack) { + fprintf(stderr, "dup_x2\n"); + } + + memcpy(stack + ((sp)*2), stack + ((sp - 1) * 2), BytesPerWord * 2); + memcpy(stack + ((sp - 1) * 2), stack + ((sp - 2) * 2), BytesPerWord * 2); + memcpy(stack + ((sp - 2) * 2), stack + ((sp - 3) * 2), BytesPerWord * 2); + memcpy(stack + ((sp - 3) * 2), stack + ((sp)*2), BytesPerWord * 2); + ++sp; + } + goto loop; + + case vm::dup2: { + if (DebugStack) { + fprintf(stderr, "dup2\n"); + } + + memcpy(stack + ((sp)*2), stack + ((sp - 2) * 2), BytesPerWord * 4); + sp += 2; + } + goto loop; + + case dup2_x1: { + if (DebugStack) { + fprintf(stderr, "dup2_x1\n"); + } + + memcpy(stack + ((sp + 1) * 2), stack + ((sp - 1) * 2), BytesPerWord * 2); + memcpy(stack + ((sp)*2), stack + ((sp - 2) * 2), BytesPerWord * 2); + memcpy(stack + ((sp - 1) * 2), stack + ((sp - 3) * 2), BytesPerWord * 2); + memcpy(stack + ((sp - 3) * 2), stack + ((sp)*2), BytesPerWord * 4); + sp += 2; + } + goto loop; + + case dup2_x2: { + if (DebugStack) { + fprintf(stderr, "dup2_x2\n"); + } + + memcpy(stack + ((sp + 1) * 2), stack + ((sp - 1) * 2), BytesPerWord * 2); + memcpy(stack + ((sp)*2), stack + ((sp - 2) * 2), BytesPerWord * 2); + memcpy(stack + ((sp - 1) * 2), stack + ((sp - 3) * 2), BytesPerWord * 2); + memcpy(stack + ((sp - 2) * 2), stack + ((sp - 4) * 2), BytesPerWord * 2); + memcpy(stack + ((sp - 4) * 2), stack + ((sp)*2), BytesPerWord * 4); + sp += 2; + } + goto loop; + + case f2d: { + pushDouble(t, popFloat(t)); + } + goto loop; + + case f2i: { + float f = popFloat(t); + switch (fpclassify(f)) { + case FP_NAN: + pushInt(t, 0); + break; + case FP_INFINITE: + pushInt(t, signbit(f) ? INT32_MIN : INT32_MAX); + break; + default: + pushInt(t, + f >= INT32_MAX + ? INT32_MAX + : (f <= INT32_MIN ? INT32_MIN : static_cast(f))); + break; + } + } + goto loop; + + case f2l: { + float f = popFloat(t); + switch (fpclassify(f)) { + case FP_NAN: + pushLong(t, 0); + break; + case FP_INFINITE: + pushLong(t, signbit(f) ? INT64_MIN : INT64_MAX); + break; + default: + pushLong(t, static_cast(f)); + break; + } + } + goto loop; + + case fadd: { + float b = popFloat(t); + float a = popFloat(t); + + pushFloat(t, a + b); + } + goto loop; + + case faload: { + int32_t index = popInt(t); + object array = popObject(t); + + if (LIKELY(array)) { + GcFloatArray* a = cast(t, array); + if (LIKELY(index >= 0 and static_cast(index) < a->length())) { + pushInt(t, a->body()[index]); + } else { + exception = makeThrowable(t, + GcArrayIndexOutOfBoundsException::Type, + "%d not in [0,%d)", + index, + a->length()); + goto throw_; + } + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + goto throw_; + } + } + goto loop; + + case fastore: { + float value = popFloat(t); + int32_t index = popInt(t); + object array = popObject(t); + + if (LIKELY(array)) { + GcFloatArray* a = cast(t, array); + if (LIKELY(index >= 0 and static_cast(index) < a->length())) { + memcpy(&a->body()[index], &value, sizeof(uint32_t)); + } else { + exception = makeThrowable(t, + GcArrayIndexOutOfBoundsException::Type, + "%d not in [0,%d)", + index, + a->length()); + goto throw_; + } + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + goto throw_; + } + } + goto loop; + + case fcmpg: { + float b = popFloat(t); + float a = popFloat(t); + + if (isNaN(a) or isNaN(b)) { + pushInt(t, 1); + } + if (a < b) { + pushInt(t, static_cast(-1)); + } else if (a > b) { + pushInt(t, 1); + } else if (a == b) { + pushInt(t, 0); + } else { + pushInt(t, 1); + } + } + goto loop; + + case fcmpl: { + float b = popFloat(t); + float a = popFloat(t); + + if (isNaN(a) or isNaN(b)) { + pushInt(t, static_cast(-1)); + } + if (a < b) { + pushInt(t, static_cast(-1)); + } else if (a > b) { + pushInt(t, 1); + } else if (a == b) { + pushInt(t, 0); + } else { + pushInt(t, static_cast(-1)); + } + } + goto loop; + + case fconst_0: { + pushFloat(t, 0); + } + goto loop; + + case fconst_1: { + pushFloat(t, 1); + } + goto loop; + + case fconst_2: { + pushFloat(t, 2); + } + goto loop; + + case fdiv: { + float b = popFloat(t); + float a = popFloat(t); + + pushFloat(t, a / b); + } + goto loop; + + case fmul: { + float b = popFloat(t); + float a = popFloat(t); + + pushFloat(t, a * b); + } + goto loop; + + case fneg: { + float a = popFloat(t); + + pushFloat(t, -a); + } + goto loop; + + case frem: { + float b = popFloat(t); + float a = popFloat(t); + + pushFloat(t, fmodf(a, b)); + } + goto loop; + + case fsub: { + float b = popFloat(t); + float a = popFloat(t); + + pushFloat(t, a - b); + } + goto loop; + + case getfield: { + if (LIKELY(peekObject(t, sp - 1))) { + uint16_t index = codeReadInt16(t, code, ip); + + GcField* field = resolveField(t, frameMethod(t, frame), index - 1); + + assertT(t, (field->flags() & ACC_STATIC) == 0); + + PROTECT(t, field); + + ACQUIRE_FIELD_FOR_READ(t, field); + + pushField(t, popObject(t), field); + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + goto throw_; + } + } + goto loop; + + case getstatic: { + uint16_t index = codeReadInt16(t, code, ip); + + GcField* field = resolveField(t, frameMethod(t, frame), index - 1); + + assertT(t, field->flags() & ACC_STATIC); + + PROTECT(t, field); + + initClass(t, field->class_()); + + ACQUIRE_FIELD_FOR_READ(t, field); + + pushField(t, field->class_()->staticTable(), field); + } + goto loop; + + case goto_: { + int16_t offset = codeReadInt16(t, code, ip); + ip = (ip - 3) + offset; + } + goto back_branch; + + case goto_w: { + int32_t offset = codeReadInt32(t, code, ip); + ip = (ip - 5) + offset; + } + goto back_branch; + + case i2b: { + pushInt(t, static_cast(popInt(t))); + } + goto loop; + + case i2c: { + pushInt(t, static_cast(popInt(t))); + } + goto loop; + + case i2d: { + pushDouble(t, static_cast(static_cast(popInt(t)))); + } + goto loop; + + case i2f: { + pushFloat(t, static_cast(static_cast(popInt(t)))); + } + goto loop; + + case i2l: { + pushLong(t, static_cast(popInt(t))); + } + goto loop; + + case i2s: { + pushInt(t, static_cast(popInt(t))); + } + goto loop; + + case iadd: { + int32_t b = popInt(t); + int32_t a = popInt(t); + + pushInt(t, a + b); + } + goto loop; + + case iaload: { + int32_t index = popInt(t); + object array = popObject(t); + + if (LIKELY(array)) { + GcIntArray* a = cast(t, array); + if (LIKELY(index >= 0 and static_cast(index) < a->length())) { + pushInt(t, a->body()[index]); + } else { + exception = makeThrowable(t, + GcArrayIndexOutOfBoundsException::Type, + "%d not in [0,%d)", + index, + a->length()); + goto throw_; + } + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + goto throw_; + } + } + goto loop; + + case iand: { + int32_t b = popInt(t); + int32_t a = popInt(t); + + pushInt(t, a & b); + } + goto loop; + + case iastore: { + int32_t value = popInt(t); + int32_t index = popInt(t); + object array = popObject(t); + + if (LIKELY(array)) { + GcIntArray* a = cast(t, array); + if (LIKELY(index >= 0 and static_cast(index) < a->length())) { + a->body()[index] = value; + } else { + exception = makeThrowable(t, + GcArrayIndexOutOfBoundsException::Type, + "%d not in [0,%d)", + index, + a->length()); + goto throw_; + } + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + goto throw_; + } + } + goto loop; + + case iconst_m1: { + pushInt(t, static_cast(-1)); + } + goto loop; + + case iconst_0: { + pushInt(t, 0); + } + goto loop; + + case iconst_1: { + pushInt(t, 1); + } + goto loop; + + case iconst_2: { + pushInt(t, 2); + } + goto loop; + + case iconst_3: { + pushInt(t, 3); + } + goto loop; + + case iconst_4: { + pushInt(t, 4); + } + goto loop; + + case iconst_5: { + pushInt(t, 5); + } + goto loop; + + case idiv: { + int32_t b = popInt(t); + int32_t a = popInt(t); + + if (UNLIKELY(b == 0)) { + exception = makeThrowable(t, GcArithmeticException::Type); + goto throw_; + } + + pushInt(t, a / b); + } + goto loop; + + case if_acmpeq: { + int16_t offset = codeReadInt16(t, code, ip); + + object b = popObject(t); + object a = popObject(t); + + if (a == b) { + ip = (ip - 3) + offset; + } + } + goto back_branch; + + case if_acmpne: { + int16_t offset = codeReadInt16(t, code, ip); + + object b = popObject(t); + object a = popObject(t); + + if (a != b) { + ip = (ip - 3) + offset; + } + } + goto back_branch; + + case if_icmpeq: { + int16_t offset = codeReadInt16(t, code, ip); + + int32_t b = popInt(t); + int32_t a = popInt(t); + + if (a == b) { + ip = (ip - 3) + offset; + } + } + goto back_branch; + + case if_icmpne: { + int16_t offset = codeReadInt16(t, code, ip); + + int32_t b = popInt(t); + int32_t a = popInt(t); + + if (a != b) { + ip = (ip - 3) + offset; + } + } + goto back_branch; + + case if_icmpgt: { + int16_t offset = codeReadInt16(t, code, ip); + + int32_t b = popInt(t); + int32_t a = popInt(t); + + if (a > b) { + ip = (ip - 3) + offset; + } + } + goto back_branch; + + case if_icmpge: { + int16_t offset = codeReadInt16(t, code, ip); + + int32_t b = popInt(t); + int32_t a = popInt(t); + + if (a >= b) { + ip = (ip - 3) + offset; + } + } + goto back_branch; + + case if_icmplt: { + int16_t offset = codeReadInt16(t, code, ip); + + int32_t b = popInt(t); + int32_t a = popInt(t); + + if (a < b) { + ip = (ip - 3) + offset; + } + } + goto back_branch; + + case if_icmple: { + int16_t offset = codeReadInt16(t, code, ip); + + int32_t b = popInt(t); + int32_t a = popInt(t); + + if (a <= b) { + ip = (ip - 3) + offset; + } + } + goto back_branch; + + case ifeq: { + int16_t offset = codeReadInt16(t, code, ip); + + if (popInt(t) == 0) { + ip = (ip - 3) + offset; + } + } + goto back_branch; + + case ifne: { + int16_t offset = codeReadInt16(t, code, ip); + + if (popInt(t)) { + ip = (ip - 3) + offset; + } + } + goto back_branch; + + case ifgt: { + int16_t offset = codeReadInt16(t, code, ip); + + if (static_cast(popInt(t)) > 0) { + ip = (ip - 3) + offset; + } + } + goto back_branch; + + case ifge: { + int16_t offset = codeReadInt16(t, code, ip); + + if (static_cast(popInt(t)) >= 0) { + ip = (ip - 3) + offset; + } + } + goto back_branch; + + case iflt: { + int16_t offset = codeReadInt16(t, code, ip); + + if (static_cast(popInt(t)) < 0) { + ip = (ip - 3) + offset; + } + } + goto back_branch; + + case ifle: { + int16_t offset = codeReadInt16(t, code, ip); + + if (static_cast(popInt(t)) <= 0) { + ip = (ip - 3) + offset; + } + } + goto back_branch; + + case ifnonnull: { + int16_t offset = codeReadInt16(t, code, ip); + + if (popObject(t)) { + ip = (ip - 3) + offset; + } + } + goto back_branch; + + case ifnull: { + int16_t offset = codeReadInt16(t, code, ip); + + if (popObject(t) == 0) { + ip = (ip - 3) + offset; + } + } + goto back_branch; + + case iinc: { + uint8_t index = code->body()[ip++]; + int8_t c = code->body()[ip++]; + + setLocalInt(t, index, localInt(t, index) + c); + } + goto loop; + + case iload: + case fload: { + pushInt(t, localInt(t, code->body()[ip++])); + } + goto loop; + + case iload_0: + case fload_0: { + pushInt(t, localInt(t, 0)); + } + goto loop; + + case iload_1: + case fload_1: { + pushInt(t, localInt(t, 1)); + } + goto loop; + + case iload_2: + case fload_2: { + pushInt(t, localInt(t, 2)); + } + goto loop; + + case iload_3: + case fload_3: { + pushInt(t, localInt(t, 3)); + } + goto loop; + + case imul: { + int32_t b = popInt(t); + int32_t a = popInt(t); + + pushInt(t, a * b); + } + goto loop; + + case ineg: { + pushInt(t, -popInt(t)); + } + goto loop; + + case instanceof: { + uint16_t index = codeReadInt16(t, code, ip); + + if (peekObject(t, sp - 1)) { + GcClass* class_ = resolveClassInPool(t, frameMethod(t, frame), index - 1); + + if (instanceOf(t, class_, popObject(t))) { + pushInt(t, 1); + } else { + pushInt(t, 0); + } + } else { + popObject(t); + pushInt(t, 0); + } + } + goto loop; + + case invokedynamic: { + uint16_t index = codeReadInt16(t, code, ip); + + ip += 2; + + GcInvocation* invocation = cast(t, singletonObject(t, code->pool(), index - 1)); + + GcCallSite* site = invocation->site(); + + loadMemoryBarrier(); + + if (site == 0) { + PROTECT(t, invocation); + + invocation->setClass(t, frameMethod(t, frame)->class_()); + + site = resolveDynamic(t, invocation); + PROTECT(t, site); + + storeStoreMemoryBarrier(); + + invocation->setSite(t, site); + site->setInvocation(t, invocation); + } + + method = site->target()->method(); + } goto invoke; + + case invokeinterface: { + uint16_t index = codeReadInt16(t, code, ip); + + ip += 2; + + GcMethod* m = resolveMethod(t, frameMethod(t, frame), index - 1); + + unsigned parameterFootprint = m->parameterFootprint(); + if (LIKELY(peekObject(t, sp - parameterFootprint))) { + method = findInterfaceMethod( + t, m, objectClass(t, peekObject(t, sp - parameterFootprint))); + goto invoke; + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + goto throw_; + } + } + goto loop; + + case invokespecial: { + uint16_t index = codeReadInt16(t, code, ip); + + GcMethod* m = resolveMethod(t, frameMethod(t, frame), index - 1); + + unsigned parameterFootprint = m->parameterFootprint(); + if (LIKELY(peekObject(t, sp - parameterFootprint))) { + GcClass* class_ = frameMethod(t, frame)->class_(); + if (isSpecialMethod(t, m, class_)) { + class_ = class_->super(); + PROTECT(t, m); + PROTECT(t, class_); + + initClass(t, class_); + + method = findVirtualMethod(t, m, class_); + } else { + method = m; + } + + goto invoke; + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + goto throw_; + } + } + goto loop; + + case invokestatic: { + uint16_t index = codeReadInt16(t, code, ip); + + GcMethod* m = resolveMethod(t, frameMethod(t, frame), index - 1); + PROTECT(t, m); + + initClass(t, m->class_()); + + method = m; + } + goto invoke; + + case invokevirtual: { + uint16_t index = codeReadInt16(t, code, ip); + + GcMethod* m = resolveMethod(t, frameMethod(t, frame), index - 1); + + unsigned parameterFootprint = m->parameterFootprint(); + if (LIKELY(peekObject(t, sp - parameterFootprint))) { + GcClass* class_ = objectClass(t, peekObject(t, sp - parameterFootprint)); + PROTECT(t, m); + PROTECT(t, class_); + + method = findVirtualMethod(t, m, class_); + goto invoke; + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + goto throw_; + } + } + goto loop; + + case ior: { + int32_t b = popInt(t); + int32_t a = popInt(t); + + pushInt(t, a | b); + } + goto loop; + + case irem: { + int32_t b = popInt(t); + int32_t a = popInt(t); + + if (UNLIKELY(b == 0)) { + exception = makeThrowable(t, GcArithmeticException::Type); + goto throw_; + } + + pushInt(t, a % b); + } + goto loop; + + case ireturn: + case freturn: { + int32_t result = popInt(t); + if (frame > base) { + popFrame(t); + pushInt(t, result); + goto loop; + } else { + return makeInt(t, result); + } + } + goto loop; + + case ishl: { + int32_t b = popInt(t); + int32_t a = popInt(t); + + pushInt(t, a << (b & 0x1F)); + } + goto loop; + + case ishr: { + int32_t b = popInt(t); + int32_t a = popInt(t); + + pushInt(t, a >> (b & 0x1F)); + } + goto loop; + + case istore: + case fstore: { + setLocalInt(t, code->body()[ip++], popInt(t)); + } + goto loop; + + case istore_0: + case fstore_0: { + setLocalInt(t, 0, popInt(t)); + } + goto loop; + + case istore_1: + case fstore_1: { + setLocalInt(t, 1, popInt(t)); + } + goto loop; + + case istore_2: + case fstore_2: { + setLocalInt(t, 2, popInt(t)); + } + goto loop; + + case istore_3: + case fstore_3: { + setLocalInt(t, 3, popInt(t)); + } + goto loop; + + case isub: { + int32_t b = popInt(t); + int32_t a = popInt(t); + + pushInt(t, a - b); + } + goto loop; + + case iushr: { + int32_t b = popInt(t); + uint32_t a = popInt(t); + + pushInt(t, a >> (b & 0x1F)); + } + goto loop; + + case ixor: { + int32_t b = popInt(t); + int32_t a = popInt(t); + + pushInt(t, a ^ b); + } + goto loop; + + case jsr: { + uint16_t offset = codeReadInt16(t, code, ip); + + pushInt(t, ip); + ip = (ip - 3) + static_cast(offset); + } + goto loop; + + case jsr_w: { + uint32_t offset = codeReadInt32(t, code, ip); + + pushInt(t, ip); + ip = (ip - 5) + static_cast(offset); + } + goto loop; + + case l2d: { + pushDouble(t, static_cast(static_cast(popLong(t)))); + } + goto loop; + + case l2f: { + pushFloat(t, static_cast(static_cast(popLong(t)))); + } + goto loop; + + case l2i: { + pushInt(t, static_cast(popLong(t))); + } + goto loop; + + case ladd: { + int64_t b = popLong(t); + int64_t a = popLong(t); + + pushLong(t, a + b); + } + goto loop; + + case laload: { + int32_t index = popInt(t); + object array = popObject(t); + + if (LIKELY(array)) { + GcLongArray* a = cast(t, array); + if (LIKELY(index >= 0 and static_cast(index) < a->length())) { + pushLong(t, a->body()[index]); + } else { + exception = makeThrowable(t, + GcArrayIndexOutOfBoundsException::Type, + "%d not in [0,%d)", + index, + a->length()); + goto throw_; + } + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + goto throw_; + } + } + goto loop; + + case land: { + int64_t b = popLong(t); + int64_t a = popLong(t); + + pushLong(t, a & b); + } + goto loop; + + case lastore: { + int64_t value = popLong(t); + int32_t index = popInt(t); + object array = popObject(t); + + if (LIKELY(array)) { + GcLongArray* a = cast(t, array); + if (LIKELY(index >= 0 and static_cast(index) < a->length())) { + a->body()[index] = value; + } else { + exception = makeThrowable(t, + GcArrayIndexOutOfBoundsException::Type, + "%d not in [0,%d)", + index, + a->length()); + goto throw_; + } + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + goto throw_; + } + } + goto loop; + + case lcmp: { + int64_t b = popLong(t); + int64_t a = popLong(t); + + pushInt(t, a > b ? 1 : a == b ? 0 : -1); + } + goto loop; + + case lconst_0: { + pushLong(t, 0); + } + goto loop; + + case lconst_1: { + pushLong(t, 1); + } + goto loop; + + case ldc: + case ldc_w: { + uint16_t index; + + if (instruction == ldc) { + index = code->body()[ip++]; + } else { + index = codeReadInt16(t, code, ip); + } + + GcSingleton* pool = code->pool(); + + if (singletonIsObject(t, pool, index - 1)) { + object v = singletonObject(t, pool, index - 1); + if (objectClass(t, v) == type(t, GcReference::Type)) { + GcClass* class_ + = resolveClassInPool(t, frameMethod(t, frame), index - 1); + + pushObject(t, reinterpret_cast(getJClass(t, class_))); + } else if (objectClass(t, v) == type(t, GcClass::Type)) { + pushObject(t, + reinterpret_cast(getJClass(t, cast(t, v)))); + } else { + pushObject(t, v); + } + } else { + pushInt(t, singletonValue(t, pool, index - 1)); + } + } + goto loop; + + case ldc2_w: { + uint16_t index = codeReadInt16(t, code, ip); + + GcSingleton* pool = code->pool(); + + uint64_t v; + memcpy(&v, &singletonValue(t, pool, index - 1), 8); + pushLong(t, v); + } + goto loop; + + case ldiv_: { + int64_t b = popLong(t); + int64_t a = popLong(t); + + if (UNLIKELY(b == 0)) { + exception = makeThrowable(t, GcArithmeticException::Type); + goto throw_; + } + + pushLong(t, a / b); + } + goto loop; + + case lload: + case dload: { + pushLong(t, localLong(t, code->body()[ip++])); + } + goto loop; + + case lload_0: + case dload_0: { + pushLong(t, localLong(t, 0)); + } + goto loop; + + case lload_1: + case dload_1: { + pushLong(t, localLong(t, 1)); + } + goto loop; + + case lload_2: + case dload_2: { + pushLong(t, localLong(t, 2)); + } + goto loop; + + case lload_3: + case dload_3: { + pushLong(t, localLong(t, 3)); + } + goto loop; + + case lmul: { + int64_t b = popLong(t); + int64_t a = popLong(t); + + pushLong(t, a * b); + } + goto loop; + + case lneg: { + pushLong(t, -popLong(t)); + } + goto loop; + + case lookupswitch: { + int32_t base = ip - 1; + + ip += 3; + ip -= (ip % 4); + + int32_t default_ = codeReadInt32(t, code, ip); + int32_t pairCount = codeReadInt32(t, code, ip); + + int32_t key = popInt(t); + + int32_t bottom = 0; + int32_t top = pairCount; + for (int32_t span = top - bottom; span; span = top - bottom) { + int32_t middle = bottom + (span / 2); + unsigned index = ip + (middle * 8); + + int32_t k = codeReadInt32(t, code, index); + + if (key < k) { + top = middle; + } else if (key > k) { + bottom = middle + 1; + } else { + ip = base + codeReadInt32(t, code, index); + goto loop; + } + } + + ip = base + default_; + } + goto loop; + + case lor: { + int64_t b = popLong(t); + int64_t a = popLong(t); + + pushLong(t, a | b); + } + goto loop; + + case lrem: { + int64_t b = popLong(t); + int64_t a = popLong(t); + + if (UNLIKELY(b == 0)) { + exception = makeThrowable(t, GcArithmeticException::Type); + goto throw_; + } + + pushLong(t, a % b); + } + goto loop; + + case lreturn: + case dreturn: { + int64_t result = popLong(t); + if (frame > base) { + popFrame(t); + pushLong(t, result); + goto loop; + } else { + return makeLong(t, result); + } + } + goto loop; + + case lshl: { + int32_t b = popInt(t); + int64_t a = popLong(t); + + pushLong(t, a << (b & 0x3F)); + } + goto loop; + + case lshr: { + int32_t b = popInt(t); + int64_t a = popLong(t); + + pushLong(t, a >> (b & 0x3F)); + } + goto loop; + + case lstore: + case dstore: { + setLocalLong(t, code->body()[ip++], popLong(t)); + } + goto loop; + + case lstore_0: + case dstore_0: { + setLocalLong(t, 0, popLong(t)); + } + goto loop; + + case lstore_1: + case dstore_1: { + setLocalLong(t, 1, popLong(t)); + } + goto loop; + + case lstore_2: + case dstore_2: { + setLocalLong(t, 2, popLong(t)); + } + goto loop; + + case lstore_3: + case dstore_3: { + setLocalLong(t, 3, popLong(t)); + } + goto loop; + + case lsub: { + int64_t b = popLong(t); + int64_t a = popLong(t); + + pushLong(t, a - b); + } + goto loop; + + case lushr: { + int64_t b = popInt(t); + uint64_t a = popLong(t); + + pushLong(t, a >> (b & 0x3F)); + } + goto loop; + + case lxor: { + int64_t b = popLong(t); + int64_t a = popLong(t); + + pushLong(t, a ^ b); + } + goto loop; + + case monitorenter: { + object o = popObject(t); + if (LIKELY(o)) { + acquire(t, o); + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + goto throw_; + } + } + goto loop; + + case monitorexit: { + object o = popObject(t); + if (LIKELY(o)) { + release(t, o); + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + goto throw_; + } + } + goto loop; + + case multianewarray: { + uint16_t index = codeReadInt16(t, code, ip); + uint8_t dimensions = code->body()[ip++]; + + GcClass* class_ = resolveClassInPool(t, frameMethod(t, frame), index - 1); + PROTECT(t, class_); + + THREAD_RUNTIME_ARRAY(t, int32_t, counts, dimensions); + for (int i = dimensions - 1; i >= 0; --i) { + RUNTIME_ARRAY_BODY(counts)[i] = popInt(t); + if (UNLIKELY(RUNTIME_ARRAY_BODY(counts)[i] < 0)) { + exception = makeThrowable(t, + GcNegativeArraySizeException::Type, + "%d", + RUNTIME_ARRAY_BODY(counts)[i]); + goto throw_; + } + } + + object array = makeArray(t, RUNTIME_ARRAY_BODY(counts)[0]); + setObjectClass(t, array, class_); + PROTECT(t, array); + + populateMultiArray(t, array, RUNTIME_ARRAY_BODY(counts), 0, dimensions); + + pushObject(t, array); + } + goto loop; + + case new_: { + uint16_t index = codeReadInt16(t, code, ip); + + GcClass* class_ = resolveClassInPool(t, frameMethod(t, frame), index - 1); + PROTECT(t, class_); + + initClass(t, class_); + + pushObject(t, make(t, class_)); + } + goto loop; + + case newarray: { + int32_t count = popInt(t); + + if (LIKELY(count >= 0)) { + uint8_t type = code->body()[ip++]; + + object array; + + switch (type) { + case T_BOOLEAN: + array = makeBooleanArray(t, count); + break; + + case T_CHAR: + array = makeCharArray(t, count); + break; + + case T_FLOAT: + array = makeFloatArray(t, count); + break; + + case T_DOUBLE: + array = makeDoubleArray(t, count); + break; + + case T_BYTE: + array = makeByteArray(t, count); + break; + + case T_SHORT: + array = makeShortArray(t, count); + break; + + case T_INT: + array = makeIntArray(t, count); + break; + + case T_LONG: + array = makeLongArray(t, count); + break; + + default: + abort(t); + } + + pushObject(t, array); + } else { + exception + = makeThrowable(t, GcNegativeArraySizeException::Type, "%d", count); + goto throw_; + } + } + goto loop; + + case nop: + goto loop; + + case pop_: { + --sp; + } + goto loop; + + case pop2: { + sp -= 2; + } + goto loop; + + case putfield: { + uint16_t index = codeReadInt16(t, code, ip); + + GcField* field = resolveField(t, frameMethod(t, frame), index - 1); + + assertT(t, (field->flags() & ACC_STATIC) == 0); + PROTECT(t, field); + + { + ACQUIRE_FIELD_FOR_WRITE(t, field); + + switch (field->code()) { + case ByteField: + case BooleanField: + case CharField: + case ShortField: + case FloatField: + case IntField: { + int32_t value = popInt(t); + object o = popObject(t); + if (LIKELY(o)) { + switch (field->code()) { + case ByteField: + case BooleanField: + fieldAtOffset(o, field->offset()) = value; + break; + + case CharField: + case ShortField: + fieldAtOffset(o, field->offset()) = value; + break; + + case FloatField: + case IntField: + fieldAtOffset(o, field->offset()) = value; + break; + } + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + } + } break; + + case DoubleField: + case LongField: { + int64_t value = popLong(t); + object o = popObject(t); + if (LIKELY(o)) { + fieldAtOffset(o, field->offset()) = value; + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + } + } break; + + case ObjectField: { + object value = popObject(t); + object o = popObject(t); + if (LIKELY(o)) { + setField(t, o, field->offset(), value); + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + } + } break; + + default: + abort(t); + } + } + + if (UNLIKELY(exception)) { + goto throw_; + } + } + goto loop; + + case putstatic: { + uint16_t index = codeReadInt16(t, code, ip); + + GcField* field = resolveField(t, frameMethod(t, frame), index - 1); + + assertT(t, field->flags() & ACC_STATIC); + + PROTECT(t, field); + + ACQUIRE_FIELD_FOR_WRITE(t, field); + + initClass(t, field->class_()); + + GcSingleton* table = field->class_()->staticTable(); + + switch (field->code()) { + case ByteField: + case BooleanField: + case CharField: + case ShortField: + case FloatField: + case IntField: { + int32_t value = popInt(t); + switch (field->code()) { + case ByteField: + case BooleanField: + fieldAtOffset(table, field->offset()) = value; + break; + + case CharField: + case ShortField: + fieldAtOffset(table, field->offset()) = value; + break; + + case FloatField: + case IntField: + fieldAtOffset(table, field->offset()) = value; + break; + } + } break; + + case DoubleField: + case LongField: { + fieldAtOffset(table, field->offset()) = popLong(t); + } break; + + case ObjectField: { + setField(t, table, field->offset(), popObject(t)); + } break; + + default: + abort(t); + } + } + goto loop; + + case ret: { + ip = localInt(t, code->body()[ip]); + } + goto loop; + + case return_: { + GcMethod* method = frameMethod(t, frame); + if ((method->flags() & ConstructorFlag) + and (method->class_()->vmFlags() & HasFinalMemberFlag)) { + storeStoreMemoryBarrier(); + } + + if (frame > base) { + popFrame(t); + goto loop; + } else { + return 0; + } + } + goto loop; + + case saload: { + int32_t index = popInt(t); + object array = popObject(t); + + if (LIKELY(array)) { + GcShortArray* a = cast(t, array); + if (LIKELY(index >= 0 and static_cast(index) < a->length())) { + pushInt(t, a->body()[index]); + } else { + exception = makeThrowable(t, + GcArrayIndexOutOfBoundsException::Type, + "%d not in [0,%d)", + index, + a->length()); + goto throw_; + } + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + goto throw_; + } + } + goto loop; + + case sastore: { + int16_t value = popInt(t); + int32_t index = popInt(t); + object array = popObject(t); + + if (LIKELY(array)) { + GcShortArray* a = cast(t, array); + if (LIKELY(index >= 0 and static_cast(index) < a->length())) { + a->body()[index] = value; + } else { + exception = makeThrowable(t, + GcArrayIndexOutOfBoundsException::Type, + "%d not in [0,%d)", + index, + a->length()); + goto throw_; + } + } else { + exception = makeThrowable(t, GcNullPointerException::Type); + goto throw_; + } + } + goto loop; + + case sipush: { + pushInt(t, static_cast(codeReadInt16(t, code, ip))); + } + goto loop; + + case swap: { + uintptr_t tmp[2]; + memcpy(tmp, stack + ((sp - 1) * 2), BytesPerWord * 2); + memcpy(stack + ((sp - 1) * 2), stack + ((sp - 2) * 2), BytesPerWord * 2); + memcpy(stack + ((sp - 2) * 2), tmp, BytesPerWord * 2); + } + goto loop; + + case tableswitch: { + int32_t base = ip - 1; + + ip += 3; + ip -= (ip % 4); + + int32_t default_ = codeReadInt32(t, code, ip); + int32_t bottom = codeReadInt32(t, code, ip); + int32_t top = codeReadInt32(t, code, ip); + + int32_t key = popInt(t); + + if (key >= bottom and key <= top) { + unsigned index = ip + ((key - bottom) * 4); + ip = base + codeReadInt32(t, code, index); + } else { + ip = base + default_; + } + } + goto loop; + + case wide: + goto wide; + + case impdep1: { + // this means we're invoking a virtual method on an instance of a + // bootstrap class, so we need to load the real class to get the + // real method and call it. + + assertT(t, frameNext(t, frame) >= base); + popFrame(t); + + assertT(t, code->body()[ip - 3] == invokevirtual); + ip -= 2; + + uint16_t index = codeReadInt16(t, code, ip); + GcMethod* method = resolveMethod(t, frameMethod(t, frame), index - 1); + + unsigned parameterFootprint = method->parameterFootprint(); + GcClass* class_ = objectClass(t, peekObject(t, sp - parameterFootprint)); + assertT(t, class_->vmFlags() & BootstrapFlag); + + resolveClass(t, frameMethod(t, frame)->class_()->loader(), class_->name()); + + ip -= 3; + } + goto loop; + + default: + abort(t); + } + +wide: + switch (code->body()[ip++]) { + case aload: { + pushObject(t, localObject(t, codeReadInt16(t, code, ip))); + } + goto loop; + + case astore: { + setLocalObject(t, codeReadInt16(t, code, ip), popObject(t)); + } + goto loop; + + case iinc: { + uint16_t index = codeReadInt16(t, code, ip); + int16_t count = codeReadInt16(t, code, ip); + + setLocalInt(t, index, localInt(t, index) + count); + } + goto loop; + + case iload: { + pushInt(t, localInt(t, codeReadInt16(t, code, ip))); + } + goto loop; + + case istore: { + setLocalInt(t, codeReadInt16(t, code, ip), popInt(t)); + } + goto loop; + + case lload: { + pushLong(t, localLong(t, codeReadInt16(t, code, ip))); + } + goto loop; + + case lstore: { + setLocalLong(t, codeReadInt16(t, code, ip), popLong(t)); + } + goto loop; + + case ret: { + ip = localInt(t, codeReadInt16(t, code, ip)); + } + goto loop; + + default: + abort(t); + } + +back_branch: + safePoint(t); + goto loop; + +invoke : { + if (method->flags() & ACC_NATIVE) { + invokeNative(t, method); + } else { + if (DebugCalls && method) { + printf("invoke %s.%s\n", + method->class_() && method->class_()->name() ? (const char *)method->class_()->name()->body().begin() : "", + method->name() ? (const char *)method->name()->body().begin() : "" + ); + } + checkStack(t, method); + pushFrame(t, method); + } +} + goto loop; + +throw_: + if (DebugRun || DebugCalls) { + fprintf(stderr, "throw @ %s\n", frameMethod(t, frame)->name()->body().begin()); + } + + pokeInt(t, t->frame + FrameIpOffset, t->ip); + for (; frame >= base; popFrame(t)) { + uint64_t eh = findExceptionHandler(t, frame); + if (eh) { + sp = frame + FrameFootprint; + ip = exceptionHandlerIp(eh); + pushObject(t, exception); + exception = 0; + goto loop; + } + } + + return 0; +} + +uint64_t interpret2(vm::Thread* t, uintptr_t* arguments) +{ + int base = arguments[0]; + bool* success = reinterpret_cast(arguments[1]); + + object r = interpret3(static_cast(t), base); + *success = true; + return reinterpret_cast(r); +} + +object interpret(Thread* t) +{ + const int base = t->frame; + + while (true) { + bool success = false; + uintptr_t arguments[] + = {static_cast(base), reinterpret_cast(&success)}; + + uint64_t r = run(t, interpret2, arguments); + if (success) { + if (t->exception) { + GcThrowable* exception = t->exception; + t->exception = 0; + throw_(t, exception); + } else { + return reinterpret_cast(r); + } + } + } +} + +void pushArguments(Thread* t, + object this_, + const char* spec, + bool indirectObjects, + va_list a) +{ + if (this_) { + pushObject(t, this_); + } + + for (MethodSpecIterator it(t, spec); it.hasNext();) { + switch (*it.next()) { + case 'L': + case '[': + if (indirectObjects) { + object* v = va_arg(a, object*); + pushObject(t, v ? *v : 0); + } else { + pushObject(t, va_arg(a, object)); + } + break; + + case 'J': + case 'D': + pushLong(t, va_arg(a, uint64_t)); + break; + + case 'F': { + pushFloat(t, va_arg(a, double)); + } break; + + default: + pushInt(t, va_arg(a, uint32_t)); + break; + } + } +} + +void pushArguments(Thread* t, + object this_, + const char* spec, + const jvalue* arguments) +{ + if (this_) { + pushObject(t, this_); + } + + unsigned index = 0; + for (MethodSpecIterator it(t, spec); it.hasNext();) { + switch (*it.next()) { + case 'L': + case '[': { + jobject v = arguments[index++].l; + pushObject(t, v ? *v : 0); + } break; + + case 'J': + case 'D': + pushLong(t, arguments[index++].j); + break; + + case 'F': { + pushFloat(t, arguments[index++].f); + } break; + + default: + pushInt(t, arguments[index++].i); + break; + } + } +} + +void pushArguments(Thread* t, object this_, const char* spec, object a) +{ + if (this_) { + pushObject(t, this_); + } + + unsigned index = 0; + for (MethodSpecIterator it(t, spec); it.hasNext();) { + switch (*it.next()) { + case 'L': + case '[': + pushObject(t, objectArrayBody(t, a, index++)); + break; + + case 'J': + case 'D': + pushLong(t, fieldAtOffset(objectArrayBody(t, a, index++), 8)); + break; + + default: + pushInt( + t, + fieldAtOffset(objectArrayBody(t, a, index++), BytesPerWord)); + break; + } + } +} + +object invoke(Thread* t, GcMethod* method) +{ + PROTECT(t, method); + + GcClass* class_; + PROTECT(t, class_); + + if (methodVirtual(t, method)) { + unsigned parameterFootprint = method->parameterFootprint(); + class_ = objectClass(t, peekObject(t, t->sp - parameterFootprint)); + + if (class_->vmFlags() & BootstrapFlag) { + resolveClass(t, roots(t)->bootLoader(), class_->name()); + } + + if (method->class_()->flags() & ACC_INTERFACE) { + method = findInterfaceMethod(t, method, class_); + } else { + method = findVirtualMethod(t, method, class_); + } + } else { + class_ = method->class_(); + } + + if (method->flags() & ACC_STATIC) { + initClass(t, class_); + } + + object result = 0; + + if (method->flags() & ACC_NATIVE) { + unsigned returnCode = invokeNative(t, method); + + switch (returnCode) { + case ByteField: + case BooleanField: + case CharField: + case ShortField: + case FloatField: + case IntField: + result = makeInt(t, popInt(t)); + break; + + case LongField: + case DoubleField: + result = makeLong(t, popLong(t)); + break; + + case ObjectField: + result = popObject(t); + break; + + case VoidField: + result = 0; + break; + + default: + abort(t); + }; + } else { + checkStack(t, method); + pushFrame(t, method); + + result = interpret(t); + + if (LIKELY(t->exception == 0)) { + popFrame(t); + } else { + GcThrowable* exception = t->exception; + t->exception = 0; + throw_(t, exception); + } + } + + return result; +} + +class MyProcessor : public Processor { + public: + MyProcessor(System* s, Allocator* allocator, const char* crashDumpDirectory) + : s(s), allocator(allocator) + { + signals.setCrashDumpDirectory(crashDumpDirectory); + } + + virtual vm::Thread* makeThread(Machine* m, + GcThread* javaThread, + vm::Thread* parent) + { + Thread* t = new (m->heap->allocate(sizeof(Thread) + m->stackSizeInBytes)) + Thread(m, javaThread, parent); + t->init(); + return t; + } + + virtual GcMethod* makeMethod(vm::Thread* t, + uint8_t vmFlags, + uint8_t returnCode, + uint8_t parameterCount, + uint8_t parameterFootprint, + uint16_t flags, + uint16_t offset, + GcByteArray* name, + GcByteArray* spec, + GcMethodAddendum* addendum, + GcClass* class_, + GcCode* code) + { + return vm::makeMethod(t, + vmFlags, + returnCode, + parameterCount, + parameterFootprint, + flags, + offset, + 0, + 0, + name, + spec, + addendum, + class_, + code); + } + + virtual GcClass* makeClass(vm::Thread* t, + uint16_t flags, + uint16_t vmFlags, + uint16_t fixedSize, + uint8_t arrayElementSize, + uint8_t arrayDimensions, + GcClass* arrayElementClass, + GcIntArray* objectMask, + GcByteArray* name, + GcByteArray* sourceFile, + GcClass* super, + object interfaceTable, + object virtualTable, + object fieldTable, + object methodTable, + GcClassAddendum* addendum, + GcSingleton* staticTable, + GcClassLoader* loader, + unsigned vtableLength UNUSED) + { + return vm::makeClass(t, + flags, + vmFlags, + fixedSize, + arrayElementSize, + arrayDimensions, + arrayElementClass, + 0, + objectMask, + name, + sourceFile, + super, + interfaceTable, + virtualTable, + fieldTable, + methodTable, + addendum, + staticTable, + loader, + 0, + 0); + } + + virtual void initVtable(vm::Thread*, GcClass*) + { + // ignore + } + + virtual void visitObjects(vm::Thread* vmt, Heap::Visitor* v) + { + Thread* t = static_cast(vmt); + + v->visit(&(t->code)); + + for (unsigned i = 0; i < t->sp; ++i) { + if (t->stack[i * 2] == ObjectTag) { + v->visit(reinterpret_cast(t->stack + (i * 2) + 1)); + } + } + } + + virtual void walkStack(vm::Thread* vmt, StackVisitor* v) + { + Thread* t = static_cast(vmt); + + if (t->frame >= 0) { + pokeInt(t, t->frame + FrameIpOffset, t->ip); + } + + MyStackWalker walker(t, t->frame); + walker.walk(v); + } + + virtual int lineNumber(vm::Thread* t, GcMethod* method, int ip) + { + return findLineNumber(static_cast(t), method, ip); + } + + virtual object* makeLocalReference(vm::Thread* vmt, object o) + { + Thread* t = static_cast(vmt); + + return pushReference(t, o); + } + + virtual void disposeLocalReference(vm::Thread*, object* r) + { + if (r) { + *r = 0; + } + } + + virtual bool pushLocalFrame(vm::Thread* vmt, unsigned capacity) + { + Thread* t = static_cast(vmt); + + if (t->sp + capacity < stackSizeInWords(t) / 2) { + t->stackPointers = new (t->m->heap) + List(t->sp, t->stackPointers); + + return true; + } else { + return false; + } + } + + virtual void popLocalFrame(vm::Thread* vmt) + { + Thread* t = static_cast(vmt); + + List* f = t->stackPointers; + t->stackPointers = f->next; + t->sp = f->item; + + t->m->heap->free(f, sizeof(List)); + } + + virtual object invokeArray(vm::Thread* vmt, + GcMethod* method, + object this_, + object arguments) + { + Thread* t = static_cast(vmt); + + assertT( + t, + t->state == Thread::ActiveState or t->state == Thread::ExclusiveState); + + assertT(t, ((method->flags() & ACC_STATIC) == 0) xor (this_ == 0)); + + if (UNLIKELY(t->sp + method->parameterFootprint() + 1 > stackSizeInWords(t) + / 2)) { + throwNew(t, GcStackOverflowError::Type); + } + + const char* spec = reinterpret_cast(method->spec()->body().begin()); + pushArguments(t, this_, spec, arguments); + + return local::invoke(t, method); + } + + virtual object invokeArray(vm::Thread* vmt, + GcMethod* method, + object this_, + const jvalue* arguments) + { + Thread* t = static_cast(vmt); + + assertT( + t, + t->state == Thread::ActiveState or t->state == Thread::ExclusiveState); + + assertT(t, ((method->flags() & ACC_STATIC) == 0) xor (this_ == 0)); + + if (UNLIKELY(t->sp + method->parameterFootprint() + 1 > stackSizeInWords(t) + / 2)) { + throwNew(t, GcStackOverflowError::Type); + } + + const char* spec = reinterpret_cast(method->spec()->body().begin()); + pushArguments(t, this_, spec, arguments); + + return local::invoke(t, method); + } + + virtual object invokeList(vm::Thread* vmt, + GcMethod* method, + object this_, + bool indirectObjects, + va_list arguments) + { + Thread* t = static_cast(vmt); + + assertT( + t, + t->state == Thread::ActiveState or t->state == Thread::ExclusiveState); + + assertT(t, ((method->flags() & ACC_STATIC) == 0) xor (this_ == 0)); + + if (UNLIKELY(t->sp + method->parameterFootprint() + 1 > stackSizeInWords(t) + / 2)) { + throwNew(t, GcStackOverflowError::Type); + } + + const char* spec = reinterpret_cast(method->spec()->body().begin()); + pushArguments(t, this_, spec, indirectObjects, arguments); + + return local::invoke(t, method); + } + + virtual object invokeList(vm::Thread* vmt, + GcClassLoader* loader, + const char* className, + const char* methodName, + const char* methodSpec, + object this_, + va_list arguments) + { + Thread* t = static_cast(vmt); + + assertT( + t, + t->state == Thread::ActiveState or t->state == Thread::ExclusiveState); + + if (UNLIKELY(t->sp + parameterFootprint(vmt, methodSpec, false) + > stackSizeInWords(t) / 2)) { + throwNew(t, GcStackOverflowError::Type); + } + + pushArguments(t, this_, methodSpec, false, arguments); + + GcMethod* method + = resolveMethod(t, loader, className, methodName, methodSpec); + + assertT(t, ((method->flags() & ACC_STATIC) == 0) xor (this_ == 0)); + + return local::invoke(t, method); + } + + virtual object getStackTrace(vm::Thread* t, vm::Thread*) + { + // not implemented + return makeObjectArray(t, 0); + } + + virtual void initialize(BootImage*, avian::util::Slice) + { + abort(s); + } + + virtual void addCompilationHandler(CompilationHandler*) + { + abort(s); + } + + virtual void compileMethod(vm::Thread*, + Zone*, + GcTriple**, + GcTriple**, + avian::codegen::DelayedPromise**, + GcMethod*, + OffsetResolver*, + JavaVM*) + { + abort(s); + } + + virtual void visitRoots(vm::Thread*, HeapWalker*) + { + abort(s); + } + + virtual void normalizeVirtualThunks(vm::Thread*) + { + abort(s); + } + + virtual unsigned* makeCallTable(vm::Thread*, HeapWalker*) + { + abort(s); + } + + virtual void boot(vm::Thread*, BootImage* image, uint8_t* code) + { + expect(s, image == 0 and code == 0); + } + + virtual void callWithCurrentContinuation(vm::Thread*, object) + { + abort(s); + } + + virtual void dynamicWind(vm::Thread*, object, object, object) + { + abort(s); + } + + virtual void feedResultToContinuation(vm::Thread*, GcContinuation*, object) + { + abort(s); + } + + virtual void feedExceptionToContinuation(vm::Thread*, + GcContinuation*, + GcThrowable*) + { + abort(s); + } + + virtual void walkContinuationBody(vm::Thread*, + Heap::Walker*, + object, + unsigned) + { + abort(s); + } + + virtual void dispose(vm::Thread* t) + { + t->m->heap->free(t, sizeof(Thread) + t->m->stackSizeInBytes); + } + + virtual void dispose() + { + signals.setCrashDumpDirectory(0); + this->~MyProcessor(); + allocator->free(this, sizeof(*this)); + } + + System* s; + Allocator* allocator; + SignalRegistrar signals; +}; + +} // namespace + +namespace vm { + +Processor* makeProcessor(System* system, + Allocator* allocator, + const char* crashDumpDirectory, + bool) +{ + return new (allocator->allocate(sizeof(local::MyProcessor))) + local::MyProcessor(system, allocator, crashDumpDirectory); +} + +} // namespace vm diff --git a/sgx-jvm/avian/src/jnienv.cpp b/sgx-jvm/avian/src/jnienv.cpp new file mode 100644 index 0000000000..57613ab2d2 --- /dev/null +++ b/sgx-jvm/avian/src/jnienv.cpp @@ -0,0 +1,3811 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/jnienv.h" +#include "avian/machine.h" +#include "avian/util.h" +#include "avian/processor.h" +#include "avian/constants.h" + +#include + +using namespace vm; + +namespace { + +namespace local { + +jint JNICALL AttachCurrentThread(Machine* m, Thread** t, void*) +{ + *t = static_cast(m->localThread->get()); + if (*t == 0) { + *t = attachThread(m, false); + } + return 0; +} + +jint JNICALL AttachCurrentThreadAsDaemon(Machine* m, Thread** t, void*) +{ + *t = static_cast(m->localThread->get()); + if (*t == 0) { + *t = attachThread(m, true); + } + return 0; +} + +jint JNICALL DetachCurrentThread(Machine* m) +{ + Thread* t = static_cast(m->localThread->get()); + if (t) { + // todo: detaching the root thread seems to cause stability + // problems which I haven't yet had a chance to investigate + // thoroughly. Meanwhile, we just ignore requests to detach it, + // which leaks a bit of memory but should be harmless otherwise. + if (m->rootThread != t) { + m->localThread->set(0); + + ACQUIRE_RAW(t, t->m->stateLock); + + enter(t, Thread::ActiveState); + + t->javaThread->peer() = 0; + + enter(t, Thread::ZombieState); + + t->state = Thread::JoinedState; + } + + return 0; + } else { + return -1; + } +} + +uint64_t destroyJavaVM(Thread* t, uintptr_t*) +{ +#ifndef SGX + // wait for other non-daemon threads to exit + { + ACQUIRE(t, t->m->stateLock); + while (t->m->liveCount - t->m->daemonCount > 1) { + t->m->stateLock->wait(t->systemThread, 0); + } + } +#endif + { + ENTER(t, Thread::ActiveState); + + t->m->classpath->shutDown(t); + } +#ifndef SGX + // wait again in case the Classpath::shutDown process started new + // threads: + { + ACQUIRE(t, t->m->stateLock); + while (t->m->liveCount - t->m->daemonCount > 1) { + t->m->stateLock->wait(t->systemThread, 0); + } + + enter(t, Thread::ExclusiveState); + } +#endif + shutDown(t); + + return 1; +} + +jint JNICALL DestroyJavaVM(Machine* m) +{ + Thread* t; + AttachCurrentThread(m, &t, 0); + + if (runRaw(t, destroyJavaVM, 0)) { + t->exit(); + return 0; + } else { + return -1; + } +} + +jint JNICALL GetEnv(Machine* m, Thread** t, jint version) +{ + *t = static_cast(m->localThread->get()); + if (*t) { + if (version <= JNI_VERSION_1_6) { + return AVIAN_JNI_OK; + } else { + return AVIAN_JNI_EVERSION; + } + } else { + return AVIAN_JNI_EDETACHED; + } +} + +jint JNICALL GetVersion(Thread* t) +{ + ENTER(t, Thread::ActiveState); + + return JNI_VERSION_1_6; +} + +jsize JNICALL GetStringLength(Thread* t, jstring s) +{ + ENTER(t, Thread::ActiveState); + + return (*s)->length(t); +} + +const jchar* JNICALL GetStringChars(Thread* t, jstring s, jboolean* isCopy) +{ + ENTER(t, Thread::ActiveState); + + jchar* chars = static_cast( + t->m->heap->allocate(((*s)->length(t) + 1) * sizeof(jchar))); + stringChars(t, *s, chars); + + if (isCopy) + *isCopy = true; + return chars; +} + +void JNICALL ReleaseStringChars(Thread* t, jstring s, const jchar* chars) +{ + ENTER(t, Thread::ActiveState); + + t->m->heap->free(chars, ((*s)->length(t) + 1) * sizeof(jchar)); +} + +void JNICALL + GetStringRegion(Thread* t, jstring s, jsize start, jsize length, jchar* dst) +{ + ENTER(t, Thread::ActiveState); + + stringChars(t, *s, start, length, dst); +} + +const jchar* JNICALL GetStringCritical(Thread* t, jstring s, jboolean* isCopy) +{ + if (t->criticalLevel == 0) { + enter(t, Thread::ActiveState); + } + + ++t->criticalLevel; + + if (isCopy) { + *isCopy = true; + } + + object data = (*s)->data(); + if (objectClass(t, data) == type(t, GcByteArray::Type)) { + return GetStringChars(t, s, isCopy); + } else { + return &cast(t, data)->body()[(*s)->offset(t)]; + } +} + +void JNICALL ReleaseStringCritical(Thread* t, jstring s, const jchar* chars) +{ + if (objectClass(t, (*s)->data()) == type(t, GcByteArray::Type)) { + ReleaseStringChars(t, s, chars); + } + + if ((--t->criticalLevel) == 0) { + enter(t, Thread::IdleState); + } +} + +jsize JNICALL GetStringUTFLength(Thread* t, jstring s) +{ + ENTER(t, Thread::ActiveState); + + return stringUTFLength(t, *s); +} + +const char* JNICALL GetStringUTFChars(Thread* t, jstring s, jboolean* isCopy) +{ + ENTER(t, Thread::ActiveState); + + int length = stringUTFLength(t, *s); + char* chars = static_cast(t->m->heap->allocate(length + 1)); + stringUTFChars(t, *s, chars, length); + + if (isCopy) + *isCopy = true; + return chars; +} + +void JNICALL ReleaseStringUTFChars(Thread* t, jstring s, const char* chars) +{ + ENTER(t, Thread::ActiveState); + + t->m->heap->free(chars, stringUTFLength(t, *s) + 1); +} + +void JNICALL GetStringUTFRegion(Thread* t, + jstring s, + jsize start, + jsize length, + char* dst) +{ + ENTER(t, Thread::ActiveState); + + stringUTFChars( + t, *s, start, length, dst, stringUTFLength(t, *s, start, length)); +} + +jsize JNICALL GetArrayLength(Thread* t, jarray array) +{ + ENTER(t, Thread::ActiveState); + + return fieldAtOffset(*array, BytesPerWord); +} + +uint64_t newString(Thread* t, uintptr_t* arguments) +{ + const jchar* chars = reinterpret_cast(arguments[0]); + jsize size = arguments[1]; + + GcCharArray* a = makeCharArray(t, size); + if (size) { + memcpy(a->body().begin(), chars, size * sizeof(jchar)); + } + + return reinterpret_cast( + makeLocalReference(t, t->m->classpath->makeString(t, a, 0, size))); +} + +jstring JNICALL NewString(Thread* t, const jchar* chars, jsize size) +{ + if (chars == 0) + return 0; + + uintptr_t arguments[] + = {reinterpret_cast(chars), static_cast(size)}; + + return reinterpret_cast(run(t, newString, arguments)); +} + +uint64_t newStringUTF(Thread* t, uintptr_t* arguments) +{ + const char* chars = reinterpret_cast(arguments[0]); + + object array = parseUtf8(t, chars, strlen(chars)); + + return reinterpret_cast(makeLocalReference( + t, + t->m->classpath->makeString( + t, array, 0, fieldAtOffset(array, BytesPerWord) - 1))); +} + +jstring JNICALL NewStringUTF(Thread* t, const char* chars) +{ + if (chars == 0) + return 0; + + uintptr_t arguments[] = {reinterpret_cast(chars)}; + + return reinterpret_cast(run(t, newStringUTF, arguments)); +} + +void replace(int a, int b, const char* in, int8_t* out) +{ + while (*in) { + *out = (*in == a ? b : *in); + ++in; + ++out; + } + *out = 0; +} + +uint64_t defineClass(Thread* t, uintptr_t* arguments) +{ + jobject loader = reinterpret_cast(arguments[0]); + const uint8_t* buffer = reinterpret_cast(arguments[1]); + jsize length = arguments[2]; + + return reinterpret_cast(makeLocalReference( + t, + getJClass( + t, + cast(t, + defineClass(t, + loader ? cast(t, *loader) + : roots(t)->bootLoader(), + buffer, + length))))); +} + +jclass JNICALL DefineClass(Thread* t, + const char*, + jobject loader, + const jbyte* buffer, + jsize length) +{ + uintptr_t arguments[] = {reinterpret_cast(loader), + reinterpret_cast(buffer), + static_cast(length)}; + + return reinterpret_cast(run(t, defineClass, arguments)); +} + +uint64_t findClass(Thread* t, uintptr_t* arguments) +{ + const char* name = reinterpret_cast(arguments[0]); + + GcByteArray* n = makeByteArray(t, strlen(name) + 1); + replace('.', '/', name, n->body().begin()); + + GcMethod* caller = getCaller(t, 0); + + GcClass* c + = resolveClass(t, + caller ? t->m->classpath->libraryClassLoader(t, caller) + : roots(t)->appLoader(), + n); + + if (t->m->classpath->mayInitClasses()) { + PROTECT(t, c); + + initClass(t, c); + } + + return reinterpret_cast(makeLocalReference(t, getJClass(t, c))); +} + +jclass JNICALL FindClass(Thread* t, const char* name) +{ + uintptr_t arguments[] = {reinterpret_cast(name)}; + + return reinterpret_cast(run(t, findClass, arguments)); +} + +uint64_t throwNew(Thread* t, uintptr_t* arguments) +{ + jclass c = reinterpret_cast(arguments[0]); + const char* message = reinterpret_cast(arguments[1]); + + GcString* m = 0; + PROTECT(t, m); + + if (message) { + m = makeString(t, "%s", message); + } + + object trace = makeTrace(t); + PROTECT(t, trace); + + t->exception = cast(t, make(t, (*c)->vmClass())); + t->exception->setMessage(t, m); + t->exception->setTrace(t, trace); + + return 1; +} + +jint JNICALL ThrowNew(Thread* t, jclass c, const char* message) +{ + if (t->exception) { + return -1; + } + + uintptr_t arguments[] + = {reinterpret_cast(c), reinterpret_cast(message)}; + + return run(t, throwNew, arguments) ? 0 : -1; +} + +jint JNICALL Throw(Thread* t, jthrowable throwable) +{ + if (t->exception) { + return -1; + } + + ENTER(t, Thread::ActiveState); + + t->exception = *throwable; + + return 0; +} + +jobject JNICALL NewLocalRef(Thread* t, jobject o) +{ + ENTER(t, Thread::ActiveState); + + return makeLocalReference(t, *o); +} + +void JNICALL DeleteLocalRef(Thread* t, jobject r) +{ + ENTER(t, Thread::ActiveState); + + disposeLocalReference(t, r); +} + +jboolean JNICALL ExceptionCheck(Thread* t) +{ + return t->exception != 0; +} + +uint64_t getObjectClass(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + + return reinterpret_cast( + makeLocalReference(t, getJClass(t, objectClass(t, *o)))); +} + +jclass JNICALL GetObjectClass(Thread* t, jobject o) +{ + uintptr_t arguments[] = {reinterpret_cast(o)}; + + return reinterpret_cast(run(t, getObjectClass, arguments)); +} + +uint64_t getSuperclass(Thread* t, uintptr_t* arguments) +{ + GcClass* class_ = (*reinterpret_cast(arguments[0]))->vmClass(); + if (class_->flags() & ACC_INTERFACE) { + return 0; + } else { + GcClass* super = class_->super(); + return super ? reinterpret_cast( + makeLocalReference(t, getJClass(t, super))) + : 0; + } +} + +jclass JNICALL GetSuperclass(Thread* t, jclass c) +{ + uintptr_t arguments[] = {reinterpret_cast(c)}; + + return reinterpret_cast(run(t, getSuperclass, arguments)); +} + +uint64_t isInstanceOf(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + jclass c = reinterpret_cast(arguments[1]); + + return instanceOf(t, (*c)->vmClass(), *o); +} + +jboolean JNICALL IsInstanceOf(Thread* t, jobject o, jclass c) +{ + uintptr_t arguments[] + = {reinterpret_cast(o), reinterpret_cast(c)}; + + return run(t, isInstanceOf, arguments); +} + +uint64_t isAssignableFrom(Thread* t, uintptr_t* arguments) +{ + jclass b = reinterpret_cast(arguments[0]); + jclass a = reinterpret_cast(arguments[1]); + + return isAssignableFrom(t, (*a)->vmClass(), (*b)->vmClass()); +} + +jboolean JNICALL IsAssignableFrom(Thread* t, jclass b, jclass a) +{ + uintptr_t arguments[] + = {reinterpret_cast(b), reinterpret_cast(a)}; + + return run(t, isAssignableFrom, arguments); +} + +GcMethod* findMethod(Thread* t, jclass c, const char* name, const char* spec) +{ + GcByteArray* n = makeByteArray(t, "%s", name); + PROTECT(t, n); + + GcByteArray* s = makeByteArray(t, "%s", spec); + return vm::findMethod(t, (*c)->vmClass(), n, s); +} + +jint methodID(Thread* t, GcMethod* method) +{ + int id = method->nativeID(); + + loadMemoryBarrier(); + + if (id == 0) { + PROTECT(t, method); + + ACQUIRE(t, t->m->referenceLock); + + if (method->nativeID() == 0) { + GcVector* v = vectorAppend(t, roots(t)->jNIMethodTable(), method); + // sequence point, for gc (don't recombine statements) + roots(t)->setJNIMethodTable(t, v); + + storeStoreMemoryBarrier(); + + method->nativeID() = roots(t)->jNIMethodTable()->size(); + } + } + + return method->nativeID(); +} + +uint64_t getMethodID(Thread* t, uintptr_t* arguments) +{ + jclass c = reinterpret_cast(arguments[0]); + const char* name = reinterpret_cast(arguments[1]); + const char* spec = reinterpret_cast(arguments[2]); + + GcMethod* method = findMethod(t, c, name, spec); + + assertT(t, (method->flags() & ACC_STATIC) == 0); + + return methodID(t, method); +} + +jmethodID JNICALL + GetMethodID(Thread* t, jclass c, const char* name, const char* spec) +{ + uintptr_t arguments[] = {reinterpret_cast(c), + reinterpret_cast(name), + reinterpret_cast(spec)}; + + return run(t, getMethodID, arguments); +} + +uint64_t getStaticMethodID(Thread* t, uintptr_t* arguments) +{ + jclass c = reinterpret_cast(arguments[0]); + const char* name = reinterpret_cast(arguments[1]); + const char* spec = reinterpret_cast(arguments[2]); + + GcMethod* method = findMethod(t, c, name, spec); + + assertT(t, method->flags() & ACC_STATIC); + + return methodID(t, method); +} + +jmethodID JNICALL + GetStaticMethodID(Thread* t, jclass c, const char* name, const char* spec) +{ + uintptr_t arguments[] = {reinterpret_cast(c), + reinterpret_cast(name), + reinterpret_cast(spec)}; + + return run(t, getStaticMethodID, arguments); +} + +GcMethod* getMethod(Thread* t, jmethodID m) +{ + assertT(t, m); + + GcMethod* method + = cast(t, roots(t)->jNIMethodTable()->body()[m - 1]); + + assertT(t, (method->flags() & ACC_STATIC) == 0); + + return method; +} + +uint64_t newObjectV(Thread* t, uintptr_t* arguments) +{ + jclass c = reinterpret_cast(arguments[0]); + jmethodID m = arguments[1]; + va_list* a = reinterpret_cast(arguments[2]); + + object o = make(t, (*c)->vmClass()); + PROTECT(t, o); + + t->m->processor->invokeList(t, getMethod(t, m), o, true, *a); + + return reinterpret_cast(makeLocalReference(t, o)); +} + +jobject JNICALL NewObjectV(Thread* t, jclass c, jmethodID m, va_list a) +{ + uintptr_t arguments[] = {reinterpret_cast(c), + m, + reinterpret_cast(VA_LIST(a))}; + + return reinterpret_cast(run(t, newObjectV, arguments)); +} + +jobject JNICALL NewObject(Thread* t, jclass c, jmethodID m, ...) +{ + va_list a; + va_start(a, m); + + jobject r = NewObjectV(t, c, m, a); + + va_end(a); + + return r; +} + +uint64_t newObjectA(Thread* t, uintptr_t* arguments) +{ + jclass c = reinterpret_cast(arguments[0]); + jmethodID m = arguments[1]; + const jvalue* a = reinterpret_cast(arguments[2]); + + object o = make(t, (*c)->vmClass()); + PROTECT(t, o); + + t->m->processor->invokeArray(t, getMethod(t, m), o, a); + + return reinterpret_cast(makeLocalReference(t, o)); +} + +jobject JNICALL NewObjectA(Thread* t, jclass c, jmethodID m, const jvalue* a) +{ + uintptr_t arguments[] + = {reinterpret_cast(c), m, reinterpret_cast(a)}; + + return reinterpret_cast(run(t, newObjectA, arguments)); +} + +uint64_t callObjectMethodV(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + jmethodID m = arguments[1]; + va_list* a = reinterpret_cast(arguments[2]); + + return reinterpret_cast(makeLocalReference( + t, t->m->processor->invokeList(t, getMethod(t, m), *o, true, *a))); +} + +jobject JNICALL CallObjectMethodV(Thread* t, jobject o, jmethodID m, va_list a) +{ + uintptr_t arguments[] = {reinterpret_cast(o), + m, + reinterpret_cast(VA_LIST(a))}; + + return reinterpret_cast(run(t, callObjectMethodV, arguments)); +} + +jobject JNICALL CallObjectMethod(Thread* t, jobject o, jmethodID m, ...) +{ + va_list a; + va_start(a, m); + + jobject r = CallObjectMethodV(t, o, m, a); + + va_end(a); + + return r; +} + +uint64_t callObjectMethodA(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + jmethodID m = arguments[1]; + const jvalue* a = reinterpret_cast(arguments[2]); + + return reinterpret_cast(makeLocalReference( + t, t->m->processor->invokeArray(t, getMethod(t, m), *o, a))); +} + +jobject JNICALL + CallObjectMethodA(Thread* t, jobject o, jmethodID m, const jvalue* a) +{ + uintptr_t arguments[] + = {reinterpret_cast(o), m, reinterpret_cast(a)}; + + return reinterpret_cast(run(t, callObjectMethodA, arguments)); +} + +uint64_t callIntMethodV(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + jmethodID m = arguments[1]; + va_list* a = reinterpret_cast(arguments[2]); + + return cast( + t, t->m->processor->invokeList(t, getMethod(t, m), *o, true, *a)) + ->value(); +} + +jboolean JNICALL + CallBooleanMethodV(Thread* t, jobject o, jmethodID m, va_list a) +{ + uintptr_t arguments[] = {reinterpret_cast(o), + m, + reinterpret_cast(VA_LIST(a))}; + + return run(t, callIntMethodV, arguments) != 0; +} + +jboolean JNICALL CallBooleanMethod(Thread* t, jobject o, jmethodID m, ...) +{ + va_list a; + va_start(a, m); + + jboolean r = CallBooleanMethodV(t, o, m, a); + + va_end(a); + + return r; +} + +uint64_t callIntMethodA(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + jmethodID m = arguments[1]; + const jvalue* a = reinterpret_cast(arguments[2]); + + return cast(t, t->m->processor->invokeArray(t, getMethod(t, m), *o, a)) + ->value(); +} + +jboolean JNICALL + CallBooleanMethodA(Thread* t, jobject o, jmethodID m, const jvalue* a) +{ + uintptr_t arguments[] + = {reinterpret_cast(o), m, reinterpret_cast(a)}; + + return run(t, callIntMethodA, arguments) != 0; +} + +jbyte JNICALL CallByteMethodV(Thread* t, jobject o, jmethodID m, va_list a) +{ + uintptr_t arguments[] = {reinterpret_cast(o), + m, + reinterpret_cast(VA_LIST(a))}; + + return run(t, callIntMethodV, arguments); +} + +jbyte JNICALL CallByteMethod(Thread* t, jobject o, jmethodID m, ...) +{ + va_list a; + va_start(a, m); + + jbyte r = CallByteMethodV(t, o, m, a); + + va_end(a); + + return r; +} + +jbyte JNICALL + CallByteMethodA(Thread* t, jobject o, jmethodID m, const jvalue* a) +{ + uintptr_t arguments[] + = {reinterpret_cast(o), m, reinterpret_cast(a)}; + + return run(t, callIntMethodA, arguments); +} + +jchar JNICALL CallCharMethodV(Thread* t, jobject o, jmethodID m, va_list a) +{ + uintptr_t arguments[] = {reinterpret_cast(o), + m, + reinterpret_cast(VA_LIST(a))}; + + return run(t, callIntMethodV, arguments); +} + +jchar JNICALL CallCharMethod(Thread* t, jobject o, jmethodID m, ...) +{ + va_list a; + va_start(a, m); + + jchar r = CallCharMethodV(t, o, m, a); + + va_end(a); + + return r; +} + +jchar JNICALL + CallCharMethodA(Thread* t, jobject o, jmethodID m, const jvalue* a) +{ + uintptr_t arguments[] + = {reinterpret_cast(o), m, reinterpret_cast(a)}; + + return run(t, callIntMethodA, arguments); +} + +jshort JNICALL CallShortMethodV(Thread* t, jobject o, jmethodID m, va_list a) +{ + uintptr_t arguments[] = {reinterpret_cast(o), + m, + reinterpret_cast(VA_LIST(a))}; + + return run(t, callIntMethodV, arguments); +} + +jshort JNICALL CallShortMethod(Thread* t, jobject o, jmethodID m, ...) +{ + va_list a; + va_start(a, m); + + jshort r = CallShortMethodV(t, o, m, a); + + va_end(a); + + return r; +} + +jshort JNICALL + CallShortMethodA(Thread* t, jobject o, jmethodID m, const jvalue* a) +{ + uintptr_t arguments[] + = {reinterpret_cast(o), m, reinterpret_cast(a)}; + + return run(t, callIntMethodA, arguments); +} + +jint JNICALL CallIntMethodV(Thread* t, jobject o, jmethodID m, va_list a) +{ + uintptr_t arguments[] = {reinterpret_cast(o), + m, + reinterpret_cast(VA_LIST(a))}; + + return run(t, callIntMethodV, arguments); +} + +jint JNICALL CallIntMethod(Thread* t, jobject o, jmethodID m, ...) +{ + va_list a; + va_start(a, m); + + jint r = CallIntMethodV(t, o, m, a); + + va_end(a); + + return r; +} + +jint JNICALL CallIntMethodA(Thread* t, jobject o, jmethodID m, const jvalue* a) +{ + uintptr_t arguments[] + = {reinterpret_cast(o), m, reinterpret_cast(a)}; + + return run(t, callIntMethodA, arguments); +} + +uint64_t callLongMethodV(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + jmethodID m = arguments[1]; + va_list* a = reinterpret_cast(arguments[2]); + + return cast( + t, t->m->processor->invokeList(t, getMethod(t, m), *o, true, *a)) + ->value(); +} + +jlong JNICALL CallLongMethodV(Thread* t, jobject o, jmethodID m, va_list a) +{ + uintptr_t arguments[] = {reinterpret_cast(o), + m, + reinterpret_cast(VA_LIST(a))}; + + return run(t, callLongMethodV, arguments); +} + +jlong JNICALL CallLongMethod(Thread* t, jobject o, jmethodID m, ...) +{ + va_list a; + va_start(a, m); + + jlong r = CallLongMethodV(t, o, m, a); + + va_end(a); + + return r; +} + +uint64_t callLongMethodA(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + jmethodID m = arguments[1]; + const jvalue* a = reinterpret_cast(arguments[2]); + + return cast(t, + t->m->processor->invokeArray(t, getMethod(t, m), *o, a)) + ->value(); +} + +jlong JNICALL + CallLongMethodA(Thread* t, jobject o, jmethodID m, const jvalue* a) +{ + uintptr_t arguments[] + = {reinterpret_cast(o), m, reinterpret_cast(a)}; + + return run(t, callLongMethodA, arguments); +} + +jfloat JNICALL CallFloatMethodV(Thread* t, jobject o, jmethodID m, va_list a) +{ + uintptr_t arguments[] = {reinterpret_cast(o), + m, + reinterpret_cast(VA_LIST(a))}; + + return bitsToFloat(run(t, callIntMethodV, arguments)); +} + +jfloat JNICALL CallFloatMethod(Thread* t, jobject o, jmethodID m, ...) +{ + va_list a; + va_start(a, m); + + jfloat r = CallFloatMethodV(t, o, m, a); + + va_end(a); + + return r; +} + +jfloat JNICALL + CallFloatMethodA(Thread* t, jobject o, jmethodID m, const jvalue* a) +{ + uintptr_t arguments[] + = {reinterpret_cast(o), m, reinterpret_cast(a)}; + + return bitsToFloat(run(t, callIntMethodA, arguments)); +} + +jdouble JNICALL CallDoubleMethodV(Thread* t, jobject o, jmethodID m, va_list a) +{ + uintptr_t arguments[] = {reinterpret_cast(o), + m, + reinterpret_cast(VA_LIST(a))}; + + return bitsToDouble(run(t, callLongMethodV, arguments)); +} + +jdouble JNICALL CallDoubleMethod(Thread* t, jobject o, jmethodID m, ...) +{ + va_list a; + va_start(a, m); + + jdouble r = CallDoubleMethodV(t, o, m, a); + + va_end(a); + + return r; +} + +jdouble JNICALL + CallDoubleMethodA(Thread* t, jobject o, jmethodID m, const jvalue* a) +{ + uintptr_t arguments[] + = {reinterpret_cast(o), m, reinterpret_cast(a)}; + + return bitsToDouble(run(t, callLongMethodA, arguments)); +} + +uint64_t callVoidMethodV(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + jmethodID m = arguments[1]; + va_list* a = reinterpret_cast(arguments[2]); + + t->m->processor->invokeList(t, getMethod(t, m), *o, true, *a); + + return 0; +} + +void JNICALL CallVoidMethodV(Thread* t, jobject o, jmethodID m, va_list a) +{ + uintptr_t arguments[] = {reinterpret_cast(o), + m, + reinterpret_cast(VA_LIST(a))}; + + run(t, callVoidMethodV, arguments); +} + +void JNICALL CallVoidMethod(Thread* t, jobject o, jmethodID m, ...) +{ + va_list a; + va_start(a, m); + + CallVoidMethodV(t, o, m, a); + + va_end(a); +} + +uint64_t callVoidMethodA(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + jmethodID m = arguments[1]; + const jvalue* a = reinterpret_cast(arguments[2]); + + t->m->processor->invokeArray(t, getMethod(t, m), *o, a); + + return 0; +} + +void JNICALL CallVoidMethodA(Thread* t, jobject o, jmethodID m, const jvalue* a) +{ + uintptr_t arguments[] + = {reinterpret_cast(o), m, reinterpret_cast(a)}; + + run(t, callVoidMethodA, arguments); +} + +GcMethod* getStaticMethod(Thread* t, jmethodID m) +{ + assertT(t, m); + + GcMethod* method + = cast(t, roots(t)->jNIMethodTable()->body()[m - 1]); + + assertT(t, method->flags() & ACC_STATIC); + + return method; +} + +uint64_t callStaticObjectMethodV(Thread* t, uintptr_t* arguments) +{ + jmethodID m = arguments[0]; + va_list* a = reinterpret_cast(arguments[1]); + + return reinterpret_cast(makeLocalReference( + t, t->m->processor->invokeList(t, getStaticMethod(t, m), 0, true, *a))); +} + +jobject JNICALL + CallStaticObjectMethodV(Thread* t, jclass, jmethodID m, va_list a) +{ + uintptr_t arguments[] = {m, reinterpret_cast(VA_LIST(a))}; + + return reinterpret_cast(run(t, callStaticObjectMethodV, arguments)); +} + +jobject JNICALL CallStaticObjectMethod(Thread* t, jclass c, jmethodID m, ...) +{ + va_list a; + va_start(a, m); + + jobject r = CallStaticObjectMethodV(t, c, m, a); + + va_end(a); + + return r; +} + +uint64_t callStaticObjectMethodA(Thread* t, uintptr_t* arguments) +{ + jmethodID m = arguments[0]; + const jvalue* a = reinterpret_cast(arguments[1]); + + return reinterpret_cast(makeLocalReference( + t, t->m->processor->invokeArray(t, getStaticMethod(t, m), 0, a))); +} + +jobject JNICALL + CallStaticObjectMethodA(Thread* t, jclass, jmethodID m, const jvalue* a) +{ + uintptr_t arguments[] = {m, reinterpret_cast(a)}; + + return reinterpret_cast(run(t, callStaticObjectMethodA, arguments)); +} + +uint64_t callStaticIntMethodV(Thread* t, uintptr_t* arguments) +{ + jmethodID m = arguments[0]; + va_list* a = reinterpret_cast(arguments[1]); + + return cast(t, + t->m->processor->invokeList( + t, getStaticMethod(t, m), 0, true, *a))->value(); +} + +jboolean JNICALL + CallStaticBooleanMethodV(Thread* t, jclass, jmethodID m, va_list a) +{ + uintptr_t arguments[] = {m, reinterpret_cast(VA_LIST(a))}; + + return run(t, callStaticIntMethodV, arguments) != 0; +} + +jboolean JNICALL CallStaticBooleanMethod(Thread* t, jclass c, jmethodID m, ...) +{ + va_list a; + va_start(a, m); + + jboolean r = CallStaticBooleanMethodV(t, c, m, a); + + va_end(a); + + return r; +} + +uint64_t callStaticIntMethodA(Thread* t, uintptr_t* arguments) +{ + jmethodID m = arguments[0]; + const jvalue* a = reinterpret_cast(arguments[1]); + + return cast( + t, t->m->processor->invokeArray(t, getStaticMethod(t, m), 0, a)) + ->value(); +} + +jboolean JNICALL + CallStaticBooleanMethodA(Thread* t, jclass, jmethodID m, const jvalue* a) +{ + uintptr_t arguments[] = {m, reinterpret_cast(a)}; + + return run(t, callStaticIntMethodA, arguments) != 0; +} + +jbyte JNICALL CallStaticByteMethodV(Thread* t, jclass, jmethodID m, va_list a) +{ + uintptr_t arguments[] = {m, reinterpret_cast(VA_LIST(a))}; + + return run(t, callStaticIntMethodV, arguments); +} + +jbyte JNICALL CallStaticByteMethod(Thread* t, jclass c, jmethodID m, ...) +{ + va_list a; + va_start(a, m); + + jbyte r = CallStaticByteMethodV(t, c, m, a); + + va_end(a); + + return r; +} + +jbyte JNICALL + CallStaticByteMethodA(Thread* t, jclass, jmethodID m, const jvalue* a) +{ + uintptr_t arguments[] = {m, reinterpret_cast(a)}; + + return run(t, callStaticIntMethodA, arguments); +} + +jchar JNICALL CallStaticCharMethodV(Thread* t, jclass, jmethodID m, va_list a) +{ + uintptr_t arguments[] = {m, reinterpret_cast(VA_LIST(a))}; + + return run(t, callStaticIntMethodV, arguments); +} + +jchar JNICALL CallStaticCharMethod(Thread* t, jclass c, jmethodID m, ...) +{ + va_list a; + va_start(a, m); + + jchar r = CallStaticCharMethodV(t, c, m, a); + + va_end(a); + + return r; +} + +jchar JNICALL + CallStaticCharMethodA(Thread* t, jclass, jmethodID m, const jvalue* a) +{ + uintptr_t arguments[] = {m, reinterpret_cast(a)}; + + return run(t, callStaticIntMethodA, arguments); +} + +jshort JNICALL CallStaticShortMethodV(Thread* t, jclass, jmethodID m, va_list a) +{ + uintptr_t arguments[] = {m, reinterpret_cast(VA_LIST(a))}; + + return run(t, callStaticIntMethodV, arguments); +} + +jshort JNICALL CallStaticShortMethod(Thread* t, jclass c, jmethodID m, ...) +{ + va_list a; + va_start(a, m); + + jshort r = CallStaticShortMethodV(t, c, m, a); + + va_end(a); + + return r; +} + +jshort JNICALL + CallStaticShortMethodA(Thread* t, jclass, jmethodID m, const jvalue* a) +{ + uintptr_t arguments[] = {m, reinterpret_cast(a)}; + + return run(t, callStaticIntMethodA, arguments); +} + +jint JNICALL CallStaticIntMethodV(Thread* t, jclass, jmethodID m, va_list a) +{ + uintptr_t arguments[] = {m, reinterpret_cast(VA_LIST(a))}; + + return run(t, callStaticIntMethodV, arguments); +} + +jint JNICALL CallStaticIntMethod(Thread* t, jclass c, jmethodID m, ...) +{ + va_list a; + va_start(a, m); + + jint r = CallStaticIntMethodV(t, c, m, a); + + va_end(a); + + return r; +} + +jint JNICALL + CallStaticIntMethodA(Thread* t, jclass, jmethodID m, const jvalue* a) +{ + uintptr_t arguments[] = {m, reinterpret_cast(a)}; + + return run(t, callStaticIntMethodA, arguments); +} + +uint64_t callStaticLongMethodV(Thread* t, uintptr_t* arguments) +{ + jmethodID m = arguments[0]; + va_list* a = reinterpret_cast(arguments[1]); + + return cast(t, + t->m->processor->invokeList( + t, getStaticMethod(t, m), 0, true, *a))->value(); +} + +jlong JNICALL CallStaticLongMethodV(Thread* t, jclass, jmethodID m, va_list a) +{ + uintptr_t arguments[] = {m, reinterpret_cast(VA_LIST(a))}; + + return run(t, callStaticLongMethodV, arguments); +} + +jlong JNICALL CallStaticLongMethod(Thread* t, jclass c, jmethodID m, ...) +{ + va_list a; + va_start(a, m); + + jlong r = CallStaticLongMethodV(t, c, m, a); + + va_end(a); + + return r; +} + +uint64_t callStaticLongMethodA(Thread* t, uintptr_t* arguments) +{ + jmethodID m = arguments[0]; + const jvalue* a = reinterpret_cast(arguments[1]); + + return cast( + t, t->m->processor->invokeArray(t, getStaticMethod(t, m), 0, a)) + ->value(); +} + +jlong JNICALL + CallStaticLongMethodA(Thread* t, jclass, jmethodID m, const jvalue* a) +{ + uintptr_t arguments[] = {m, reinterpret_cast(a)}; + + return run(t, callStaticLongMethodA, arguments); +} + +jfloat JNICALL CallStaticFloatMethodV(Thread* t, jclass, jmethodID m, va_list a) +{ + uintptr_t arguments[] = {m, reinterpret_cast(VA_LIST(a))}; + + return bitsToFloat(run(t, callStaticIntMethodV, arguments)); +} + +jfloat JNICALL CallStaticFloatMethod(Thread* t, jclass c, jmethodID m, ...) +{ + va_list a; + va_start(a, m); + + jfloat r = CallStaticFloatMethodV(t, c, m, a); + + va_end(a); + + return r; +} + +jfloat JNICALL + CallStaticFloatMethodA(Thread* t, jclass, jmethodID m, const jvalue* a) +{ + uintptr_t arguments[] = {m, reinterpret_cast(a)}; + + return bitsToFloat(run(t, callStaticIntMethodA, arguments)); +} + +jdouble JNICALL + CallStaticDoubleMethodV(Thread* t, jclass, jmethodID m, va_list a) +{ + uintptr_t arguments[] = {m, reinterpret_cast(VA_LIST(a))}; + + return bitsToDouble(run(t, callStaticLongMethodV, arguments)); +} + +jdouble JNICALL CallStaticDoubleMethod(Thread* t, jclass c, jmethodID m, ...) +{ + va_list a; + va_start(a, m); + + jdouble r = CallStaticDoubleMethodV(t, c, m, a); + + va_end(a); + + return r; +} + +jdouble JNICALL + CallStaticDoubleMethodA(Thread* t, jclass, jmethodID m, const jvalue* a) +{ + uintptr_t arguments[] = {m, reinterpret_cast(a)}; + + return bitsToDouble(run(t, callStaticLongMethodA, arguments)); +} + +uint64_t callStaticVoidMethodV(Thread* t, uintptr_t* arguments) +{ + jmethodID m = arguments[0]; + va_list* a = reinterpret_cast(arguments[1]); + + t->m->processor->invokeList(t, getStaticMethod(t, m), 0, true, *a); + + return 0; +} + +void JNICALL CallStaticVoidMethodV(Thread* t, jclass, jmethodID m, va_list a) +{ + uintptr_t arguments[] = {m, reinterpret_cast(VA_LIST(a))}; + + run(t, callStaticVoidMethodV, arguments); +} + +void JNICALL CallStaticVoidMethod(Thread* t, jclass c, jmethodID m, ...) +{ + va_list a; + va_start(a, m); + + CallStaticVoidMethodV(t, c, m, a); + + va_end(a); +} + +uint64_t callStaticVoidMethodA(Thread* t, uintptr_t* arguments) +{ + jmethodID m = arguments[0]; + const jvalue* a = reinterpret_cast(arguments[1]); + + t->m->processor->invokeArray(t, getStaticMethod(t, m), 0, a); + + return 0; +} + +void JNICALL + CallStaticVoidMethodA(Thread* t, jclass, jmethodID m, const jvalue* a) +{ + uintptr_t arguments[] = {m, reinterpret_cast(a)}; + + run(t, callStaticVoidMethodA, arguments); +} + +jint fieldID(Thread* t, GcField* field) +{ + int id = field->nativeID(); + + loadMemoryBarrier(); + + if (id == 0) { + PROTECT(t, field); + + ACQUIRE(t, t->m->referenceLock); + + if (field->nativeID() == 0) { + GcVector* v = vectorAppend(t, roots(t)->jNIFieldTable(), field); + // sequence point, for gc (don't recombine statements) + roots(t)->setJNIFieldTable(t, v); + + storeStoreMemoryBarrier(); + + field->nativeID() = roots(t)->jNIFieldTable()->size(); + } + } + + return field->nativeID(); +} + +uint64_t getFieldID(Thread* t, uintptr_t* arguments) +{ + jclass c = reinterpret_cast(arguments[0]); + const char* name = reinterpret_cast(arguments[1]); + const char* spec = reinterpret_cast(arguments[2]); + + return fieldID(t, resolveField(t, (*c)->vmClass(), name, spec)); +} + +jfieldID JNICALL + GetFieldID(Thread* t, jclass c, const char* name, const char* spec) +{ + uintptr_t arguments[] = {reinterpret_cast(c), + reinterpret_cast(name), + reinterpret_cast(spec)}; + + return run(t, getFieldID, arguments); +} + +jfieldID JNICALL + GetStaticFieldID(Thread* t, jclass c, const char* name, const char* spec) +{ + uintptr_t arguments[] = {reinterpret_cast(c), + reinterpret_cast(name), + reinterpret_cast(spec)}; + + return run(t, getFieldID, arguments); +} + +GcField* getField(Thread* t, jfieldID f) +{ + assertT(t, f); + + GcField* field = cast(t, roots(t)->jNIFieldTable()->body()[f - 1]); + + assertT(t, (field->flags() & ACC_STATIC) == 0); + + return field; +} + +uint64_t getObjectField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + GcField* field = getField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return reinterpret_cast( + makeLocalReference(t, fieldAtOffset(*o, field->offset()))); +} + +jobject JNICALL GetObjectField(Thread* t, jobject o, jfieldID field) +{ + uintptr_t arguments[] = {reinterpret_cast(o), field}; + + return reinterpret_cast(run(t, getObjectField, arguments)); +} + +uint64_t getBooleanField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + GcField* field = getField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return fieldAtOffset(*o, field->offset()); +} + +jboolean JNICALL GetBooleanField(Thread* t, jobject o, jfieldID field) +{ + uintptr_t arguments[] = {reinterpret_cast(o), field}; + + return run(t, getBooleanField, arguments); +} + +uint64_t getByteField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + GcField* field = getField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return fieldAtOffset(*o, field->offset()); +} + +jbyte JNICALL GetByteField(Thread* t, jobject o, jfieldID field) +{ + uintptr_t arguments[] = {reinterpret_cast(o), field}; + + return run(t, getByteField, arguments); +} + +uint64_t getCharField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + GcField* field = getField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return fieldAtOffset(*o, field->offset()); +} + +jchar JNICALL GetCharField(Thread* t, jobject o, jfieldID field) +{ + uintptr_t arguments[] = {reinterpret_cast(o), field}; + + return run(t, getCharField, arguments); +} + +uint64_t getShortField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + GcField* field = getField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return fieldAtOffset(*o, field->offset()); +} + +jshort JNICALL GetShortField(Thread* t, jobject o, jfieldID field) +{ + uintptr_t arguments[] = {reinterpret_cast(o), field}; + + return run(t, getShortField, arguments); +} + +uint64_t getIntField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + GcField* field = getField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return fieldAtOffset(*o, field->offset()); +} + +jint JNICALL GetIntField(Thread* t, jobject o, jfieldID field) +{ + uintptr_t arguments[] = {reinterpret_cast(o), field}; + + return run(t, getIntField, arguments); +} + +uint64_t getLongField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + GcField* field = getField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return fieldAtOffset(*o, field->offset()); +} + +jlong JNICALL GetLongField(Thread* t, jobject o, jfieldID field) +{ + uintptr_t arguments[] = {reinterpret_cast(o), field}; + + return run(t, getLongField, arguments); +} + +uint64_t getFloatField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + GcField* field = getField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return floatToBits(fieldAtOffset(*o, field->offset())); +} + +jfloat JNICALL GetFloatField(Thread* t, jobject o, jfieldID field) +{ + uintptr_t arguments[] = {reinterpret_cast(o), field}; + + return bitsToFloat(run(t, getFloatField, arguments)); +} + +uint64_t getDoubleField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + GcField* field = getField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return doubleToBits(fieldAtOffset(*o, field->offset())); +} + +jdouble JNICALL GetDoubleField(Thread* t, jobject o, jfieldID field) +{ + uintptr_t arguments[] = {reinterpret_cast(o), field}; + + return bitsToDouble(run(t, getDoubleField, arguments)); +} + +uint64_t setObjectField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + GcField* field = getField(t, arguments[1]); + jobject v = reinterpret_cast(arguments[2]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + setField(t, *o, field->offset(), (v ? *v : 0)); + + return 1; +} + +void JNICALL SetObjectField(Thread* t, jobject o, jfieldID field, jobject v) +{ + uintptr_t arguments[] + = {reinterpret_cast(o), field, reinterpret_cast(v)}; + + run(t, setObjectField, arguments); +} + +uint64_t setBooleanField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + GcField* field = getField(t, arguments[1]); + jboolean v = arguments[2]; + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + fieldAtOffset(*o, field->offset()) = v; + + return 1; +} + +void JNICALL SetBooleanField(Thread* t, jobject o, jfieldID field, jboolean v) +{ + uintptr_t arguments[] = {reinterpret_cast(o), field, v}; + + run(t, setBooleanField, arguments); +} + +uint64_t setByteField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + GcField* field = getField(t, arguments[1]); + jbyte v = arguments[2]; + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + fieldAtOffset(*o, field->offset()) = v; + + return 1; +} + +void JNICALL SetByteField(Thread* t, jobject o, jfieldID field, jbyte v) +{ + uintptr_t arguments[] + = {reinterpret_cast(o), field, static_cast(v)}; + + run(t, setByteField, arguments); +} + +uint64_t setCharField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + GcField* field = getField(t, arguments[1]); + jchar v = arguments[2]; + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + fieldAtOffset(*o, field->offset()) = v; + + return 1; +} + +void JNICALL SetCharField(Thread* t, jobject o, jfieldID field, jchar v) +{ + uintptr_t arguments[] = {reinterpret_cast(o), field, v}; + + run(t, setCharField, arguments); +} + +uint64_t setShortField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + GcField* field = getField(t, arguments[1]); + jshort v = arguments[2]; + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + fieldAtOffset(*o, field->offset()) = v; + + return 1; +} + +void JNICALL SetShortField(Thread* t, jobject o, jfieldID field, jshort v) +{ + uintptr_t arguments[] + = {reinterpret_cast(o), field, static_cast(v)}; + + run(t, setShortField, arguments); +} + +uint64_t setIntField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + GcField* field = getField(t, arguments[1]); + jint v = arguments[2]; + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + fieldAtOffset(*o, field->offset()) = v; + + return 1; +} + +void JNICALL SetIntField(Thread* t, jobject o, jfieldID field, jint v) +{ + uintptr_t arguments[] + = {reinterpret_cast(o), field, static_cast(v)}; + + run(t, setIntField, arguments); +} + +uint64_t setLongField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + GcField* field = getField(t, arguments[1]); + jlong v; + memcpy(&v, arguments + 2, sizeof(jlong)); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + fieldAtOffset(*o, field->offset()) = v; + + return 1; +} + +void JNICALL SetLongField(Thread* t, jobject o, jfieldID field, jlong v) +{ + uintptr_t arguments[2 + (sizeof(jlong) / BytesPerWord)]; + arguments[0] = reinterpret_cast(o); + arguments[1] = field; + memcpy(arguments + 2, &v, sizeof(jlong)); + + run(t, setLongField, arguments); +} + +uint64_t setFloatField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + GcField* field = getField(t, arguments[1]); + jfloat v = bitsToFloat(arguments[2]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + fieldAtOffset(*o, field->offset()) = v; + + return 1; +} + +void JNICALL SetFloatField(Thread* t, jobject o, jfieldID field, jfloat v) +{ + uintptr_t arguments[] + = {reinterpret_cast(o), field, floatToBits(v)}; + + run(t, setFloatField, arguments); +} + +uint64_t setDoubleField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + GcField* field = getField(t, arguments[1]); + jdouble v; + memcpy(&v, arguments + 2, sizeof(jdouble)); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + fieldAtOffset(*o, field->offset()) = v; + + return 1; +} + +void JNICALL SetDoubleField(Thread* t, jobject o, jfieldID field, jdouble v) +{ + uintptr_t arguments[2 + (sizeof(jdouble) / BytesPerWord)]; + arguments[0] = reinterpret_cast(o); + arguments[1] = field; + memcpy(arguments + 2, &v, sizeof(jdouble)); + + run(t, setDoubleField, arguments); +} + +GcField* getStaticField(Thread* t, jfieldID f) +{ + assertT(t, f); + + GcField* field = cast(t, roots(t)->jNIFieldTable()->body()[f - 1]); + + assertT(t, field->flags() & ACC_STATIC); + + return field; +} + +uint64_t getStaticObjectField(Thread* t, uintptr_t* arguments) +{ + GcJclass* c = cast(t, *reinterpret_cast(arguments[0])); + + initClass(t, c->vmClass()); + + GcField* field = getStaticField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return reinterpret_cast(makeLocalReference( + t, fieldAtOffset(c->vmClass()->staticTable(), field->offset()))); +} + +jobject JNICALL GetStaticObjectField(Thread* t, jclass c, jfieldID field) +{ + uintptr_t arguments[] = {reinterpret_cast(c), field}; + + return reinterpret_cast(run(t, getStaticObjectField, arguments)); +} + +uint64_t getStaticBooleanField(Thread* t, uintptr_t* arguments) +{ + GcJclass* c = cast(t, *reinterpret_cast(arguments[0])); + + initClass(t, c->vmClass()); + + GcField* field = getStaticField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return fieldAtOffset(c->vmClass()->staticTable(), field->offset()); +} + +jboolean JNICALL GetStaticBooleanField(Thread* t, jclass c, jfieldID field) +{ + uintptr_t arguments[] = {reinterpret_cast(c), field}; + + return run(t, getStaticBooleanField, arguments); +} + +uint64_t getStaticByteField(Thread* t, uintptr_t* arguments) +{ + GcJclass* c = cast(t, *reinterpret_cast(arguments[0])); + + initClass(t, c->vmClass()); + + GcField* field = getStaticField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return fieldAtOffset(c->vmClass()->staticTable(), field->offset()); +} + +jbyte JNICALL GetStaticByteField(Thread* t, jclass c, jfieldID field) +{ + uintptr_t arguments[] = {reinterpret_cast(c), field}; + + return run(t, getStaticByteField, arguments); +} + +uint64_t getStaticCharField(Thread* t, uintptr_t* arguments) +{ + GcJclass* c = cast(t, *reinterpret_cast(arguments[0])); + + initClass(t, c->vmClass()); + + GcField* field = getStaticField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return fieldAtOffset(c->vmClass()->staticTable(), field->offset()); +} + +jchar JNICALL GetStaticCharField(Thread* t, jclass c, jfieldID field) +{ + uintptr_t arguments[] = {reinterpret_cast(c), field}; + + return run(t, getStaticCharField, arguments); +} + +uint64_t getStaticShortField(Thread* t, uintptr_t* arguments) +{ + GcJclass* c = cast(t, *reinterpret_cast(arguments[0])); + + initClass(t, c->vmClass()); + + GcField* field = getStaticField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return fieldAtOffset(c->vmClass()->staticTable(), field->offset()); +} + +jshort JNICALL GetStaticShortField(Thread* t, jclass c, jfieldID field) +{ + uintptr_t arguments[] = {reinterpret_cast(c), field}; + + return run(t, getStaticShortField, arguments); +} + +uint64_t getStaticIntField(Thread* t, uintptr_t* arguments) +{ + GcJclass* c = cast(t, *reinterpret_cast(arguments[0])); + + initClass(t, c->vmClass()); + + GcField* field = getStaticField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return fieldAtOffset(c->vmClass()->staticTable(), field->offset()); +} + +jint JNICALL GetStaticIntField(Thread* t, jclass c, jfieldID field) +{ + uintptr_t arguments[] = {reinterpret_cast(c), field}; + + return run(t, getStaticIntField, arguments); +} + +uint64_t getStaticLongField(Thread* t, uintptr_t* arguments) +{ + GcJclass* c = cast(t, *reinterpret_cast(arguments[0])); + + initClass(t, c->vmClass()); + + GcField* field = getStaticField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return fieldAtOffset(c->vmClass()->staticTable(), field->offset()); +} + +jlong JNICALL GetStaticLongField(Thread* t, jclass c, jfieldID field) +{ + uintptr_t arguments[] = {reinterpret_cast(c), field}; + + return run(t, getStaticLongField, arguments); +} + +uint64_t getStaticFloatField(Thread* t, uintptr_t* arguments) +{ + GcJclass* c = cast(t, *reinterpret_cast(arguments[0])); + + initClass(t, c->vmClass()); + + GcField* field = getStaticField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return floatToBits( + fieldAtOffset(c->vmClass()->staticTable(), field->offset())); +} + +jfloat JNICALL GetStaticFloatField(Thread* t, jclass c, jfieldID field) +{ + uintptr_t arguments[] = {reinterpret_cast(c), field}; + + return bitsToFloat(run(t, getStaticFloatField, arguments)); +} + +uint64_t getStaticDoubleField(Thread* t, uintptr_t* arguments) +{ + GcJclass* c = cast(t, *reinterpret_cast(arguments[0])); + + initClass(t, c->vmClass()); + + GcField* field = getStaticField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return doubleToBits( + fieldAtOffset(c->vmClass()->staticTable(), field->offset())); +} + +jdouble JNICALL GetStaticDoubleField(Thread* t, jclass c, jfieldID field) +{ + uintptr_t arguments[] = {reinterpret_cast(c), field}; + + return bitsToDouble(run(t, getStaticDoubleField, arguments)); +} + +uint64_t setStaticObjectField(Thread* t, uintptr_t* arguments) +{ + GcJclass* c = cast(t, *reinterpret_cast(arguments[0])); + + initClass(t, c->vmClass()); + + GcField* field = getStaticField(t, arguments[1]); + jobject v = reinterpret_cast(arguments[2]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + setField(t, c->vmClass()->staticTable(), field->offset(), (v ? *v : 0)); + + return 1; +} + +void JNICALL + SetStaticObjectField(Thread* t, jclass c, jfieldID field, jobject v) +{ + uintptr_t arguments[] + = {reinterpret_cast(c), field, reinterpret_cast(v)}; + + run(t, setStaticObjectField, arguments); +} + +uint64_t setStaticBooleanField(Thread* t, uintptr_t* arguments) +{ + GcJclass* c = cast(t, *reinterpret_cast(arguments[0])); + + initClass(t, c->vmClass()); + + GcField* field = getStaticField(t, arguments[1]); + jboolean v = arguments[2]; + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + fieldAtOffset(c->vmClass()->staticTable(), field->offset()) = v; + + return 1; +} + +void JNICALL + SetStaticBooleanField(Thread* t, jclass c, jfieldID field, jboolean v) +{ + uintptr_t arguments[] = {reinterpret_cast(c), field, v}; + + run(t, setStaticBooleanField, arguments); +} + +uint64_t setStaticByteField(Thread* t, uintptr_t* arguments) +{ + GcJclass* c = cast(t, *reinterpret_cast(arguments[0])); + + initClass(t, c->vmClass()); + + GcField* field = getStaticField(t, arguments[1]); + jbyte v = arguments[2]; + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + fieldAtOffset(c->vmClass()->staticTable(), field->offset()) = v; + + return 1; +} + +void JNICALL SetStaticByteField(Thread* t, jclass c, jfieldID field, jbyte v) +{ + uintptr_t arguments[] + = {reinterpret_cast(c), field, static_cast(v)}; + + run(t, setStaticByteField, arguments); +} + +uint64_t setStaticCharField(Thread* t, uintptr_t* arguments) +{ + GcJclass* c = cast(t, *reinterpret_cast(arguments[0])); + + initClass(t, c->vmClass()); + + GcField* field = getStaticField(t, arguments[1]); + jchar v = arguments[2]; + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + fieldAtOffset(c->vmClass()->staticTable(), field->offset()) = v; + + return 1; +} + +void JNICALL SetStaticCharField(Thread* t, jclass c, jfieldID field, jchar v) +{ + uintptr_t arguments[] = {reinterpret_cast(c), field, v}; + + run(t, setStaticCharField, arguments); +} + +uint64_t setStaticShortField(Thread* t, uintptr_t* arguments) +{ + GcJclass* c = cast(t, *reinterpret_cast(arguments[0])); + + initClass(t, c->vmClass()); + + GcField* field = getStaticField(t, arguments[1]); + jshort v = arguments[2]; + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + fieldAtOffset(c->vmClass()->staticTable(), field->offset()) = v; + + return 1; +} + +void JNICALL SetStaticShortField(Thread* t, jclass c, jfieldID field, jshort v) +{ + uintptr_t arguments[] + = {reinterpret_cast(c), field, static_cast(v)}; + + run(t, setStaticShortField, arguments); +} + +uint64_t setStaticIntField(Thread* t, uintptr_t* arguments) +{ + GcJclass* c = cast(t, *reinterpret_cast(arguments[0])); + + initClass(t, c->vmClass()); + + GcField* field = getStaticField(t, arguments[1]); + jint v = arguments[2]; + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + fieldAtOffset(c->vmClass()->staticTable(), field->offset()) = v; + + return 1; +} + +void JNICALL SetStaticIntField(Thread* t, jclass c, jfieldID field, jint v) +{ + uintptr_t arguments[] + = {reinterpret_cast(c), field, static_cast(v)}; + + run(t, setStaticIntField, arguments); +} + +uint64_t setStaticLongField(Thread* t, uintptr_t* arguments) +{ + GcJclass* c = cast(t, *reinterpret_cast(arguments[0])); + + initClass(t, c->vmClass()); + + GcField* field = getStaticField(t, arguments[1]); + jlong v; + memcpy(&v, arguments + 2, sizeof(jlong)); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + fieldAtOffset(c->vmClass()->staticTable(), field->offset()) = v; + + return 1; +} + +void JNICALL SetStaticLongField(Thread* t, jclass c, jfieldID field, jlong v) +{ + uintptr_t arguments[2 + (sizeof(jlong) / BytesPerWord)]; + arguments[0] = reinterpret_cast(c); + arguments[1] = field; + memcpy(arguments + 2, &v, sizeof(jlong)); + + run(t, setStaticLongField, arguments); +} + +uint64_t setStaticFloatField(Thread* t, uintptr_t* arguments) +{ + GcJclass* c = cast(t, *reinterpret_cast(arguments[0])); + + initClass(t, c->vmClass()); + + GcField* field = getStaticField(t, arguments[1]); + jfloat v = bitsToFloat(arguments[2]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + fieldAtOffset(c->vmClass()->staticTable(), field->offset()) = v; + + return 1; +} + +void JNICALL SetStaticFloatField(Thread* t, jclass c, jfieldID field, jfloat v) +{ + uintptr_t arguments[] + = {reinterpret_cast(c), field, floatToBits(v)}; + + run(t, setStaticFloatField, arguments); +} + +uint64_t setStaticDoubleField(Thread* t, uintptr_t* arguments) +{ + GcJclass* c = cast(t, *reinterpret_cast(arguments[0])); + + initClass(t, c->vmClass()); + + GcField* field = getStaticField(t, arguments[1]); + jdouble v; + memcpy(&v, arguments + 2, sizeof(jdouble)); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + fieldAtOffset(c->vmClass()->staticTable(), field->offset()) = v; + + return 1; +} + +void JNICALL + SetStaticDoubleField(Thread* t, jclass c, jfieldID field, jdouble v) +{ + uintptr_t arguments[2 + (sizeof(jdouble) / BytesPerWord)]; + arguments[0] = reinterpret_cast(c); + arguments[1] = field; + memcpy(arguments + 2, &v, sizeof(jdouble)); + + run(t, setStaticDoubleField, arguments); +} + +jobject JNICALL newGlobalRef(Thread* t, jobject o, bool weak) +{ + ENTER(t, Thread::ActiveState); + + ACQUIRE(t, t->m->referenceLock); + + if (o) { + for (Reference* r = t->m->jniReferences; r; r = r->next) { + if (r->target == *o and r->weak == weak) { + acquire(t, r); + + return &(r->target); + } + } + + Reference* r = new (t->m->heap->allocate(sizeof(Reference))) + Reference(*o, &(t->m->jniReferences), weak); + + acquire(t, r); + + return &(r->target); + } else { + return 0; + } +} + +jobject JNICALL NewGlobalRef(Thread* t, jobject o) +{ + return newGlobalRef(t, o, false); +} + +void JNICALL DeleteGlobalRef(Thread* t, jobject r) +{ + ENTER(t, Thread::ActiveState); + + ACQUIRE(t, t->m->referenceLock); + + if (r) { + release(t, reinterpret_cast(r)); + } +} + +jobject JNICALL NewWeakGlobalRef(Thread* t, jobject o) +{ + return newGlobalRef(t, o, true); +} + +void JNICALL DeleteWeakGlobalRef(Thread* t, jobject r) +{ + DeleteGlobalRef(t, r); +} + +jint JNICALL EnsureLocalCapacity(Thread*, jint) +{ + return 0; +} + +jthrowable JNICALL ExceptionOccurred(Thread* t) +{ + ENTER(t, Thread::ActiveState); + + return reinterpret_cast(makeLocalReference(t, t->exception)); +} + +void JNICALL ExceptionDescribe(Thread* t) +{ + ENTER(t, Thread::ActiveState); + + return printTrace(t, t->exception); +} + +void JNICALL ExceptionClear(Thread* t) +{ + ENTER(t, Thread::ActiveState); + + t->exception = 0; +} + +uint64_t newObjectArray(Thread* t, uintptr_t* arguments) +{ + jsize length = arguments[0]; + jclass class_ = reinterpret_cast(arguments[1]); + jobject init = reinterpret_cast(arguments[2]); + + object a = makeObjectArray(t, (*class_)->vmClass(), length); + object value = (init ? *init : 0); + for (jsize i = 0; i < length; ++i) { + reinterpret_cast(a)->setBodyElement(t, i, value); + } + return reinterpret_cast(makeLocalReference(t, a)); +} + +jobjectArray JNICALL + NewObjectArray(Thread* t, jsize length, jclass class_, jobject init) +{ + uintptr_t arguments[] = {static_cast(length), + reinterpret_cast(class_), + reinterpret_cast(init)}; + + return reinterpret_cast(run(t, newObjectArray, arguments)); +} + +jobject JNICALL + GetObjectArrayElement(Thread* t, jobjectArray array, jsize index) +{ + ENTER(t, Thread::ActiveState); + + return makeLocalReference( + t, objectArrayBody(t, reinterpret_cast(*array), index)); +} + +void JNICALL SetObjectArrayElement(Thread* t, + jobjectArray array, + jsize index, + jobject value) +{ + ENTER(t, Thread::ActiveState); + + setField(t, + reinterpret_cast(*array), + ArrayBody + (index * BytesPerWord), + (value ? *value : 0)); +} + +uint64_t newArray(Thread* t, uintptr_t* arguments) +{ + object (*constructor)(Thread*, unsigned) + = reinterpret_cast(arguments[0]); + + jsize length = arguments[1]; + + return reinterpret_cast( + makeLocalReference(t, constructor(t, length))); +} + +jbooleanArray JNICALL NewBooleanArray(Thread* t, jsize length) +{ + uintptr_t arguments[] + = {reinterpret_cast(voidPointer(makeBooleanArray)), + static_cast(length)}; + + return reinterpret_cast(run(t, newArray, arguments)); +} + +object makeByteArray0(Thread* t, unsigned length) +{ + return makeByteArray(t, length); +} + +jbyteArray JNICALL NewByteArray(Thread* t, jsize length) +{ + uintptr_t arguments[] + = {reinterpret_cast(voidPointer(makeByteArray0)), + static_cast(length)}; + + return reinterpret_cast(run(t, newArray, arguments)); +} + +jcharArray JNICALL NewCharArray(Thread* t, jsize length) +{ + uintptr_t arguments[] + = {reinterpret_cast(voidPointer(makeCharArray)), + static_cast(length)}; + + return reinterpret_cast(run(t, newArray, arguments)); +} + +jshortArray JNICALL NewShortArray(Thread* t, jsize length) +{ + uintptr_t arguments[] + = {reinterpret_cast(voidPointer(makeShortArray)), + static_cast(length)}; + + return reinterpret_cast(run(t, newArray, arguments)); +} + +jintArray JNICALL NewIntArray(Thread* t, jsize length) +{ + uintptr_t arguments[] + = {reinterpret_cast(voidPointer(makeIntArray)), + static_cast(length)}; + + return reinterpret_cast(run(t, newArray, arguments)); +} + +jlongArray JNICALL NewLongArray(Thread* t, jsize length) +{ + uintptr_t arguments[] + = {reinterpret_cast(voidPointer(makeLongArray)), + static_cast(length)}; + + return reinterpret_cast(run(t, newArray, arguments)); +} + +jfloatArray JNICALL NewFloatArray(Thread* t, jsize length) +{ + uintptr_t arguments[] + = {reinterpret_cast(voidPointer(makeFloatArray)), + static_cast(length)}; + + return reinterpret_cast(run(t, newArray, arguments)); +} + +jdoubleArray JNICALL NewDoubleArray(Thread* t, jsize length) +{ + uintptr_t arguments[] + = {reinterpret_cast(voidPointer(makeDoubleArray)), + static_cast(length)}; + + return reinterpret_cast(run(t, newArray, arguments)); +} + +jboolean* JNICALL + GetBooleanArrayElements(Thread* t, jbooleanArray array, jboolean* isCopy) +{ + ENTER(t, Thread::ActiveState); + + unsigned size = (*array)->length() * sizeof(jboolean); + jboolean* p = static_cast(t->m->heap->allocate(size)); + if (size) { + memcpy(p, (*array)->body().begin(), size); + } + + if (isCopy) { + *isCopy = true; + } + + return p; +} + +jbyte* JNICALL + GetByteArrayElements(Thread* t, jbyteArray array, jboolean* isCopy) +{ + ENTER(t, Thread::ActiveState); + + unsigned size = (*array)->length() * sizeof(jbyte); + jbyte* p = static_cast(t->m->heap->allocate(size)); + if (size) { + memcpy(p, (*array)->body().begin(), size); + } + + if (isCopy) { + *isCopy = true; + } + + return p; +} + +jchar* JNICALL + GetCharArrayElements(Thread* t, jcharArray array, jboolean* isCopy) +{ + ENTER(t, Thread::ActiveState); + + unsigned size = (*array)->length() * sizeof(jchar); + jchar* p = static_cast(t->m->heap->allocate(size)); + if (size) { + memcpy(p, (*array)->body().begin(), size); + } + + if (isCopy) { + *isCopy = true; + } + + return p; +} + +jshort* JNICALL + GetShortArrayElements(Thread* t, jshortArray array, jboolean* isCopy) +{ + ENTER(t, Thread::ActiveState); + + unsigned size = (*array)->length() * sizeof(jshort); + jshort* p = static_cast(t->m->heap->allocate(size)); + if (size) { + memcpy(p, (*array)->body().begin(), size); + } + + if (isCopy) { + *isCopy = true; + } + + return p; +} + +jint* JNICALL GetIntArrayElements(Thread* t, jintArray array, jboolean* isCopy) +{ + ENTER(t, Thread::ActiveState); + + unsigned size = (*array)->length() * sizeof(jint); + jint* p = static_cast(t->m->heap->allocate(size)); + if (size) { + memcpy(p, (*array)->body().begin(), size); + } + + if (isCopy) { + *isCopy = true; + } + + return p; +} + +jlong* JNICALL + GetLongArrayElements(Thread* t, jlongArray array, jboolean* isCopy) +{ + ENTER(t, Thread::ActiveState); + + unsigned size = (*array)->length() * sizeof(jlong); + jlong* p = static_cast(t->m->heap->allocate(size)); + if (size) { + memcpy(p, (*array)->body().begin(), size); + } + + if (isCopy) { + *isCopy = true; + } + + return p; +} + +jfloat* JNICALL + GetFloatArrayElements(Thread* t, jfloatArray array, jboolean* isCopy) +{ + ENTER(t, Thread::ActiveState); + + unsigned size = (*array)->length() * sizeof(jfloat); + jfloat* p = static_cast(t->m->heap->allocate(size)); + if (size) { + memcpy(p, (*array)->body().begin(), size); + } + + if (isCopy) { + *isCopy = true; + } + + return p; +} + +jdouble* JNICALL + GetDoubleArrayElements(Thread* t, jdoubleArray array, jboolean* isCopy) +{ + ENTER(t, Thread::ActiveState); + + unsigned size = (*array)->length() * sizeof(jdouble); + jdouble* p = static_cast(t->m->heap->allocate(size)); + if (size) { + memcpy(p, (*array)->body().begin(), size); + } + + if (isCopy) { + *isCopy = true; + } + + return p; +} + +void JNICALL ReleaseBooleanArrayElements(Thread* t, + jbooleanArray array, + jboolean* p, + jint mode) +{ + ENTER(t, Thread::ActiveState); + + unsigned size = (*array)->length() * sizeof(jboolean); + + if (mode == 0 or mode == AVIAN_JNI_COMMIT) { + if (size) { + memcpy((*array)->body().begin(), p, size); + } + } + + if (mode == 0 or mode == AVIAN_JNI_ABORT) { + t->m->heap->free(p, size); + } +} + +void JNICALL + ReleaseByteArrayElements(Thread* t, jbyteArray array, jbyte* p, jint mode) +{ + ENTER(t, Thread::ActiveState); + + unsigned size = (*array)->length() * sizeof(jbyte); + + if (mode == 0 or mode == AVIAN_JNI_COMMIT) { + if (size) { + memcpy((*array)->body().begin(), p, size); + } + } + + if (mode == 0 or mode == AVIAN_JNI_ABORT) { + t->m->heap->free(p, size); + } +} + +void JNICALL + ReleaseCharArrayElements(Thread* t, jcharArray array, jchar* p, jint mode) +{ + ENTER(t, Thread::ActiveState); + + unsigned size = (*array)->length() * sizeof(jchar); + + if (mode == 0 or mode == AVIAN_JNI_COMMIT) { + if (size) { + memcpy((*array)->body().begin(), p, size); + } + } + + if (mode == 0 or mode == AVIAN_JNI_ABORT) { + t->m->heap->free(p, size); + } +} + +void JNICALL ReleaseShortArrayElements(Thread* t, + jshortArray array, + jshort* p, + jint mode) +{ + ENTER(t, Thread::ActiveState); + + unsigned size = (*array)->length() * sizeof(jshort); + + if (mode == 0 or mode == AVIAN_JNI_COMMIT) { + if (size) { + memcpy((*array)->body().begin(), p, size); + } + } + + if (mode == 0 or mode == AVIAN_JNI_ABORT) { + t->m->heap->free(p, size); + } +} + +void JNICALL + ReleaseIntArrayElements(Thread* t, jintArray array, jint* p, jint mode) +{ + ENTER(t, Thread::ActiveState); + + unsigned size = (*array)->length() * sizeof(jint); + + if (mode == 0 or mode == AVIAN_JNI_COMMIT) { + if (size) { + memcpy((*array)->body().begin(), p, size); + } + } + + if (mode == 0 or mode == AVIAN_JNI_ABORT) { + t->m->heap->free(p, size); + } +} + +void JNICALL + ReleaseLongArrayElements(Thread* t, jlongArray array, jlong* p, jint mode) +{ + ENTER(t, Thread::ActiveState); + + unsigned size = (*array)->length() * sizeof(jlong); + + if (mode == 0 or mode == AVIAN_JNI_COMMIT) { + if (size) { + memcpy((*array)->body().begin(), p, size); + } + } + + if (mode == 0 or mode == AVIAN_JNI_ABORT) { + t->m->heap->free(p, size); + } +} + +void JNICALL ReleaseFloatArrayElements(Thread* t, + jfloatArray array, + jfloat* p, + jint mode) +{ + ENTER(t, Thread::ActiveState); + + unsigned size = (*array)->length() * sizeof(jfloat); + + if (mode == 0 or mode == AVIAN_JNI_COMMIT) { + if (size) { + memcpy((*array)->body().begin(), p, size); + } + } + + if (mode == 0 or mode == AVIAN_JNI_ABORT) { + t->m->heap->free(p, size); + } +} + +void JNICALL ReleaseDoubleArrayElements(Thread* t, + jdoubleArray array, + jdouble* p, + jint mode) +{ + ENTER(t, Thread::ActiveState); + + unsigned size = (*array)->length() * sizeof(jdouble); + + if (mode == 0 or mode == AVIAN_JNI_COMMIT) { + if (size) { + memcpy((*array)->body().begin(), p, size); + } + } + + if (mode == 0 or mode == AVIAN_JNI_ABORT) { + t->m->heap->free(p, size); + } +} + +void JNICALL GetBooleanArrayRegion(Thread* t, + jbooleanArray array, + jint offset, + jint length, + jboolean* dst) +{ + ENTER(t, Thread::ActiveState); + + if (length) { + memcpy(dst, &(*array)->body()[offset], length * sizeof(jboolean)); + } +} + +void JNICALL GetByteArrayRegion(Thread* t, + jbyteArray array, + jint offset, + jint length, + jbyte* dst) +{ + ENTER(t, Thread::ActiveState); + + if (length) { + memcpy(dst, &(*array)->body()[offset], length * sizeof(jbyte)); + } +} + +void JNICALL GetCharArrayRegion(Thread* t, + jcharArray array, + jint offset, + jint length, + jchar* dst) +{ + ENTER(t, Thread::ActiveState); + + if (length) { + memcpy(dst, &(*array)->body()[offset], length * sizeof(jchar)); + } +} + +void JNICALL GetShortArrayRegion(Thread* t, + jshortArray array, + jint offset, + jint length, + jshort* dst) +{ + ENTER(t, Thread::ActiveState); + + if (length) { + memcpy(dst, &(*array)->body()[offset], length * sizeof(jshort)); + } +} + +void JNICALL GetIntArrayRegion(Thread* t, + jintArray array, + jint offset, + jint length, + jint* dst) +{ + ENTER(t, Thread::ActiveState); + + if (length) { + memcpy(dst, &(*array)->body()[offset], length * sizeof(jint)); + } +} + +void JNICALL GetLongArrayRegion(Thread* t, + jlongArray array, + jint offset, + jint length, + jlong* dst) +{ + ENTER(t, Thread::ActiveState); + + if (length) { + memcpy(dst, &(*array)->body()[offset], length * sizeof(jlong)); + } +} + +void JNICALL GetFloatArrayRegion(Thread* t, + jfloatArray array, + jint offset, + jint length, + jfloat* dst) +{ + ENTER(t, Thread::ActiveState); + + if (length) { + memcpy(dst, &(*array)->body()[offset], length * sizeof(jfloat)); + } +} + +void JNICALL GetDoubleArrayRegion(Thread* t, + jdoubleArray array, + jint offset, + jint length, + jdouble* dst) +{ + ENTER(t, Thread::ActiveState); + + if (length) { + memcpy(dst, &(*array)->body()[offset], length * sizeof(jdouble)); + } +} + +void JNICALL SetBooleanArrayRegion(Thread* t, + jbooleanArray array, + jint offset, + jint length, + const jboolean* src) +{ + ENTER(t, Thread::ActiveState); + + if (length) { + memcpy(&(*array)->body()[offset], src, length * sizeof(jboolean)); + } +} + +void JNICALL SetByteArrayRegion(Thread* t, + jbyteArray array, + jint offset, + jint length, + const jbyte* src) +{ + ENTER(t, Thread::ActiveState); + + if (length) { + memcpy(&(*array)->body()[offset], src, length * sizeof(jbyte)); + } +} + +void JNICALL SetCharArrayRegion(Thread* t, + jcharArray array, + jint offset, + jint length, + const jchar* src) +{ + ENTER(t, Thread::ActiveState); + + if (length) { + memcpy(&(*array)->body()[offset], src, length * sizeof(jchar)); + } +} + +void JNICALL SetShortArrayRegion(Thread* t, + jshortArray array, + jint offset, + jint length, + const jshort* src) +{ + ENTER(t, Thread::ActiveState); + + if (length) { + memcpy(&(*array)->body()[offset], src, length * sizeof(jshort)); + } +} + +void JNICALL SetIntArrayRegion(Thread* t, + jintArray array, + jint offset, + jint length, + const jint* src) +{ + ENTER(t, Thread::ActiveState); + + if (length) { + memcpy(&(*array)->body()[offset], src, length * sizeof(jint)); + } +} + +void JNICALL SetLongArrayRegion(Thread* t, + jlongArray array, + jint offset, + jint length, + const jlong* src) +{ + ENTER(t, Thread::ActiveState); + + if (length) { + memcpy(&(*array)->body()[offset], src, length * sizeof(jlong)); + } +} + +void JNICALL SetFloatArrayRegion(Thread* t, + jfloatArray array, + jint offset, + jint length, + const jfloat* src) +{ + ENTER(t, Thread::ActiveState); + + if (length) { + memcpy(&(*array)->body()[offset], src, length * sizeof(jfloat)); + } +} + +void JNICALL SetDoubleArrayRegion(Thread* t, + jdoubleArray array, + jint offset, + jint length, + const jdouble* src) +{ + ENTER(t, Thread::ActiveState); + + if (length) { + memcpy(&(*array)->body()[offset], src, length * sizeof(jdouble)); + } +} + +void* JNICALL + GetPrimitiveArrayCritical(Thread* t, jarray array, jboolean* isCopy) +{ + if (t->criticalLevel == 0) { + enter(t, Thread::ActiveState); + } + + ++t->criticalLevel; + + if (isCopy) { + *isCopy = true; + } + + expect(t, *array); + + return reinterpret_cast(*array) + 2; +} + +void JNICALL ReleasePrimitiveArrayCritical(Thread* t, jarray, void*, jint) +{ + if ((--t->criticalLevel) == 0) { + enter(t, Thread::IdleState); + } +} + +uint64_t fromReflectedMethod(Thread* t, uintptr_t* arguments) +{ + jobject m = reinterpret_cast(arguments[0]); + + return methodID(t, t->m->classpath->getVMMethod(t, *m)); +} + +jmethodID JNICALL FromReflectedMethod(Thread* t, jobject method) +{ + uintptr_t arguments[] = {reinterpret_cast(method)}; + + return static_cast(run(t, fromReflectedMethod, arguments)); +} + +uint64_t toReflectedMethod(Thread* t, uintptr_t* arguments) +{ + jmethodID m = arguments[1]; + jboolean isStatic = arguments[2]; + + return reinterpret_cast(makeLocalReference( + t, + t->m->classpath->makeJMethod( + t, isStatic ? getStaticMethod(t, m) : getMethod(t, m)))); +} + +jobject JNICALL + ToReflectedMethod(Thread* t, jclass c, jmethodID method, jboolean isStatic) +{ + uintptr_t arguments[] = {reinterpret_cast(c), + static_cast(method), + static_cast(isStatic)}; + + return reinterpret_cast(run(t, toReflectedMethod, arguments)); +} + +uint64_t fromReflectedField(Thread* t, uintptr_t* arguments) +{ + jobject f = reinterpret_cast(arguments[0]); + + return fieldID(t, t->m->classpath->getVMField(t, cast(t, *f))); +} + +jfieldID JNICALL FromReflectedField(Thread* t, jobject field) +{ + uintptr_t arguments[] = {reinterpret_cast(field)}; + + return static_cast(run(t, fromReflectedField, arguments)); +} + +uint64_t toReflectedField(Thread* t, uintptr_t* arguments) +{ + jfieldID f = arguments[1]; + jboolean isStatic = arguments[2]; + + return reinterpret_cast(makeLocalReference( + t, + t->m->classpath->makeJField( + t, isStatic ? getStaticField(t, f) : getField(t, f)))); +} + +jobject JNICALL + ToReflectedField(Thread* t, jclass c, jfieldID field, jboolean isStatic) +{ + uintptr_t arguments[] = {reinterpret_cast(c), + static_cast(field), + static_cast(isStatic)}; + + return reinterpret_cast(run(t, toReflectedField, arguments)); +} + +uint64_t registerNatives(Thread* t, uintptr_t* arguments) +{ + jclass c = reinterpret_cast(arguments[0]); + const JNINativeMethod* methods + = reinterpret_cast(arguments[1]); + jint methodCount = arguments[2]; + + for (int i = 0; i < methodCount; ++i) { + if (methods[i].function) { + // Android's class library sometimes prepends a mysterious "!" + // to the method signature, which we happily ignore: + const char* sig = methods[i].signature; + if (*sig == '!') + ++sig; + + GcMethod* method + = findMethodOrNull(t, (*c)->vmClass(), methods[i].name, sig); + + if (method == 0 or (method->flags() & ACC_NATIVE) == 0) { + // The JNI spec says we must throw a NoSuchMethodError in this + // case, but that would prevent using a code shrinker like + // ProGuard effectively. Instead, we just ignore it. + + if (false) { + fprintf(stderr, + "not found: %s.%s%s\n", + (*c)->vmClass()->name()->body().begin(), + methods[i].name, + sig); + abort(t); + } + } else { + registerNative(t, method, methods[i].function); + } + } + } + + return 1; +} + +jint JNICALL RegisterNatives(Thread* t, + jclass c, + const JNINativeMethod* methods, + jint methodCount) +{ + uintptr_t arguments[] = {reinterpret_cast(c), + reinterpret_cast(methods), + static_cast(methodCount)}; + + return run(t, registerNatives, arguments) ? 0 : -1; +} + +jint JNICALL UnregisterNatives(Thread* t, jclass c) +{ + ENTER(t, Thread::ActiveState); + + unregisterNatives(t, (*c)->vmClass()); + + return 0; +} + +uint64_t monitorOp(Thread* t, uintptr_t* arguments) +{ + void (*op)(Thread*, object) + = reinterpret_cast(arguments[0]); + + jobject o = reinterpret_cast(arguments[1]); + + op(t, *o); + + return 1; +} + +void acquire0(Thread* t, object o) +{ + return acquire(t, o); +} + +jint JNICALL MonitorEnter(Thread* t, jobject o) +{ + uintptr_t arguments[] = {reinterpret_cast(voidPointer(acquire0)), + reinterpret_cast(o)}; + + return run(t, monitorOp, arguments) ? 0 : -1; +} + +void release0(Thread* t, object o) +{ + return release(t, o); +} + +jint JNICALL MonitorExit(Thread* t, jobject o) +{ + uintptr_t arguments[] = {reinterpret_cast(voidPointer(release0)), + reinterpret_cast(o)}; + + return run(t, monitorOp, arguments) ? 0 : -1; +} + +jint JNICALL GetJavaVM(Thread* t, Machine** m) +{ + *m = t->m; + return 0; +} + +jboolean JNICALL IsSameObject(Thread* t, jobject a, jobject b) +{ + if (a and b) { + ENTER(t, Thread::ActiveState); + + return *a == *b; + } else { + return a == b; + } +} + +uint64_t pushLocalFrame(Thread* t, uintptr_t* arguments) +{ + if (t->m->processor->pushLocalFrame(t, arguments[0])) { + return 1; + } else { + throw_(t, roots(t)->outOfMemoryError()); + } +} + +jint JNICALL PushLocalFrame(Thread* t, jint capacity) +{ + uintptr_t arguments[] = {static_cast(capacity)}; + + return run(t, pushLocalFrame, arguments) ? 0 : -1; +} + +uint64_t popLocalFrame(Thread* t, uintptr_t* arguments) +{ + uint64_t r; + jobject presult = reinterpret_cast(arguments[0]); + if (presult != NULL) { + object result = *presult; + PROTECT(t, result); + + t->m->processor->popLocalFrame(t); + + r = reinterpret_cast(makeLocalReference(t, result)); + } else { + t->m->processor->popLocalFrame(t); + r = 0; + } + + return r; +} + +jobject JNICALL PopLocalFrame(Thread* t, jobject result) +{ + uintptr_t arguments[] = {reinterpret_cast(result)}; + + return reinterpret_cast(run(t, popLocalFrame, arguments)); +} + +uint64_t newDirectByteBuffer(Thread* t, uintptr_t* arguments) +{ + jlong capacity; + memcpy(&capacity, arguments + 1, sizeof(jlong)); + + return reinterpret_cast(makeLocalReference( + t, + t->m->classpath->makeDirectByteBuffer( + t, reinterpret_cast(arguments[0]), capacity))); +} + +jobject JNICALL NewDirectByteBuffer(Thread* t, void* p, jlong capacity) +{ + uintptr_t arguments[1 + (sizeof(jlong) / BytesPerWord)]; + arguments[0] = reinterpret_cast(p); + memcpy(arguments + 1, &capacity, sizeof(jlong)); + + return reinterpret_cast(run(t, newDirectByteBuffer, arguments)); +} + +uint64_t getDirectBufferAddress(Thread* t, uintptr_t* arguments) +{ + return reinterpret_cast(t->m->classpath->getDirectBufferAddress( + t, *reinterpret_cast(arguments[0]))); +} + +void* JNICALL GetDirectBufferAddress(Thread* t, jobject b) +{ + uintptr_t arguments[] = {reinterpret_cast(b)}; + + return reinterpret_cast(run(t, getDirectBufferAddress, arguments)); +} + +uint64_t getDirectBufferCapacity(Thread* t, uintptr_t* arguments) +{ + return t->m->classpath->getDirectBufferCapacity( + t, *reinterpret_cast(arguments[0])); +} + +jlong JNICALL GetDirectBufferCapacity(Thread* t, jobject b) +{ + uintptr_t arguments[] = {reinterpret_cast(b)}; + + return run(t, getDirectBufferCapacity, arguments); +} + +struct JavaVMOption { + char* optionString; + void* extraInfo; +}; + +struct JavaVMInitArgs { + jint version; + + jint nOptions; + JavaVMOption* options; + jboolean ignoreUnrecognized; +}; + +int parseSize(const char* s) +{ + unsigned length = strlen(s); + RUNTIME_ARRAY(char, buffer, length + 1); + + if (length == 0) + return 0; + + char suffix = s[length - 1]; + if (suffix== 'k' or suffix == 'K') { + memcpy(RUNTIME_ARRAY_BODY(buffer), s, length - 1); + RUNTIME_ARRAY_BODY(buffer)[length - 1] = 0; + return atoi(RUNTIME_ARRAY_BODY(buffer)) * 1024; + } + + if (suffix == 'm' or suffix == 'M') { + memcpy(RUNTIME_ARRAY_BODY(buffer), s, length - 1); + RUNTIME_ARRAY_BODY(buffer)[length - 1] = 0; + return atoi(RUNTIME_ARRAY_BODY(buffer)) * 1024 * 1024; + } + + if (suffix == 'g' or suffix == 'G') { + memcpy(RUNTIME_ARRAY_BODY(buffer), s, length - 1); + RUNTIME_ARRAY_BODY(buffer)[length - 1] = 0; + return atoi(RUNTIME_ARRAY_BODY(buffer)) * 1024 * 1024 * 1024; + } + + return atoi(s); +} + +void append(char** p, const char* value, unsigned length, char tail) +{ + if (length) { + memcpy(*p, value, length); + *p += length; + *((*p)++) = tail; + } +} + +uint64_t boot(Thread* t, uintptr_t*) +{ + GcThrowable* throwable = makeThrowable(t, GcNullPointerException::Type); + // sequence point, for gc (don't recombine statements) + roots(t)->setNullPointerException(t, throwable); + + throwable = makeThrowable(t, GcArithmeticException::Type); + // sequence point, for gc (don't recombine statements) + roots(t)->setArithmeticException(t, throwable); + + throwable = makeThrowable(t, GcArrayIndexOutOfBoundsException::Type); + // sequence point, for gc (don't recombine statements) + roots(t)->setArrayIndexOutOfBoundsException(t, throwable); + + throwable = makeThrowable(t, GcOutOfMemoryError::Type); + // sequence point, for gc (don't recombine statements) + roots(t)->setOutOfMemoryError(t, throwable); + + throwable = makeThrowable(t, GcThrowable::Type); + // sequence point, for gc (don't recombine statements) + roots(t)->setShutdownInProgress(t, throwable); + + t->m->classpath->preBoot(t); + + t->javaThread = t->m->classpath->makeThread(t, 0); + + t->javaThread->peer() = reinterpret_cast(t); + +#ifndef SGX + GcThread* jthread = t->m->classpath->makeThread(t, t); + // sequence point, for gc (don't recombine statements) + roots(t)->setFinalizerThread(t, jthread); + roots(t)->finalizerThread()->daemon() = true; +#endif + + t->m->classpath->boot(t); + + const char* port = findProperty(t, "avian.trace.port"); + if (port) { + GcString* host = makeString(t, "0.0.0.0"); + PROTECT(t, host); + + GcMethod* method = resolveMethod(t, + roots(t)->bootLoader(), + "avian/Traces", + "startTraceListener", + "(Ljava/lang/String;I)V"); + + t->m->processor->invoke(t, method, 0, host, atoi(port)); + } + + enter(t, Thread::IdleState); + + return 1; +} + +} // namespace local + +} // namespace + +namespace vm { + +void populateJNITables(JavaVMVTable* vmTable, JNIEnvVTable* envTable) +{ + memset(vmTable, 0, sizeof(JavaVMVTable)); + + vmTable->DestroyJavaVM = local::DestroyJavaVM; + vmTable->AttachCurrentThread = local::AttachCurrentThread; + vmTable->AttachCurrentThreadAsDaemon = local::AttachCurrentThreadAsDaemon; + vmTable->DetachCurrentThread = local::DetachCurrentThread; + vmTable->GetEnv = local::GetEnv; + + memset(envTable, 0, sizeof(JNIEnvVTable)); + + envTable->GetVersion = local::GetVersion; + envTable->GetStringLength = local::GetStringLength; + envTable->GetStringChars = local::GetStringChars; + envTable->ReleaseStringChars = local::ReleaseStringChars; + envTable->GetStringRegion = local::GetStringRegion; + envTable->GetStringCritical = local::GetStringCritical; + envTable->ReleaseStringCritical = local::ReleaseStringCritical; + envTable->GetStringUTFLength = local::GetStringUTFLength; + envTable->GetStringUTFChars = local::GetStringUTFChars; + envTable->ReleaseStringUTFChars = local::ReleaseStringUTFChars; + envTable->GetStringUTFRegion = local::GetStringUTFRegion; + envTable->GetArrayLength = local::GetArrayLength; + envTable->NewString = local::NewString; + envTable->NewStringUTF = local::NewStringUTF; + envTable->DefineClass = local::DefineClass; + envTable->FindClass = local::FindClass; + envTable->ThrowNew = local::ThrowNew; + envTable->Throw = local::Throw; + envTable->ExceptionCheck = local::ExceptionCheck; + envTable->NewDirectByteBuffer = local::NewDirectByteBuffer; + envTable->GetDirectBufferAddress = local::GetDirectBufferAddress; + envTable->GetDirectBufferCapacity = local::GetDirectBufferCapacity; + envTable->NewLocalRef = local::NewLocalRef; + envTable->DeleteLocalRef = local::DeleteLocalRef; + envTable->GetObjectClass = local::GetObjectClass; + envTable->GetSuperclass = local::GetSuperclass; + envTable->IsInstanceOf = local::IsInstanceOf; + envTable->IsAssignableFrom = local::IsAssignableFrom; + envTable->GetFieldID = local::GetFieldID; + envTable->GetMethodID = local::GetMethodID; + envTable->GetStaticMethodID = local::GetStaticMethodID; + envTable->NewObjectV = local::NewObjectV; + envTable->NewObjectA = local::NewObjectA; + envTable->NewObject = local::NewObject; + envTable->CallObjectMethodV = local::CallObjectMethodV; + envTable->CallObjectMethodA = local::CallObjectMethodA; + envTable->CallObjectMethod = local::CallObjectMethod; + envTable->CallBooleanMethodV = local::CallBooleanMethodV; + envTable->CallBooleanMethodA = local::CallBooleanMethodA; + envTable->CallBooleanMethod = local::CallBooleanMethod; + envTable->CallByteMethodV = local::CallByteMethodV; + envTable->CallByteMethodA = local::CallByteMethodA; + envTable->CallByteMethod = local::CallByteMethod; + envTable->CallCharMethodV = local::CallCharMethodV; + envTable->CallCharMethodA = local::CallCharMethodA; + envTable->CallCharMethod = local::CallCharMethod; + envTable->CallShortMethodV = local::CallShortMethodV; + envTable->CallShortMethodA = local::CallShortMethodA; + envTable->CallShortMethod = local::CallShortMethod; + envTable->CallIntMethodV = local::CallIntMethodV; + envTable->CallIntMethodA = local::CallIntMethodA; + envTable->CallIntMethod = local::CallIntMethod; + envTable->CallLongMethodV = local::CallLongMethodV; + envTable->CallLongMethodA = local::CallLongMethodA; + envTable->CallLongMethod = local::CallLongMethod; + envTable->CallFloatMethodV = local::CallFloatMethodV; + envTable->CallFloatMethodA = local::CallFloatMethodA; + envTable->CallFloatMethod = local::CallFloatMethod; + envTable->CallDoubleMethodV = local::CallDoubleMethodV; + envTable->CallDoubleMethodA = local::CallDoubleMethodA; + envTable->CallDoubleMethod = local::CallDoubleMethod; + envTable->CallVoidMethodV = local::CallVoidMethodV; + envTable->CallVoidMethodA = local::CallVoidMethodA; + envTable->CallVoidMethod = local::CallVoidMethod; + envTable->CallStaticObjectMethodV = local::CallStaticObjectMethodV; + envTable->CallStaticObjectMethodA = local::CallStaticObjectMethodA; + envTable->CallStaticObjectMethod = local::CallStaticObjectMethod; + envTable->CallStaticBooleanMethodV = local::CallStaticBooleanMethodV; + envTable->CallStaticBooleanMethodA = local::CallStaticBooleanMethodA; + envTable->CallStaticBooleanMethod = local::CallStaticBooleanMethod; + envTable->CallStaticByteMethodV = local::CallStaticByteMethodV; + envTable->CallStaticByteMethodA = local::CallStaticByteMethodA; + envTable->CallStaticByteMethod = local::CallStaticByteMethod; + envTable->CallStaticCharMethodV = local::CallStaticCharMethodV; + envTable->CallStaticCharMethodA = local::CallStaticCharMethodA; + envTable->CallStaticCharMethod = local::CallStaticCharMethod; + envTable->CallStaticShortMethodV = local::CallStaticShortMethodV; + envTable->CallStaticShortMethodA = local::CallStaticShortMethodA; + envTable->CallStaticShortMethod = local::CallStaticShortMethod; + envTable->CallStaticIntMethodV = local::CallStaticIntMethodV; + envTable->CallStaticIntMethodA = local::CallStaticIntMethodA; + envTable->CallStaticIntMethod = local::CallStaticIntMethod; + envTable->CallStaticLongMethodV = local::CallStaticLongMethodV; + envTable->CallStaticLongMethodA = local::CallStaticLongMethodA; + envTable->CallStaticLongMethod = local::CallStaticLongMethod; + envTable->CallStaticFloatMethodV = local::CallStaticFloatMethodV; + envTable->CallStaticFloatMethodA = local::CallStaticFloatMethodA; + envTable->CallStaticFloatMethod = local::CallStaticFloatMethod; + envTable->CallStaticDoubleMethodV = local::CallStaticDoubleMethodV; + envTable->CallStaticDoubleMethodA = local::CallStaticDoubleMethodA; + envTable->CallStaticDoubleMethod = local::CallStaticDoubleMethod; + envTable->CallStaticVoidMethodV = local::CallStaticVoidMethodV; + envTable->CallStaticVoidMethodA = local::CallStaticVoidMethodA; + envTable->CallStaticVoidMethod = local::CallStaticVoidMethod; + envTable->GetStaticFieldID = local::GetStaticFieldID; + envTable->GetObjectField = local::GetObjectField; + envTable->GetBooleanField = local::GetBooleanField; + envTable->GetByteField = local::GetByteField; + envTable->GetCharField = local::GetCharField; + envTable->GetShortField = local::GetShortField; + envTable->GetIntField = local::GetIntField; + envTable->GetLongField = local::GetLongField; + envTable->GetFloatField = local::GetFloatField; + envTable->GetDoubleField = local::GetDoubleField; + envTable->SetObjectField = local::SetObjectField; + envTable->SetBooleanField = local::SetBooleanField; + envTable->SetByteField = local::SetByteField; + envTable->SetCharField = local::SetCharField; + envTable->SetShortField = local::SetShortField; + envTable->SetIntField = local::SetIntField; + envTable->SetLongField = local::SetLongField; + envTable->SetFloatField = local::SetFloatField; + envTable->SetDoubleField = local::SetDoubleField; + envTable->GetStaticObjectField = local::GetStaticObjectField; + envTable->GetStaticBooleanField = local::GetStaticBooleanField; + envTable->GetStaticByteField = local::GetStaticByteField; + envTable->GetStaticCharField = local::GetStaticCharField; + envTable->GetStaticShortField = local::GetStaticShortField; + envTable->GetStaticIntField = local::GetStaticIntField; + envTable->GetStaticLongField = local::GetStaticLongField; + envTable->GetStaticFloatField = local::GetStaticFloatField; + envTable->GetStaticDoubleField = local::GetStaticDoubleField; + envTable->SetStaticObjectField = local::SetStaticObjectField; + envTable->SetStaticBooleanField = local::SetStaticBooleanField; + envTable->SetStaticByteField = local::SetStaticByteField; + envTable->SetStaticCharField = local::SetStaticCharField; + envTable->SetStaticShortField = local::SetStaticShortField; + envTable->SetStaticIntField = local::SetStaticIntField; + envTable->SetStaticLongField = local::SetStaticLongField; + envTable->SetStaticFloatField = local::SetStaticFloatField; + envTable->SetStaticDoubleField = local::SetStaticDoubleField; + envTable->NewGlobalRef = local::NewGlobalRef; + envTable->NewWeakGlobalRef = local::NewWeakGlobalRef; + envTable->DeleteGlobalRef = local::DeleteGlobalRef; + envTable->DeleteWeakGlobalRef = local::DeleteWeakGlobalRef; + envTable->EnsureLocalCapacity = local::EnsureLocalCapacity; + envTable->ExceptionOccurred = local::ExceptionOccurred; + envTable->ExceptionDescribe = local::ExceptionDescribe; + envTable->ExceptionClear = local::ExceptionClear; + envTable->NewObjectArray = local::NewObjectArray; + envTable->GetObjectArrayElement = local::GetObjectArrayElement; + envTable->SetObjectArrayElement = local::SetObjectArrayElement; + envTable->NewBooleanArray = local::NewBooleanArray; + envTable->NewByteArray = local::NewByteArray; + envTable->NewCharArray = local::NewCharArray; + envTable->NewShortArray = local::NewShortArray; + envTable->NewIntArray = local::NewIntArray; + envTable->NewLongArray = local::NewLongArray; + envTable->NewFloatArray = local::NewFloatArray; + envTable->NewDoubleArray = local::NewDoubleArray; + envTable->GetBooleanArrayElements = local::GetBooleanArrayElements; + envTable->GetByteArrayElements = local::GetByteArrayElements; + envTable->GetCharArrayElements = local::GetCharArrayElements; + envTable->GetShortArrayElements = local::GetShortArrayElements; + envTable->GetIntArrayElements = local::GetIntArrayElements; + envTable->GetLongArrayElements = local::GetLongArrayElements; + envTable->GetFloatArrayElements = local::GetFloatArrayElements; + envTable->GetDoubleArrayElements = local::GetDoubleArrayElements; + envTable->ReleaseBooleanArrayElements = local::ReleaseBooleanArrayElements; + envTable->ReleaseByteArrayElements = local::ReleaseByteArrayElements; + envTable->ReleaseCharArrayElements = local::ReleaseCharArrayElements; + envTable->ReleaseShortArrayElements = local::ReleaseShortArrayElements; + envTable->ReleaseIntArrayElements = local::ReleaseIntArrayElements; + envTable->ReleaseLongArrayElements = local::ReleaseLongArrayElements; + envTable->ReleaseFloatArrayElements = local::ReleaseFloatArrayElements; + envTable->ReleaseDoubleArrayElements = local::ReleaseDoubleArrayElements; + envTable->GetBooleanArrayRegion = local::GetBooleanArrayRegion; + envTable->GetByteArrayRegion = local::GetByteArrayRegion; + envTable->GetCharArrayRegion = local::GetCharArrayRegion; + envTable->GetShortArrayRegion = local::GetShortArrayRegion; + envTable->GetIntArrayRegion = local::GetIntArrayRegion; + envTable->GetLongArrayRegion = local::GetLongArrayRegion; + envTable->GetFloatArrayRegion = local::GetFloatArrayRegion; + envTable->GetDoubleArrayRegion = local::GetDoubleArrayRegion; + envTable->SetBooleanArrayRegion = local::SetBooleanArrayRegion; + envTable->SetByteArrayRegion = local::SetByteArrayRegion; + envTable->SetCharArrayRegion = local::SetCharArrayRegion; + envTable->SetShortArrayRegion = local::SetShortArrayRegion; + envTable->SetIntArrayRegion = local::SetIntArrayRegion; + envTable->SetLongArrayRegion = local::SetLongArrayRegion; + envTable->SetFloatArrayRegion = local::SetFloatArrayRegion; + envTable->SetDoubleArrayRegion = local::SetDoubleArrayRegion; + envTable->GetPrimitiveArrayCritical = local::GetPrimitiveArrayCritical; + envTable->ReleasePrimitiveArrayCritical + = local::ReleasePrimitiveArrayCritical; + envTable->RegisterNatives = local::RegisterNatives; + envTable->UnregisterNatives = local::UnregisterNatives; + envTable->MonitorEnter = local::MonitorEnter; + envTable->MonitorExit = local::MonitorExit; + envTable->GetJavaVM = local::GetJavaVM; + envTable->IsSameObject = local::IsSameObject; + envTable->PushLocalFrame = local::PushLocalFrame; + envTable->PopLocalFrame = local::PopLocalFrame; + envTable->FromReflectedMethod = local::FromReflectedMethod; + envTable->ToReflectedMethod = local::ToReflectedMethod; + envTable->FromReflectedField = local::FromReflectedField; + envTable->ToReflectedField = local::ToReflectedField; +} + +} // namespace vm + +extern "C" AVIAN_EXPORT jint JNICALL JNI_GetDefaultJavaVMInitArgs(void*) +{ + return 0; +} + +extern "C" AVIAN_EXPORT jint JNICALL + JNI_GetCreatedJavaVMs(Machine**, jsize, jsize*) +{ + // todo + return -1; +} + +extern "C" AVIAN_EXPORT jint JNICALL + JNI_CreateJavaVM(Machine** m, Thread** t, void* args) +{ + local::JavaVMInitArgs* a = static_cast(args); + + unsigned heapLimit = 0; + unsigned stackLimit = 0; + 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; + const char* bootClasspathAppend = ""; + const char* crashDumpDirectory = 0; + + unsigned propertyCount = 0; + + for (int i = 0; i < a->nOptions; ++i) { + if (strncmp(a->options[i].optionString, "-X", 2) == 0) { + const char* p = a->options[i].optionString + 2; + if (strncmp(p, "mx", 2) == 0) { + heapLimit = local::parseSize(p + 2); + } else if (strncmp(p, "ss", 2) == 0) { + stackLimit = local::parseSize(p + 2); + } else if (strncmp(p, + BOOTCLASSPATH_PREPEND_OPTION ":", + sizeof(BOOTCLASSPATH_PREPEND_OPTION)) == 0) { + bootClasspathPrepend = p + sizeof(BOOTCLASSPATH_PREPEND_OPTION); + } else if (strncmp( + p, BOOTCLASSPATH_OPTION ":", sizeof(BOOTCLASSPATH_OPTION)) + == 0) { + bootClasspath = p + sizeof(BOOTCLASSPATH_OPTION); + } else if (strncmp(p, + BOOTCLASSPATH_APPEND_OPTION ":", + sizeof(BOOTCLASSPATH_APPEND_OPTION)) == 0) { + bootClasspathAppend = p + sizeof(BOOTCLASSPATH_APPEND_OPTION); + } + } else if (strncmp(a->options[i].optionString, "-D", 2) == 0) { + const char* p = a->options[i].optionString + 2; + if (strncmp(p, BOOTSTRAP_PROPERTY "=", sizeof(BOOTSTRAP_PROPERTY)) == 0) { + bootLibraries = p + sizeof(BOOTSTRAP_PROPERTY); + } else if (strncmp(p, + JAVA_COMMAND_PROPERTY "=", + sizeof(JAVA_COMMAND_PROPERTY)) == 0 + or strncmp(p, + JAVA_LAUNCHER_PROPERTY "=", + sizeof(JAVA_LAUNCHER_PROPERTY)) == 0) { + // this means we're being invoked via the javac or java + // command, so the bootstrap library should be e.g. libjvm.so + bootLibraries = SO_PREFIX "jvm" SO_SUFFIX; + } else if (strncmp(p, CRASHDIR_PROPERTY "=", sizeof(CRASHDIR_PROPERTY)) + == 0) { + crashDumpDirectory = p + sizeof(CRASHDIR_PROPERTY); + } else if (strncmp(p, CLASSPATH_PROPERTY "=", sizeof(CLASSPATH_PROPERTY)) + == 0) { + classpath = p + sizeof(CLASSPATH_PROPERTY); + } 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) { + embedPrefix = p + sizeof(EMBED_PREFIX_PROPERTY); + } + + ++propertyCount; + } + } + + if (heapLimit == 0) + heapLimit = 128 * 1024 * 1024; + + if (stackLimit == 0) + stackLimit = 128 * 1024; + + bool addClasspathProperty = classpath == 0; + if (addClasspathProperty) { + classpath = "."; + ++propertyCount; + } + + System* s = makeSystem(reentrant); + Heap* h = makeHeap(s, heapLimit); + Classpath* c = makeClasspath(s, h, javaHome, embedPrefix); + + if (bootClasspath == 0) { + bootClasspath = c->bootClasspath(); + } + + unsigned bcppl = strlen(bootClasspathPrepend); + unsigned bcpl = strlen(bootClasspath); + unsigned bcpal = strlen(bootClasspathAppend); + + unsigned bootClasspathBufferSize = bcppl + bcpl + bcpal + 3; + RUNTIME_ARRAY(char, bootClasspathBuffer, bootClasspathBufferSize); + char* bootClasspathPointer = RUNTIME_ARRAY_BODY(bootClasspathBuffer); + if (bootClasspathBufferSize > 3) { + local::append(&bootClasspathPointer, + bootClasspathPrepend, + bcppl, + bcpl + bcpal ? PATH_SEPARATOR : 0); + local::append( + &bootClasspathPointer, bootClasspath, bcpl, bcpal ? PATH_SEPARATOR : 0); + local::append(&bootClasspathPointer, bootClasspathAppend, bcpal, 0); + } else { + *RUNTIME_ARRAY_BODY(bootClasspathBuffer) = 0; + } + + char* bootLibrary = bootLibraries ? strdup(bootLibraries) : 0; + char* bootLibraryEnd = bootLibrary ? strchr(bootLibrary, PATH_SEPARATOR) : 0; + if (bootLibraryEnd) + *bootLibraryEnd = 0; + + Finder* bf + = makeFinder(s, h, RUNTIME_ARRAY_BODY(bootClasspathBuffer), bootLibrary); + Finder* af = makeFinder(s, h, classpath, bootLibrary); + if (bootLibrary) + free(bootLibrary); + Processor* p = makeProcessor(s, h, crashDumpDirectory, true); + + // reserve space for avian.version and file.encoding: + propertyCount += 2; + + const char** properties = static_cast( + h->allocate(sizeof(const char*) * propertyCount)); + + const char** propertyPointer = properties; + + const char** arguments = static_cast( + h->allocate(sizeof(const char*) * a->nOptions)); + + const char** argumentPointer = arguments; + + for (int i = 0; i < a->nOptions; ++i) { + if (strncmp(a->options[i].optionString, "-D", 2) == 0) { + *(propertyPointer++) = a->options[i].optionString + 2; + } + *(argumentPointer++) = a->options[i].optionString; + } + + unsigned cpl = strlen(classpath); + RUNTIME_ARRAY(char, classpathProperty, cpl + strlen(CLASSPATH_PROPERTY) + 2); + if (addClasspathProperty) { + char* p = RUNTIME_ARRAY_BODY(classpathProperty); + local::append(&p, CLASSPATH_PROPERTY, strlen(CLASSPATH_PROPERTY), '='); + local::append(&p, classpath, cpl, 0); + *(propertyPointer++) = RUNTIME_ARRAY_BODY(classpathProperty); + } + + *(propertyPointer++) = "avian.version=" AVIAN_VERSION; + + // todo: should this be derived from the OS locale? Should it be + // overrideable via JavaVMInitArgs? + *(propertyPointer++) = "file.encoding=UTF-8"; + + *m = new (h->allocate(sizeof(Machine))) Machine(s, + h, + bf, + af, + p, + c, + properties, + propertyCount, + arguments, + a->nOptions, + stackLimit); + + h->free(properties, sizeof(const char*) * propertyCount); + + *t = p->makeThread(*m, 0, 0); + + enter(*t, Thread::ActiveState); + enter(*t, Thread::IdleState); + + return run(*t, local::boot, 0) ? 0 : -1; +} + +extern "C" AVIAN_EXPORT jstring JNICALL JVM_GetTemporaryDirectory(JNIEnv* e UNUSED) +{ + // Unimplemented + // This is used in newer builds of openjdk8, as a place to store statistics or something... + abort(); +} + +extern "C" AVIAN_EXPORT jboolean JNICALL JVM_KnownToNotExist(JNIEnv* e UNUSED, jobject loader UNUSED, jstring classname UNUSED) +{ + // Unimplemented + abort(); +} + +extern "C" AVIAN_EXPORT jintArray JNICALL JVM_GetResourceLookupCache(JNIEnv* e UNUSED, jobject loader UNUSED, jstring resourcename UNUSED) +{ + // Unimplemented + abort(); +} diff --git a/sgx-jvm/avian/src/lzma-decode.cpp b/sgx-jvm/avian/src/lzma-decode.cpp new file mode 100644 index 0000000000..e94253e340 --- /dev/null +++ b/sgx-jvm/avian/src/lzma-decode.cpp @@ -0,0 +1,66 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/lzma-util.h" +#include "C/LzmaDec.h" + +using namespace vm; + +namespace { + +int32_t read4(const uint8_t* in) +{ + return (static_cast(in[3]) << 24) + | (static_cast(in[2]) << 16) + | (static_cast(in[1]) << 8) | (static_cast(in[0])); +} + +} // namespace + +namespace vm { + +uint8_t* decodeLZMA(System* s, + avian::util::Alloc* a, + uint8_t* in, + size_t inSize, + size_t* outSize) +{ + const size_t PropHeaderSize = 5; + const size_t HeaderSize = 13; + + int32_t outSize32 = read4(in + PropHeaderSize); + expect(s, outSize32 >= 0); + SizeT outSizeT = outSize32; + + uint8_t* out = static_cast(a->allocate(outSize32)); + + SizeT inSizeT = inSize; + LzmaAllocator allocator(a); + + ELzmaStatus status; + int result = LzmaDecode(out, + &outSizeT, + in + HeaderSize, + &inSizeT, + in, + PropHeaderSize, + LZMA_FINISH_END, + &status, + &(allocator.allocator)); + + expect(s, result == SZ_OK); + expect(s, status == LZMA_STATUS_FINISHED_WITH_MARK); + + *outSize = outSize32; + + return out; +} + +} // namespace vm diff --git a/sgx-jvm/avian/src/lzma-encode.cpp b/sgx-jvm/avian/src/lzma-encode.cpp new file mode 100644 index 0000000000..b15ebe54eb --- /dev/null +++ b/sgx-jvm/avian/src/lzma-encode.cpp @@ -0,0 +1,79 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/lzma-util.h" +#include "C/LzmaEnc.h" + +using namespace vm; + +namespace { + +SRes myProgress(void*, UInt64, UInt64) +{ + return SZ_OK; +} + +} // namespace + +namespace vm { + +uint8_t* encodeLZMA(System* s, + avian::util::Alloc* a, + uint8_t* in, + size_t inSize, + size_t* outSize) +{ + const unsigned PropHeaderSize = 5; + const unsigned HeaderSize = 13; + + unsigned bufferSize = inSize * 2; + + uint8_t* buffer = static_cast(a->allocate(bufferSize)); + + LzmaAllocator allocator(a); + + CLzmaEncProps props; + LzmaEncProps_Init(&props); + props.level = 9; + props.writeEndMark = 1; + + ICompressProgress progress = {myProgress}; + + SizeT propsSize = PropHeaderSize; + + int32_t inSize32 = inSize; + memcpy(buffer + PropHeaderSize, &inSize32, 4); + + SizeT outSizeT = bufferSize; + int result = LzmaEncode(buffer + HeaderSize, + &outSizeT, + in, + inSize, + &props, + buffer, + &propsSize, + 1, + &progress, + &(allocator.allocator), + &(allocator.allocator)); + + expect(s, result == SZ_OK); + + *outSize = outSizeT + HeaderSize; + + uint8_t* out = static_cast(a->allocate(*outSize)); + memcpy(out, buffer, *outSize); + + a->free(buffer, bufferSize); + + return out; +} + +} // namespace vm diff --git a/sgx-jvm/avian/src/lzma/load.cpp b/sgx-jvm/avian/src/lzma/load.cpp new file mode 100644 index 0000000000..3f177e6b39 --- /dev/null +++ b/sgx-jvm/avian/src/lzma/load.cpp @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "C/LzmaDec.h" + +#if (defined __MINGW32__) || (defined _MSC_VER) +#define EXPORT __declspec(dllexport) +#include +#define open _open +#define write _write +#define close _close +#ifdef _MSC_VER +#define S_IRWXU (_S_IREAD | _S_IWRITE) +#define and && +#endif +#else +#define EXPORT __attribute__((visibility("default"))) +#include +#include +#include +#define O_BINARY 0 +#endif + +#if (!defined __x86_64__) && ((defined __MINGW32__) || (defined _MSC_VER)) +#define SYMBOL(x) binary_exe_##x +#else +#define SYMBOL(x) _binary_exe_##x +#endif + +extern "C" { +extern const uint8_t SYMBOL(start)[]; +extern const uint8_t SYMBOL(end)[]; + +} // extern "C" + +namespace { + +int32_t read4(const uint8_t* in) +{ + return (static_cast(in[3]) << 24) + | (static_cast(in[2]) << 16) + | (static_cast(in[1]) << 8) | (static_cast(in[0])); +} + +void* myAllocate(void*, size_t size) +{ + return malloc(size); +} + +void myFree(void*, void* address) +{ + free(address); +} + +#if (defined __MINGW32__) || (defined _MSC_VER) + +void* openLibrary(const char* name) +{ + return LoadLibrary(name); +} + +void* librarySymbol(void* library, const char* name) +{ + void* address; + FARPROC p = GetProcAddress(static_cast(library), name); + memcpy(&address, &p, sizeof(void*)); + return address; +} + +const char* libraryError(void*) +{ + return "unknown error"; +} + +const char* temporaryFileName(char* buffer, unsigned size) +{ + unsigned c = GetTempPathA(size, buffer); + if (c) { + if (GetTempFileNameA(buffer, "223", 0, buffer + c)) { + DeleteFileA(buffer + c); + return buffer + c; + } + } + return 0; +} + +#else + +void* openLibrary(const char* name) +{ + return dlopen(name, RTLD_LAZY | RTLD_LOCAL); +} + +void* librarySymbol(void* library, const char* name) +{ + return dlsym(library, name); +} + +const char* libraryError(void*) +{ + return dlerror(); +} + +const char* temporaryFileName(char* buffer, unsigned) +{ + return tmpnam(buffer); +} + +#endif + +} // namespace + +int main(int ac, const char** av) +{ + const unsigned PropHeaderSize = 5; + const unsigned HeaderSize = 13; + + SizeT inSize = SYMBOL(end) - SYMBOL(start); + + int32_t outSize32 = read4(SYMBOL(start) + PropHeaderSize); + SizeT outSize = outSize32; + + uint8_t* out = static_cast(malloc(outSize)); + if (out) { + ISzAlloc allocator = {myAllocate, myFree}; + ELzmaStatus status = LZMA_STATUS_NOT_SPECIFIED; + + if (SZ_OK == LzmaDecode(out, + &outSize, + SYMBOL(start) + HeaderSize, + &inSize, + SYMBOL(start), + PropHeaderSize, + LZMA_FINISH_END, + &status, + &allocator)) { + const unsigned BufferSize = 1024; + char buffer[BufferSize]; + const char* name = temporaryFileName(buffer, BufferSize); + if (name) { + int file = open(name, O_CREAT | O_EXCL | O_WRONLY | O_BINARY, S_IRWXU); + if (file != -1) { + SizeT result = write(file, out, outSize); + free(out); + + if (close(file) == 0 and outSize == result) { + void* library = openLibrary(name); + unlink(name); + + if (library) { + void* main = librarySymbol(library, "avianMain"); + if (main) { + int (*mainFunction)(const char*, int, const char**); + memcpy(&mainFunction, &main, sizeof(void*)); + return mainFunction(name, ac, av); + } else { + fprintf(stderr, "unable to find main in %s", name); + } + } else { + fprintf(stderr, + "unable to load %s: %s\n", + name, + libraryError(library)); + } + } else { + unlink(name); + + fprintf(stderr, + "close or write failed; tried %d, got %d; %s\n", + static_cast(outSize), + static_cast(result), + strerror(errno)); + } + } else { + fprintf(stderr, "unable to open %s\n", name); + } + } else { + fprintf(stderr, "unable to make temporary file name\n"); + } + } else { + fprintf(stderr, "unable to decode LZMA data\n"); + } + } else { + fprintf(stderr, + "unable to allocate buffer of size %d\n", + static_cast(outSize)); + } + + return -1; +} diff --git a/sgx-jvm/avian/src/lzma/main.cpp b/sgx-jvm/avian/src/lzma/main.cpp new file mode 100644 index 0000000000..5820e235cf --- /dev/null +++ b/sgx-jvm/avian/src/lzma/main.cpp @@ -0,0 +1,192 @@ +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#else +#include +#include +#endif +#include + +#include "LzmaEnc.h" +#include "LzmaDec.h" + +namespace { + +int32_t read4(const uint8_t* in) +{ + return (static_cast(in[3]) << 24) + | (static_cast(in[2]) << 16) + | (static_cast(in[1]) << 8) | (static_cast(in[0])); +} + +void* myAllocate(void*, size_t size) +{ + return malloc(size); +} + +void myFree(void*, void* address) +{ + free(address); +} + +SRes myProgress(void*, UInt64, UInt64) +{ + return SZ_OK; +} + +void usageAndExit(const char* program) +{ + fprintf(stderr, + "usage: %s {encode|decode} " + "[]", + program); + exit(-1); +} + +} // namespace + +int main(int argc, const char** argv) +{ + if (argc < 4 or argc > 5) { + usageAndExit(argv[0]); + } + + bool encode = strcmp(argv[1], "encode") == 0; + + uint8_t* data = 0; + unsigned size; + int fd = open(argv[2], O_RDONLY); + if (fd != -1) { + struct stat s; + int r = fstat(fd, &s); + if (r != -1) { +#ifdef _WIN32 + HANDLE fm; + HANDLE h = (HANDLE)_get_osfhandle(fd); + + fm = CreateFileMapping(h, NULL, PAGE_READONLY, 0, 0, NULL); + data = static_cast( + MapViewOfFile(fm, FILE_MAP_READ, 0, 0, s.st_size)); + + CloseHandle(fm); +#else + data = static_cast( + mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0)); +#endif + size = s.st_size; + } + close(fd); + } + + bool success = false; + + if (data) { + const unsigned PropHeaderSize = 5; + const unsigned HeaderSize = 13; + + SizeT outSize; + bool outSizeIsValid; + if (encode) { + outSize = size * 2; + outSizeIsValid = true; + } else { + int32_t outSize32 = read4(data + PropHeaderSize); + if (outSize32 < 0 and argc == 5) { + outSize32 = atoi(argv[4]); + } + + outSize = outSize32; + outSizeIsValid = outSize32 >= 0; + } + + if (outSizeIsValid) { + uint8_t* out = static_cast(malloc(outSize)); + if (out) { + SizeT inSize = size; + ISzAlloc allocator = {myAllocate, myFree}; + ELzmaStatus status = LZMA_STATUS_NOT_SPECIFIED; + int result; + if (encode) { + CLzmaEncProps props; + LzmaEncProps_Init(&props); + props.level = 9; + props.writeEndMark = 1; + + ICompressProgress progress = {myProgress}; + + SizeT propsSize = PropHeaderSize; + + int32_t inSize32 = inSize; + memcpy(out + PropHeaderSize, &inSize32, 4); + + result = LzmaEncode(out + HeaderSize, + &outSize, + data, + inSize, + &props, + out, + &propsSize, + 1, + &progress, + &allocator, + &allocator); + + outSize += HeaderSize; + } else { + result = LzmaDecode(out, + &outSize, + data + HeaderSize, + &inSize, + data, + PropHeaderSize, + LZMA_FINISH_END, + &status, + &allocator); + } + + if (result == SZ_OK) { + FILE* outFile = fopen(argv[3], "wb"); + + if (outFile) { + if (fwrite(out, outSize, 1, outFile) == 1) { + success = true; + } else { + fprintf(stderr, "unable to write to %s\n", argv[3]); + } + + fclose(outFile); + } else { + fprintf(stderr, "unable to open %s\n", argv[3]); + } + } else { + fprintf(stderr, + "unable to %s data: result %d status %d\n", + encode ? "encode" : "decode", + result, + status); + } + + free(out); + } else { + fprintf(stderr, "unable to allocate output buffer\n"); + } + } else { + fprintf(stderr, "unable to determine uncompressed size\n"); + } + +#ifdef _WIN32 + UnmapViewOfFile(data); +#else + munmap(data, size); +#endif + } else { + perror(argv[0]); + } + + return (success ? 0 : -1); +} diff --git a/sgx-jvm/avian/src/machine.cpp b/sgx-jvm/avian/src/machine.cpp new file mode 100644 index 0000000000..50768edc2e --- /dev/null +++ b/sgx-jvm/avian/src/machine.cpp @@ -0,0 +1,6366 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/jnienv.h" +#include "avian/machine.h" +#include "avian/util.h" +#include +#include "avian/constants.h" +#include "avian/processor.h" +#include "avian/arch.h" +#include "avian/lzma.h" + +#include +#include + +#if defined(PLATFORM_WINDOWS) +#define WIN32_LEAN_AND_MEAN +#include +#endif + +using namespace vm; +using namespace avian::util; + +namespace { + +const bool DebugClassReader = false; + +const unsigned NoByte = 0xFFFF; + +#ifdef USE_ATOMIC_OPERATIONS +void atomicIncrement(uint32_t* p, int v) +{ + for (uint32_t old = *p; not atomicCompareAndSwap32(p, old, old + v); + old = *p) { + } +} +#endif + +void join(Thread* t, Thread* o) +{ + if (t != o) { + assertT(t, o->state != Thread::JoinedState); + assertT(t, (o->getFlags() & Thread::SystemFlag) == 0); + if (o->getFlags() & Thread::JoinFlag) { + o->systemThread->join(); + } + o->state = Thread::JoinedState; + } +} + +#ifndef NDEBUG + +bool find(Thread* t, Thread* o) +{ + return (t == o) or (t->peer and find(t->peer, o)) + or (t->child and find(t->child, o)); +} + +unsigned count(Thread* t, Thread* o) +{ + unsigned c = 0; + + if (t != o) + ++c; + if (t->peer) + c += count(t->peer, o); + if (t->child) + c += count(t->child, o); + + return c; +} + +Thread** fill(Thread* t, Thread* o, Thread** array) +{ + if (t != o) + *(array++) = t; + if (t->peer) + array = fill(t->peer, o, array); + if (t->child) + array = fill(t->child, o, array); + + return array; +} + +#endif // not NDEBUG + +void dispose(Thread* t, Thread* o, bool remove) +{ + if (remove) { +#ifndef NDEBUG + expect(t, find(t->m->rootThread, o)); + + unsigned c = count(t->m->rootThread, o); + THREAD_RUNTIME_ARRAY(t, Thread*, threads, c); + fill(t->m->rootThread, o, RUNTIME_ARRAY_BODY(threads)); +#endif + + if (o->parent) { + Thread* previous = 0; + for (Thread* p = o->parent->child; p;) { + if (p == o) { + if (p == o->parent->child) { + o->parent->child = p->peer; + } else { + previous->peer = p->peer; + } + break; + } else { + previous = p; + p = p->peer; + } + } + + for (Thread* p = o->child; p;) { + Thread* next = p->peer; + p->peer = o->parent->child; + o->parent->child = p; + p->parent = o->parent; + p = next; + } + } else if (o->child) { + t->m->rootThread = o->child; + + for (Thread* p = o->peer; p;) { + Thread* next = p->peer; + p->peer = t->m->rootThread; + t->m->rootThread = p; + p = next; + } + } else if (o->peer) { + t->m->rootThread = o->peer; + } else { + abort(t); + } + +#ifndef NDEBUG + expect(t, not find(t->m->rootThread, o)); + + for (unsigned i = 0; i < c; ++i) { + expect(t, find(t->m->rootThread, RUNTIME_ARRAY_BODY(threads)[i])); + } +#endif + } + + o->dispose(); +} + +void visitAll(Thread* m, Thread* o, void (*visit)(Thread*, Thread*)) +{ + for (Thread* p = o->child; p;) { + Thread* child = p; + p = p->peer; + visitAll(m, child, visit); + } + + visit(m, o); +} + +void disposeNoRemove(Thread* m, Thread* o) +{ + dispose(m, o, false); +} + +void interruptDaemon(Thread* m, Thread* o) +{ + if (o->getFlags() & Thread::DaemonFlag) { + interrupt(m, o); + } +} + +void turnOffTheLights(Thread* t) +{ + expect(t, t->m->liveCount == 1); + + visitAll(t, t->m->rootThread, join); + + enter(t, Thread::ExitState); + + { + GcFinalizer* p = 0; + PROTECT(t, p); + + for (p = t->m->finalizers; p;) { + GcFinalizer* f = p; + p = cast(t, p->next()); + + void (*function)(Thread*, object); + memcpy(&function, &f->finalize(), BytesPerWord); + if (function) { + function(t, f->target()); + } + } + + for (p = t->m->tenuredFinalizers; p;) { + GcFinalizer* f = p; + p = cast(t, p->next()); + + void (*function)(Thread*, object); + memcpy(&function, &f->finalize(), BytesPerWord); + if (function) { + function(t, f->target()); + } + } + } + + if (GcArray* files = roots(t)->virtualFiles()) { + PROTECT(t, files); + for (unsigned i = 0; i < files->length(); ++i) { + object region = files->body()[i]; + if (region) { + static_cast(cast(t, region)->region()) + ->dispose(); + } + } + } + + for (GcFinder* p = roots(t)->virtualFileFinders(); p; p = p->next()) { + static_cast(p->finder())->dispose(); + } + + Machine* m = t->m; + + visitAll(t, t->m->rootThread, disposeNoRemove); + + System* s = m->system; + + expect(s, m->threadCount == 0); + + Heap* h = m->heap; + Processor* p = m->processor; + Classpath* c = m->classpath; + Finder* bf = m->bootFinder; + Finder* af = m->appFinder; + + c->dispose(); + h->disposeFixies(); + m->dispose(); + p->dispose(); + bf->dispose(); + af->dispose(); + h->dispose(); + s->dispose(); +} + +void killZombies(Thread* t, Thread* o) +{ + for (Thread* p = o->child; p;) { + Thread* child = p; + p = p->peer; + killZombies(t, child); + } + + if ((o->getFlags() & Thread::SystemFlag) == 0) { + switch (o->state) { + case Thread::ZombieState: + join(t, o); + // fall through + + case Thread::JoinedState: + dispose(t, o, true); + + default: + break; + } + } +} + +unsigned footprint(Thread* t) +{ + expect(t, t->criticalLevel == 0); + + unsigned n = t->heapOffset + t->heapIndex + t->backupHeapIndex; + + for (Thread* c = t->child; c; c = c->peer) { + n += footprint(c); + } + + return n; +} + +void visitRoots(Thread* t, Heap::Visitor* v) +{ + if (t->state != Thread::ZombieState) { + v->visit(&(t->javaThread)); + v->visit(&(t->exception)); + + t->m->processor->visitObjects(t, v); + + for (Thread::Protector* p = t->protector; p; p = p->next) { + p->visit(v); + } + } + + for (Thread* c = t->child; c; c = c->peer) { + visitRoots(c, v); + } +} + +bool walk(Thread*, + Heap::Walker* w, + uint32_t* mask, + unsigned fixedSize, + unsigned arrayElementSize, + unsigned arrayLength, + unsigned start) +{ + unsigned fixedSizeInWords = ceilingDivide(fixedSize, BytesPerWord); + unsigned arrayElementSizeInWords + = ceilingDivide(arrayElementSize, BytesPerWord); + + for (unsigned i = start; i < fixedSizeInWords; ++i) { + if (mask[i / 32] & (static_cast(1) << (i % 32))) { + if (not w->visit(i)) { + return false; + } + } + } + + bool arrayObjectElements = false; + for (unsigned j = 0; j < arrayElementSizeInWords; ++j) { + unsigned k = fixedSizeInWords + j; + if (mask[k / 32] & (static_cast(1) << (k % 32))) { + arrayObjectElements = true; + break; + } + } + + if (arrayObjectElements) { + unsigned arrayStart; + unsigned elementStart; + if (start > fixedSizeInWords) { + unsigned s = start - fixedSizeInWords; + arrayStart = s / arrayElementSizeInWords; + elementStart = s % arrayElementSizeInWords; + } else { + arrayStart = 0; + elementStart = 0; + } + + for (unsigned i = arrayStart; i < arrayLength; ++i) { + for (unsigned j = elementStart; j < arrayElementSizeInWords; ++j) { + unsigned k = fixedSizeInWords + j; + if (mask[k / 32] & (static_cast(1) << (k % 32))) { + if (not w->visit(fixedSizeInWords + (i * arrayElementSizeInWords) + + j)) { + return false; + } + } + } + } + } + + return true; +} + +object findInInterfaces( + Thread* t, + GcClass* class_, + GcByteArray* name, + GcByteArray* spec, + object (*find)(Thread*, GcClass*, GcByteArray*, GcByteArray*)) +{ + object result = 0; + if (GcArray* itable = cast(t, class_->interfaceTable())) { + PROTECT(t, itable); + for (unsigned i = 0; i < itable->length() and result == 0; i += 2) { + result = find(t, cast(t, itable->body()[i]), name, spec); + } + } + return result; +} + +void finalizerTargetUnreachable(Thread* t, Heap::Visitor* v, GcFinalizer** p) +{ + v->visit(&(*p)->target()); + + GcFinalizer* finalizer = *p; + *p = cast(t, finalizer->next()); + + void (*function)(Thread*, object); + memcpy(&function, &finalizer->finalize(), BytesPerWord); + + if (function) { + // TODO: use set() here? + finalizer->next() = t->m->finalizeQueue; + t->m->finalizeQueue = finalizer; + } else { + finalizer->setQueueTarget(t, finalizer->target()); + finalizer->setQueueNext(t, roots(t)->objectsToFinalize()); + roots(t)->setObjectsToFinalize(t, finalizer); + } +} + +void referenceTargetUnreachable(Thread* t, Heap::Visitor* v, GcJreference** p) +{ + if (DebugReferences) { + fprintf( + stderr, "target %p unreachable for reference %p\n", (*p)->target(), *p); + } + + v->visit(p); + (*p)->target() = 0; + + if (objectClass(t, *p) == type(t, GcCleaner::Type)) { + // In openjdk, sun/misc/Cleaner extends PhantomReference + GcCleaner* cleaner = (*p)->as(t); + + *p = cast(t, (*p)->vmNext()); + + cleaner->setQueueNext(t, roots(t)->objectsToClean()); + roots(t)->setObjectsToClean(t, cleaner); + } else { + if ((*p)->queue() + and t->m->heap->status((*p)->queue()) != Heap::Unreachable) { + // queue is reachable - add the reference + + v->visit(&(*p)->queue()); + + GcReferenceQueue* q = (*p)->queue(); + + if (q->front()) { + (*p)->setJNext(t, q->front()); + } else { + (*p)->setJNext(t, *p); + } + q->setFront(t, *p); + + (*p)->queue() = 0; + } + + *p = cast(t, (*p)->vmNext()); + } +} + +void referenceUnreachable(Thread* t, Heap::Visitor* v, GcJreference** p) +{ + GcJreference* r = t->m->heap->follow(*p); + + if (DebugReferences) { + fprintf(stderr, "reference %p unreachable (target %p)\n", *p, r->target()); + } + + if (r->queue() and t->m->heap->status(r->queue()) != Heap::Unreachable) { + // queue is reachable - add the reference + referenceTargetUnreachable(t, v, p); + } else { + *p = cast(t, (*p)->vmNext()); + } +} + +void referenceTargetReachable(Thread* t, Heap::Visitor* v, GcJreference** p) +{ + if (DebugReferences) { + fprintf( + stderr, "target %p reachable for reference %p\n", (*p)->target(), *p); + } + + v->visit(p); + v->visit(&(*p)->target()); + + if (t->m->heap->status((*p)->queue()) == Heap::Unreachable) { + (*p)->queue() = 0; + } else { + v->visit(&(*p)->queue()); + } +} + +bool isFinalizable(Thread* t, object o) +{ + return t->m->heap->status(o) == Heap::Unreachable + and (t->m->heap->follow(objectClass(t, o))->vmFlags() + & HasFinalizerFlag); +} + +void clearTargetIfFinalizable(Thread* t, GcJreference* r) +{ + if (isFinalizable(t, t->m->heap->follow(r->target()))) { + r->target() = 0; + } +} + +void postVisit(Thread* t, Heap::Visitor* v) +{ + Machine* m = t->m; + bool major = m->heap->collectionType() == Heap::MajorCollection; + + assertT(t, m->finalizeQueue == 0); + + m->heap->postVisit(); + + for (GcJreference* p = m->weakReferences; p;) { + GcJreference* r = m->heap->follow(p); + p = cast(t, r->vmNext()); + clearTargetIfFinalizable(t, r); + } + + if (major) { + for (GcJreference* p = m->tenuredWeakReferences; p;) { + GcJreference* r = m->heap->follow(p); + p = cast(t, r->vmNext()); + clearTargetIfFinalizable(t, r); + } + } + + for (Reference* r = m->jniReferences; r; r = r->next) { + if (r->weak + and isFinalizable(t, + static_cast(t->m->heap->follow(r->target)))) { + r->target = 0; + } + } + + GcFinalizer* firstNewTenuredFinalizer = 0; + GcFinalizer* lastNewTenuredFinalizer = 0; + + { + object unreachable = 0; + for (GcFinalizer** p = &(m->finalizers); *p;) { + v->visit(p); + + if (m->heap->status((*p)->target()) == Heap::Unreachable) { + GcFinalizer* finalizer = *p; + *p = cast(t, finalizer->next()); + + finalizer->next() = unreachable; + unreachable = finalizer; + } else { + p = reinterpret_cast(&(*p)->next()); + } + } + + for (GcFinalizer** p = &(m->finalizers); *p;) { + // target is reachable + v->visit(&(*p)->target()); + + if (m->heap->status(*p) == Heap::Tenured) { + // the finalizer is tenured, so we remove it from + // m->finalizers and later add it to m->tenuredFinalizers + + if (lastNewTenuredFinalizer == 0) { + lastNewTenuredFinalizer = *p; + } + + GcFinalizer* finalizer = *p; + *p = cast(t, finalizer->next()); + finalizer->next() = firstNewTenuredFinalizer; + firstNewTenuredFinalizer = finalizer; + } else { + p = reinterpret_cast(&(*p)->next()); + } + } + + for (object* p = &unreachable; *p;) { + // target is unreachable - queue it up for finalization + finalizerTargetUnreachable(t, v, reinterpret_cast(p)); + } + } + + GcJreference* firstNewTenuredWeakReference = 0; + GcJreference* lastNewTenuredWeakReference = 0; + + for (GcJreference** p = &(m->weakReferences); *p;) { + if (m->heap->status(*p) == Heap::Unreachable) { + // reference is unreachable + referenceUnreachable(t, v, p); + } else if (m->heap->status(m->heap->follow(*p)->target()) + == Heap::Unreachable) { + // target is unreachable + referenceTargetUnreachable(t, v, p); + } else { + // both reference and target are reachable + referenceTargetReachable(t, v, p); + + if (m->heap->status(*p) == Heap::Tenured) { + // the reference is tenured, so we remove it from + // m->weakReferences and later add it to + // m->tenuredWeakReferences + + if (lastNewTenuredWeakReference == 0) { + lastNewTenuredWeakReference = *p; + } + + GcJreference* reference = (*p); + *p = cast(t, reference->vmNext()); + reference->vmNext() = firstNewTenuredWeakReference; + firstNewTenuredWeakReference = reference; + } else { + p = reinterpret_cast(&(*p)->vmNext()); + } + } + } + + if (major) { + { + object unreachable = 0; + for (GcFinalizer** p = &(m->tenuredFinalizers); *p;) { + v->visit(p); + + if (m->heap->status((*p)->target()) == Heap::Unreachable) { + GcFinalizer* finalizer = *p; + *p = cast(t, finalizer->next()); + + finalizer->next() = unreachable; + unreachable = finalizer; + } else { + p = reinterpret_cast(&(*p)->next()); + } + } + + for (GcFinalizer** p = &(m->tenuredFinalizers); *p;) { + // target is reachable + v->visit(&(*p)->target()); + p = reinterpret_cast(&(*p)->next()); + } + + for (object* p = &unreachable; *p;) { + // target is unreachable - queue it up for finalization + finalizerTargetUnreachable(t, v, reinterpret_cast(p)); + } + } + + for (GcJreference** p = &(m->tenuredWeakReferences); *p;) { + if (m->heap->status(*p) == Heap::Unreachable) { + // reference is unreachable + referenceUnreachable(t, v, reinterpret_cast(p)); + } else if (m->heap->status(m->heap->follow(*p)->target()) + == Heap::Unreachable) { + // target is unreachable + referenceTargetUnreachable(t, v, reinterpret_cast(p)); + } else { + // both reference and target are reachable + referenceTargetReachable(t, v, reinterpret_cast(p)); + p = reinterpret_cast(&(*p)->vmNext()); + } + } + } + + if (lastNewTenuredFinalizer) { + lastNewTenuredFinalizer->next() = m->tenuredFinalizers; + m->tenuredFinalizers = firstNewTenuredFinalizer; + } + + if (lastNewTenuredWeakReference) { + lastNewTenuredWeakReference->vmNext() = m->tenuredWeakReferences; + m->tenuredWeakReferences = firstNewTenuredWeakReference; + } + + for (Reference* r = m->jniReferences; r; r = r->next) { + if (r->weak) { + if (m->heap->status(r->target) == Heap::Unreachable) { + r->target = 0; + } else { + v->visit(&(r->target)); + } + } + } +} + +void postCollect(Thread* t) +{ +#ifdef VM_STRESS + t->m->heap->free(t->defaultHeap, ThreadHeapSizeInBytes); + t->defaultHeap + = static_cast(t->m->heap->allocate(ThreadHeapSizeInBytes)); + memset(t->defaultHeap, 0, ThreadHeapSizeInBytes); +#endif + + if (t->heap == t->defaultHeap) { + memset(t->defaultHeap, 0, t->heapIndex * BytesPerWord); + } else { + memset(t->defaultHeap, 0, ThreadHeapSizeInBytes); + t->heap = t->defaultHeap; + } + + t->heapOffset = 0; + + if (t->m->heap->limitExceeded()) { + // if we're out of memory, pretend the thread-local heap is + // already full so we don't make things worse: + t->heapIndex = ThreadHeapSizeInWords; + } else { + t->heapIndex = 0; + } + + if (t->getFlags() & Thread::UseBackupHeapFlag) { + memset(t->backupHeap, 0, ThreadBackupHeapSizeInBytes); + + t->clearFlag(Thread::UseBackupHeapFlag); + t->backupHeapIndex = 0; + } + + for (Thread* c = t->child; c; c = c->peer) { + postCollect(c); + } +} + +uint64_t invoke(Thread* t, uintptr_t* arguments) +{ + GcMethod* m = cast(t, *reinterpret_cast(arguments[0])); + object o = *reinterpret_cast(arguments[1]); + + t->m->processor->invoke(t, m, o); + + return 1; +} + +void finalizeObject(Thread* t, object o, const char* name) +{ + for (GcClass* c = objectClass(t, o); c; c = c->super()) { + GcArray* mtable = cast(t, c->methodTable()); + for (unsigned i = 0; i < mtable->length(); ++i) { + GcMethod* m = cast(t, mtable->body()[i]); + + if (vm::strcmp(reinterpret_cast(name), + m->name()->body().begin()) == 0 + and vm::strcmp(reinterpret_cast("()V"), + m->spec()->body().begin()) == 0) { + PROTECT(t, m); + PROTECT(t, o); + + uintptr_t arguments[] = {reinterpret_cast(&m), + reinterpret_cast(&o)}; + + run(t, invoke, arguments); + + t->exception = 0; + return; + } + } + } + abort(t); +} + +unsigned readByte(AbstractStream& s, unsigned* value) +{ + if (*value == NoByte) { + return s.read1(); + } else { + unsigned r = *value; + *value = NoByte; + return r; + } +} + +GcCharArray* parseUtf8NonAscii(Thread* t, + AbstractStream& s, + GcByteArray* bytesSoFar, + unsigned byteCount, + unsigned sourceIndex, + unsigned byteA, + unsigned byteB) +{ + PROTECT(t, bytesSoFar); + + unsigned length = bytesSoFar->length() - 1; + GcCharArray* value = makeCharArray(t, length + 1); + + unsigned vi = 0; + for (; vi < byteCount; ++vi) { + value->body()[vi] = bytesSoFar->body()[vi]; + } + + for (unsigned si = sourceIndex; si < length; ++si) { + unsigned a = readByte(s, &byteA); + if (a & 0x80) { + if (a & 0x20) { + // 3 bytes + si += 2; + assertT(t, si < length); + unsigned b = readByte(s, &byteB); + unsigned c = s.read1(); + value->body()[vi++] = ((a & 0xf) << 12) | ((b & 0x3f) << 6) + | (c & 0x3f); + } else { + // 2 bytes + ++si; + assertT(t, si < length); + unsigned b = readByte(s, &byteB); + + if (a == 0xC0 and b == 0x80) { + value->body()[vi++] = 0; + } else { + value->body()[vi++] = ((a & 0x1f) << 6) | (b & 0x3f); + } + } + } else { + value->body()[vi++] = a; + } + } + + if (vi < length) { + PROTECT(t, value); + + GcCharArray* v = makeCharArray(t, vi + 1); + memcpy(v->body().begin(), value->body().begin(), vi * 2); + value = v; + } + + return value; +} + +object parseUtf8(Thread* t, AbstractStream& s, unsigned length) +{ + GcByteArray* value = makeByteArray(t, length + 1); + unsigned vi = 0; + for (unsigned si = 0; si < length; ++si) { + unsigned a = s.read1(); + if (a & 0x80) { + if (a & 0x20) { + // 3 bytes + return parseUtf8NonAscii(t, s, value, vi, si, a, NoByte); + } else { + // 2 bytes + unsigned b = s.read1(); + + if (a == 0xC0 and b == 0x80) { + ++si; + assertT(t, si < length); + value->body()[vi++] = 0; + } else { + return parseUtf8NonAscii(t, s, value, vi, si, a, b); + } + } + } else { + value->body()[vi++] = a; + } + } + + if (vi < length) { + PROTECT(t, value); + + GcByteArray* v = makeByteArray(t, vi + 1); + memcpy(v->body().begin(), value->body().begin(), vi); + value = v; + } + + return value; +} + +GcByteArray* makeByteArray(Thread* t, Stream& s, unsigned length) +{ + GcByteArray* value = makeByteArray(t, length + 1); + s.read(reinterpret_cast(value->body().begin()), length); + return value; +} + +void removeByteArray(Thread* t, object o) +{ + hashMapRemove(t, roots(t)->byteArrayMap(), o, byteArrayHash, objectEqual); +} + +GcByteArray* internByteArray(Thread* t, GcByteArray* array) +{ + PROTECT(t, array); + + ACQUIRE(t, t->m->referenceLock); + + GcTriple* n = hashMapFindNode( + t, roots(t)->byteArrayMap(), array, byteArrayHash, byteArrayEqual); + if (n) { + return cast(t, cast(t, n->first())->target()); + } else { + hashMapInsert(t, roots(t)->byteArrayMap(), array, 0, byteArrayHash); + addFinalizer(t, array, removeByteArray); + return array; + } +} + +unsigned parsePoolEntry(Thread* t, + Stream& s, + uint32_t* index, + GcSingleton* pool, + unsigned i) +{ + PROTECT(t, pool); + + s.setPosition(index[i]); + + switch (s.read1()) { + case CONSTANT_Integer: + case CONSTANT_Float: { + uint32_t v = s.read4(); + singletonValue(t, pool, i) = v; + + if (DebugClassReader) { + fprintf(stderr, " consts[%d] = int/float 0x%x\n", i, v); + } + } + return 1; + + case CONSTANT_Long: + case CONSTANT_Double: { + uint64_t v = s.read8(); + memcpy(&singletonValue(t, pool, i), &v, 8); + + if (DebugClassReader) { + fprintf(stderr, " consts[%d] = long/double \n", i); + } + } + return 2; + + case CONSTANT_Utf8: { + if (singletonObject(t, pool, i) == 0) { + GcByteArray* value = internByteArray(t, makeByteArray(t, s, s.read2())); + pool->setBodyElement(t, i, reinterpret_cast(value)); + + if (DebugClassReader) { + fprintf(stderr, " consts[%d] = utf8 %s\n", i, value->body().begin()); + } + } + } + return 1; + + case CONSTANT_Class: { + if (singletonObject(t, pool, i) == 0) { + unsigned si = s.read2() - 1; + parsePoolEntry(t, s, index, pool, si); + + GcReference* value = makeReference( + t, 0, 0, cast(t, singletonObject(t, pool, si)), 0); + pool->setBodyElement(t, i, reinterpret_cast(value)); + + if (DebugClassReader) { + fprintf(stderr, " consts[%d] = class \n", i); + } + } + } + return 1; + + case CONSTANT_String: { + if (singletonObject(t, pool, i) == 0) { + unsigned si = s.read2() - 1; + parsePoolEntry(t, s, index, pool, si); + + object value + = parseUtf8(t, cast(t, singletonObject(t, pool, si))); + value = t->m->classpath->makeString( + t, value, 0, fieldAtOffset(value, BytesPerWord) - 1); + value = intern(t, value); + pool->setBodyElement(t, i, reinterpret_cast(value)); + + if (DebugClassReader) { + fprintf(stderr, " consts[%d] = string \n", i); + } + } + } + return 1; + + case CONSTANT_NameAndType: { + if (singletonObject(t, pool, i) == 0) { + unsigned ni = s.read2() - 1; + unsigned ti = s.read2() - 1; + + parsePoolEntry(t, s, index, pool, ni); + parsePoolEntry(t, s, index, pool, ti); + + GcByteArray* name = cast(t, singletonObject(t, pool, ni)); + GcByteArray* type = cast(t, singletonObject(t, pool, ti)); + GcPair* value = makePair(t, name, type); + pool->setBodyElement(t, i, reinterpret_cast(value)); + + if (DebugClassReader) { + fprintf(stderr, + " consts[%d] = nameAndType %s%s\n", + i, + name->body().begin(), + type->body().begin()); + } + } + } + return 1; + + case CONSTANT_Fieldref: + case CONSTANT_Methodref: + case CONSTANT_InterfaceMethodref: { + if (singletonObject(t, pool, i) == 0) { + unsigned ci = s.read2() - 1; + unsigned nti = s.read2() - 1; + + parsePoolEntry(t, s, index, pool, ci); + parsePoolEntry(t, s, index, pool, nti); + + GcByteArray* className + = cast(t, singletonObject(t, pool, ci))->name(); + GcPair* nameAndType = cast(t, singletonObject(t, pool, nti)); + + object value = makeReference(t, + 0, + className, + cast(t, nameAndType->first()), + cast(t, nameAndType->second())); + pool->setBodyElement(t, i, reinterpret_cast(value)); + + if (DebugClassReader) { + fprintf(stderr, + " consts[%d] = method %s.%s%s\n", + i, + className->body().begin(), + cast(t, nameAndType->first())->body().begin(), + cast(t, nameAndType->second())->body().begin()); + } + } + } + return 1; + + case CONSTANT_MethodHandle: + if (singletonObject(t, pool, i) == 0) { + unsigned kind = s.read1(); + unsigned ri = s.read2() - 1; + + parsePoolEntry(t, s, index, pool, ri); + + GcReference* value = cast(t, singletonObject(t, pool, ri)); + + if (DebugClassReader) { + fprintf(stderr, + " consts[%d] = method handle %d %s.%s%s\n", + i, + kind, + value->class_()->body().begin(), + value->name()->body().begin(), + value->spec()->body().begin()); + } + + value = makeReference( + t, kind, value->class_(), value->name(), value->spec()); + + pool->setBodyElement(t, i, reinterpret_cast(value)); + } + return 1; + + case CONSTANT_MethodType: + if (singletonObject(t, pool, i) == 0) { + unsigned ni = s.read2() - 1; + + parsePoolEntry(t, s, index, pool, ni); + + pool->setBodyElement( + t, i, reinterpret_cast(singletonObject(t, pool, ni))); + } + return 1; + + case CONSTANT_InvokeDynamic: + if (singletonObject(t, pool, i) == 0) { + unsigned bootstrap = s.read2(); + unsigned nti = s.read2() - 1; + + parsePoolEntry(t, s, index, pool, nti); + + GcPair* nameAndType = cast(t, singletonObject(t, pool, nti)); + + const char* specString = reinterpret_cast( + cast(t, nameAndType->second())->body().begin()); + + unsigned parameterCount; + unsigned parameterFootprint; + unsigned returnCode; + scanMethodSpec(t, + specString, + true, + ¶meterCount, + ¶meterFootprint, + &returnCode); + + GcMethod* template_ + = makeMethod(t, + 0, + returnCode, + parameterCount, + parameterFootprint, + ACC_STATIC, + 0, + 0, + 0, + cast(t, nameAndType->first()), + cast(t, nameAndType->second()), + 0, + 0, + 0); + + object value = reinterpret_cast( + makeInvocation(t, bootstrap, -1, 0, pool, template_, 0)); + + pool->setBodyElement(t, i, reinterpret_cast(value)); + } + return 1; + + default: + abort(t); + } +} + +GcSingleton* parsePool(Thread* t, Stream& s) +{ + unsigned count = s.read2() - 1; + GcSingleton* pool = makeSingletonOfSize(t, count + poolMaskSize(count)); + PROTECT(t, pool); + + if (DebugClassReader) { + fprintf(stderr, " const pool entries %d\n", count); + } + + if (count) { + uint32_t* index = static_cast(t->m->heap->allocate(count * 4)); + + THREAD_RESOURCE2(t, + uint32_t*, + index, + unsigned, + count, + t->m->heap->free(index, count * 4)); + + for (unsigned i = 0; i < count; ++i) { + index[i] = s.position(); + + switch (s.read1()) { + case CONSTANT_Class: + case CONSTANT_String: + singletonMarkObject(t, pool, i); + s.skip(2); + break; + + case CONSTANT_Integer: + s.skip(4); + break; + + case CONSTANT_Float: + singletonSetBit(t, pool, count, i); + s.skip(4); + break; + + case CONSTANT_NameAndType: + case CONSTANT_Fieldref: + case CONSTANT_Methodref: + case CONSTANT_InterfaceMethodref: + singletonMarkObject(t, pool, i); + s.skip(4); + break; + + case CONSTANT_Long: + s.skip(8); + ++i; + break; + + case CONSTANT_Double: + singletonSetBit(t, pool, count, i); + singletonSetBit(t, pool, count, i + 1); + s.skip(8); + ++i; + break; + + case CONSTANT_Utf8: + singletonMarkObject(t, pool, i); + s.skip(s.read2()); + break; + + case CONSTANT_MethodHandle: + singletonMarkObject(t, pool, i); + s.skip(3); + break; + + case CONSTANT_MethodType: + singletonMarkObject(t, pool, i); + s.skip(2); + break; + + case CONSTANT_InvokeDynamic: + singletonMarkObject(t, pool, i); + s.skip(4); + break; + + default: + abort(t); + } + } + + unsigned end = s.position(); + + for (unsigned i = 0; i < count;) { + i += parsePoolEntry(t, s, index, pool, i); + } + + s.setPosition(end); + } + + return pool; +} + +void addInterfaces(Thread* t, GcClass* class_, GcHashMap* map) +{ + GcArray* table = cast(t, class_->interfaceTable()); + if (table) { + unsigned increment = 2; + if (class_->flags() & ACC_INTERFACE) { + increment = 1; + } + + PROTECT(t, map); + PROTECT(t, table); + + for (unsigned i = 0; i < table->length(); i += increment) { + GcClass* interface = cast(t, table->body()[i]); + GcByteArray* name = interface->name(); + hashMapInsertMaybe( + t, map, name, interface, byteArrayHash, byteArrayEqual); + } + } +} + +GcClassAddendum* getClassAddendum(Thread* t, GcClass* class_, GcSingleton* pool) +{ + GcClassAddendum* addendum = class_->addendum(); + if (addendum == 0) { + PROTECT(t, class_); + + addendum = makeClassAddendum(t, pool, 0, 0, 0, 0, -1, 0, 0, 0, 0); + setField(t, class_, ClassAddendum, addendum); + } + return addendum; +} + +void parseInterfaceTable(Thread* t, + Stream& s, + GcClass* class_, + GcSingleton* pool, + Gc::Type throwType) +{ + PROTECT(t, class_); + PROTECT(t, pool); + + GcHashMap* map = makeHashMap(t, 0, 0); + PROTECT(t, map); + + if (class_->super()) { + addInterfaces(t, class_->super(), map); + } + + unsigned count = s.read2(); + GcArray* table = 0; + PROTECT(t, table); + + if (count) { + table = makeArray(t, count); + + GcClassAddendum* addendum = getClassAddendum(t, class_, pool); + addendum->setInterfaceTable(t, table); + } + + for (unsigned i = 0; i < count; ++i) { + GcByteArray* name + = cast(t, singletonObject(t, pool, s.read2() - 1))->name(); + PROTECT(t, name); + + GcClass* interface = resolveClass( + t, class_->loader(), name, true, throwType); + + PROTECT(t, interface); + + table->setBodyElement(t, i, interface); + + hashMapInsertMaybe(t, map, name, interface, byteArrayHash, byteArrayEqual); + + addInterfaces(t, interface, map); + } + + GcArray* interfaceTable = 0; + if (map->size()) { + unsigned length = map->size(); + if ((class_->flags() & ACC_INTERFACE) == 0) { + length *= 2; + } + interfaceTable = makeArray(t, length); + PROTECT(t, interfaceTable); + + unsigned i = 0; + for (HashMapIterator it(t, map); it.hasMore();) { + GcClass* interface = cast(t, it.next()->second()); + + interfaceTable->setBodyElement(t, i, interface); + ++i; + + if ((class_->flags() & ACC_INTERFACE) == 0) { + if (GcArray* vt = cast(t, interface->virtualTable())) { + PROTECT(t, vt); + // we'll fill in this table in parseMethodTable(): + GcArray* vtable = makeArray(t, vt->length()); + + interfaceTable->setBodyElement(t, i, vtable); + } + + ++i; + } + } + } + + class_->setInterfaceTable(t, interfaceTable); +} + +void parseFieldTable(Thread* t, Stream& s, GcClass* class_, GcSingleton* pool) +{ + PROTECT(t, class_); + PROTECT(t, pool); + + unsigned memberOffset = BytesPerWord; + if (class_->super()) { + memberOffset = class_->super()->fixedSize(); + } + + unsigned count = s.read2(); + if (count) { + unsigned staticOffset = BytesPerWord * 3; + unsigned staticCount = 0; + + GcArray* fieldTable = makeArray(t, count); + PROTECT(t, fieldTable); + + GcIntArray* staticValueTable = makeIntArray(t, count); + PROTECT(t, staticValueTable); + + GcFieldAddendum* addendum = 0; + PROTECT(t, addendum); + + THREAD_RUNTIME_ARRAY(t, uint8_t, staticTypes, count); + + for (unsigned i = 0; i < count; ++i) { + unsigned flags = s.read2(); + unsigned name = s.read2(); + unsigned spec = s.read2(); + + unsigned value = 0; + + addendum = 0; + + unsigned code = fieldCode( + t, + cast(t, singletonObject(t, pool, spec - 1))->body()[0]); + + unsigned attributeCount = s.read2(); + for (unsigned j = 0; j < attributeCount; ++j) { + GcByteArray* name + = cast(t, singletonObject(t, pool, s.read2() - 1)); + unsigned length = s.read4(); + + if (vm::strcmp(reinterpret_cast("ConstantValue"), + name->body().begin()) == 0) { + value = s.read2(); + } else if (vm::strcmp(reinterpret_cast("Signature"), + name->body().begin()) == 0) { + if (addendum == 0) { + addendum = makeFieldAddendum(t, pool, 0, 0); + } + + addendum->setSignature(t, singletonObject(t, pool, s.read2() - 1)); + } else if (vm::strcmp(reinterpret_cast( + "RuntimeVisibleAnnotations"), + name->body().begin()) == 0) { + if (addendum == 0) { + addendum = makeFieldAddendum(t, pool, 0, 0); + } + + GcByteArray* body = makeByteArray(t, length); + s.read(reinterpret_cast(body->body().begin()), length); + + addendum->setAnnotationTable(t, body); + } else { + s.skip(length); + } + } + + GcField* field + = makeField(t, + 0, // vm flags + code, + flags, + 0, // offset + 0, // native ID + cast(t, singletonObject(t, pool, name - 1)), + cast(t, singletonObject(t, pool, spec - 1)), + addendum, + class_); + + unsigned size = fieldSize(t, code); + if (flags & ACC_STATIC) { + staticOffset = pad(staticOffset, size); + + field->offset() = staticOffset; + + staticOffset += size; + + staticValueTable->body()[staticCount] = value; + + RUNTIME_ARRAY_BODY(staticTypes)[staticCount++] = code; + } else { + if (flags & ACC_FINAL) { + class_->vmFlags() |= HasFinalMemberFlag; + } + + memberOffset = pad(memberOffset, size); + + field->offset() = memberOffset; + + memberOffset += size; + } + + fieldTable->setBodyElement(t, i, field); + } + + class_->setFieldTable(t, fieldTable); + + if (staticCount) { + unsigned footprint + = ceilingDivide(staticOffset - (BytesPerWord * 2), BytesPerWord); + GcSingleton* staticTable = makeSingletonOfSize(t, footprint); + + uint8_t* body = reinterpret_cast(staticTable->body().begin()); + + memcpy(body, &class_, BytesPerWord); + singletonMarkObject(t, staticTable, 0); + + for (unsigned i = 0, offset = BytesPerWord; i < staticCount; ++i) { + unsigned size = fieldSize(t, RUNTIME_ARRAY_BODY(staticTypes)[i]); + offset = pad(offset, size); + + unsigned value = staticValueTable->body()[i]; + if (value) { + switch (RUNTIME_ARRAY_BODY(staticTypes)[i]) { + case ByteField: + case BooleanField: + body[offset] = singletonValue(t, pool, value - 1); + break; + + case CharField: + case ShortField: + *reinterpret_cast(body + offset) + = singletonValue(t, pool, value - 1); + break; + + case IntField: + case FloatField: + *reinterpret_cast(body + offset) + = singletonValue(t, pool, value - 1); + break; + + case LongField: + case DoubleField: + memcpy(body + offset, &singletonValue(t, pool, value - 1), 8); + break; + + case ObjectField: + memcpy(body + offset, + &singletonObject(t, pool, value - 1), + BytesPerWord); + break; + + default: + abort(t); + } + } + + if (RUNTIME_ARRAY_BODY(staticTypes)[i] == ObjectField) { + singletonMarkObject(t, staticTable, offset / BytesPerWord); + } + + offset += size; + } + + class_->setStaticTable(t, staticTable); + } + } + + class_->fixedSize() = memberOffset; + + if (class_->super() and memberOffset == class_->super()->fixedSize()) { + class_->setObjectMask(t, class_->super()->objectMask()); + } else { + GcIntArray* mask = makeIntArray( + t, ceilingDivide(class_->fixedSize(), 32 * BytesPerWord)); + mask->body()[0] = 1; + + GcIntArray* superMask = 0; + if (class_->super()) { + superMask = class_->super()->objectMask(); + if (superMask) { + memcpy( + mask->body().begin(), + superMask->body().begin(), + ceilingDivide(class_->super()->fixedSize(), 32 * BytesPerWord) * 4); + } + } + + bool sawReferenceField = false; + GcArray* fieldTable = cast(t, class_->fieldTable()); + if (fieldTable) { + for (int i = fieldTable->length() - 1; i >= 0; --i) { + GcField* field = cast(t, fieldTable->body()[i]); + if ((field->flags() & ACC_STATIC) == 0 + and field->code() == ObjectField) { + unsigned index = field->offset() / BytesPerWord; + mask->body()[index / 32] |= 1 << (index % 32); + sawReferenceField = true; + } + } + } + + if (superMask or sawReferenceField) { + class_->setObjectMask(t, mask); + } + } +} + +uint16_t read16(uint8_t* code, unsigned& ip) +{ + uint16_t a = code[ip++]; + uint16_t b = code[ip++]; + return (a << 8) | b; +} + +uint32_t read32(uint8_t* code, unsigned& ip) +{ + uint32_t b = code[ip++]; + uint32_t a = code[ip++]; + uint32_t c = code[ip++]; + uint32_t d = code[ip++]; + return (a << 24) | (b << 16) | (c << 8) | d; +} + +void disassembleCode(const char* prefix, uint8_t* code, unsigned length) +{ + unsigned ip = 0; + + while (ip < length) { + unsigned instr; + fprintf(stderr, "%s%x:\t", prefix, ip); + switch (instr = code[ip++]) { + case aaload: + fprintf(stderr, "aaload\n"); + break; + case aastore: + fprintf(stderr, "aastore\n"); + break; + + case aconst_null: + fprintf(stderr, "aconst_null\n"); + break; + + case aload: + fprintf(stderr, "aload %02x\n", code[ip++]); + break; + case aload_0: + fprintf(stderr, "aload_0\n"); + break; + case aload_1: + fprintf(stderr, "aload_1\n"); + break; + case aload_2: + fprintf(stderr, "aload_2\n"); + break; + case aload_3: + fprintf(stderr, "aload_3\n"); + break; + + case anewarray: + fprintf(stderr, "anewarray %04x\n", read16(code, ip)); + break; + case areturn: + fprintf(stderr, "areturn\n"); + break; + case arraylength: + fprintf(stderr, "arraylength\n"); + break; + + case astore: + fprintf(stderr, "astore %02x\n", code[ip++]); + break; + case astore_0: + fprintf(stderr, "astore_0\n"); + break; + case astore_1: + fprintf(stderr, "astore_1\n"); + break; + case astore_2: + fprintf(stderr, "astore_2\n"); + break; + case astore_3: + fprintf(stderr, "astore_3\n"); + break; + + case athrow: + fprintf(stderr, "athrow\n"); + break; + case baload: + fprintf(stderr, "baload\n"); + break; + case bastore: + fprintf(stderr, "bastore\n"); + break; + + case bipush: + fprintf(stderr, "bipush %02x\n", code[ip++]); + break; + case caload: + fprintf(stderr, "caload\n"); + break; + case castore: + fprintf(stderr, "castore\n"); + break; + case checkcast: + fprintf(stderr, "checkcast %04x\n", read16(code, ip)); + break; + case d2f: + fprintf(stderr, "d2f\n"); + break; + case d2i: + fprintf(stderr, "d2i\n"); + break; + case d2l: + fprintf(stderr, "d2l\n"); + break; + case dadd: + fprintf(stderr, "dadd\n"); + break; + case daload: + fprintf(stderr, "daload\n"); + break; + case dastore: + fprintf(stderr, "dastore\n"); + break; + case dcmpg: + fprintf(stderr, "dcmpg\n"); + break; + case dcmpl: + fprintf(stderr, "dcmpl\n"); + break; + case dconst_0: + fprintf(stderr, "dconst_0\n"); + break; + case dconst_1: + fprintf(stderr, "dconst_1\n"); + break; + case ddiv: + fprintf(stderr, "ddiv\n"); + break; + case dmul: + fprintf(stderr, "dmul\n"); + break; + case dneg: + fprintf(stderr, "dneg\n"); + break; + case vm::drem: + fprintf(stderr, "drem\n"); + break; + case dsub: + fprintf(stderr, "dsub\n"); + break; + case vm::dup: + fprintf(stderr, "dup\n"); + break; + case dup_x1: + fprintf(stderr, "dup_x1\n"); + break; + case dup_x2: + fprintf(stderr, "dup_x2\n"); + break; + case vm::dup2: + fprintf(stderr, "dup2\n"); + break; + case dup2_x1: + fprintf(stderr, "dup2_x1\n"); + break; + case dup2_x2: + fprintf(stderr, "dup2_x2\n"); + break; + case f2d: + fprintf(stderr, "f2d\n"); + break; + case f2i: + fprintf(stderr, "f2i\n"); + break; + case f2l: + fprintf(stderr, "f2l\n"); + break; + case fadd: + fprintf(stderr, "fadd\n"); + break; + case faload: + fprintf(stderr, "faload\n"); + break; + case fastore: + fprintf(stderr, "fastore\n"); + break; + case fcmpg: + fprintf(stderr, "fcmpg\n"); + break; + case fcmpl: + fprintf(stderr, "fcmpl\n"); + break; + case fconst_0: + fprintf(stderr, "fconst_0\n"); + break; + case fconst_1: + fprintf(stderr, "fconst_1\n"); + break; + case fconst_2: + fprintf(stderr, "fconst_2\n"); + break; + case fdiv: + fprintf(stderr, "fdiv\n"); + break; + case fmul: + fprintf(stderr, "fmul\n"); + break; + case fneg: + fprintf(stderr, "fneg\n"); + break; + case frem: + fprintf(stderr, "frem\n"); + break; + case fsub: + fprintf(stderr, "fsub\n"); + break; + + case getfield: + fprintf(stderr, "getfield %04x\n", read16(code, ip)); + break; + case getstatic: + fprintf(stderr, "getstatic %04x\n", read16(code, ip)); + break; + case goto_: { + int16_t offset = read16(code, ip); + fprintf(stderr, "goto %04x\n", offset + ip - 3); + } break; + case goto_w: { + int32_t offset = read32(code, ip); + fprintf(stderr, "goto_w %08x\n", offset + ip - 5); + } break; + + case i2b: + fprintf(stderr, "i2b\n"); + break; + case i2c: + fprintf(stderr, "i2c\n"); + break; + case i2d: + fprintf(stderr, "i2d\n"); + break; + case i2f: + fprintf(stderr, "i2f\n"); + break; + case i2l: + fprintf(stderr, "i2l\n"); + break; + case i2s: + fprintf(stderr, "i2s\n"); + break; + case iadd: + fprintf(stderr, "iadd\n"); + break; + case iaload: + fprintf(stderr, "iaload\n"); + break; + case iand: + fprintf(stderr, "iand\n"); + break; + case iastore: + fprintf(stderr, "iastore\n"); + break; + case iconst_m1: + fprintf(stderr, "iconst_m1\n"); + break; + case iconst_0: + fprintf(stderr, "iconst_0\n"); + break; + case iconst_1: + fprintf(stderr, "iconst_1\n"); + break; + case iconst_2: + fprintf(stderr, "iconst_2\n"); + break; + case iconst_3: + fprintf(stderr, "iconst_3\n"); + break; + case iconst_4: + fprintf(stderr, "iconst_4\n"); + break; + case iconst_5: + fprintf(stderr, "iconst_5\n"); + break; + case idiv: + fprintf(stderr, "idiv\n"); + break; + + case if_acmpeq: { + int16_t offset = read16(code, ip); + fprintf(stderr, "if_acmpeq %04x\n", offset + ip - 3); + } break; + case if_acmpne: { + int16_t offset = read16(code, ip); + fprintf(stderr, "if_acmpne %04x\n", offset + ip - 3); + } break; + case if_icmpeq: { + int16_t offset = read16(code, ip); + fprintf(stderr, "if_icmpeq %04x\n", offset + ip - 3); + } break; + case if_icmpne: { + int16_t offset = read16(code, ip); + fprintf(stderr, "if_icmpne %04x\n", offset + ip - 3); + } break; + + case if_icmpgt: { + int16_t offset = read16(code, ip); + fprintf(stderr, "if_icmpgt %04x\n", offset + ip - 3); + } break; + case if_icmpge: { + int16_t offset = read16(code, ip); + fprintf(stderr, "if_icmpge %04x\n", offset + ip - 3); + } break; + case if_icmplt: { + int16_t offset = read16(code, ip); + fprintf(stderr, "if_icmplt %04x\n", offset + ip - 3); + } break; + case if_icmple: { + int16_t offset = read16(code, ip); + fprintf(stderr, "if_icmple %04x\n", offset + ip - 3); + } break; + + case ifeq: { + int16_t offset = read16(code, ip); + fprintf(stderr, "ifeq %04x\n", offset + ip - 3); + } break; + case ifne: { + int16_t offset = read16(code, ip); + fprintf(stderr, "ifne %04x\n", offset + ip - 3); + } break; + case ifgt: { + int16_t offset = read16(code, ip); + fprintf(stderr, "ifgt %04x\n", offset + ip - 3); + } break; + case ifge: { + int16_t offset = read16(code, ip); + fprintf(stderr, "ifge %04x\n", offset + ip - 3); + } break; + case iflt: { + int16_t offset = read16(code, ip); + fprintf(stderr, "iflt %04x\n", offset + ip - 3); + } break; + case ifle: { + int16_t offset = read16(code, ip); + fprintf(stderr, "ifle %04x\n", offset + ip - 3); + } break; + + case ifnonnull: { + int16_t offset = read16(code, ip); + fprintf(stderr, "ifnonnull %04x\n", offset + ip - 3); + } break; + case ifnull: { + int16_t offset = read16(code, ip); + fprintf(stderr, "ifnull %04x\n", offset + ip - 3); + } break; + + case iinc: { + uint8_t a = code[ip++]; + uint8_t b = code[ip++]; + fprintf(stderr, "iinc %02x %02x\n", a, b); + } break; + + case iload: + fprintf(stderr, "iload %02x\n", code[ip++]); + break; + case fload: + fprintf(stderr, "fload %02x\n", code[ip++]); + break; + + case iload_0: + fprintf(stderr, "iload_0\n"); + break; + case fload_0: + fprintf(stderr, "fload_0\n"); + break; + case iload_1: + fprintf(stderr, "iload_1\n"); + break; + case fload_1: + fprintf(stderr, "fload_1\n"); + break; + + case iload_2: + fprintf(stderr, "iload_2\n"); + break; + case fload_2: + fprintf(stderr, "fload_2\n"); + break; + case iload_3: + fprintf(stderr, "iload_3\n"); + break; + case fload_3: + fprintf(stderr, "fload_3\n"); + break; + + case imul: + fprintf(stderr, "imul\n"); + break; + case ineg: + fprintf(stderr, "ineg\n"); + break; + + case instanceof: + fprintf(stderr, "instanceof %04x\n", read16(code, ip)); + break; + case invokeinterface: + fprintf(stderr, "invokeinterface %04x\n", read16(code, ip)); + break; + case invokespecial: + fprintf(stderr, "invokespecial %04x\n", read16(code, ip)); + break; + case invokestatic: + fprintf(stderr, "invokestatic %04x\n", read16(code, ip)); + break; + case invokevirtual: + fprintf(stderr, "invokevirtual %04x\n", read16(code, ip)); + break; + + case ior: + fprintf(stderr, "ior\n"); + break; + case irem: + fprintf(stderr, "irem\n"); + break; + case ireturn: + fprintf(stderr, "ireturn\n"); + break; + case freturn: + fprintf(stderr, "freturn\n"); + break; + case ishl: + fprintf(stderr, "ishl\n"); + break; + case ishr: + fprintf(stderr, "ishr\n"); + break; + + case istore: + fprintf(stderr, "istore %02x\n", code[ip++]); + break; + case fstore: + fprintf(stderr, "fstore %02x\n", code[ip++]); + break; + + case istore_0: + fprintf(stderr, "istore_0\n"); + break; + case fstore_0: + fprintf(stderr, "fstore_0\n"); + break; + case istore_1: + fprintf(stderr, "istore_1\n"); + break; + case fstore_1: + fprintf(stderr, "fstore_1\n"); + break; + case istore_2: + fprintf(stderr, "istore_2\n"); + break; + case fstore_2: + fprintf(stderr, "fstore_2\n"); + break; + case istore_3: + fprintf(stderr, "istore_3\n"); + break; + case fstore_3: + fprintf(stderr, "fstore_3\n"); + break; + + case isub: + fprintf(stderr, "isub\n"); + break; + case iushr: + fprintf(stderr, "iushr\n"); + break; + case ixor: + fprintf(stderr, "ixor\n"); + break; + + case jsr: + fprintf(stderr, "jsr %04x\n", read16(code, ip)); + break; + case jsr_w: + fprintf(stderr, "jsr_w %08x\n", read32(code, ip)); + break; + + case l2d: + fprintf(stderr, "l2d\n"); + break; + case l2f: + fprintf(stderr, "l2f\n"); + break; + case l2i: + fprintf(stderr, "l2i\n"); + break; + case ladd: + fprintf(stderr, "ladd\n"); + break; + case laload: + fprintf(stderr, "laload\n"); + break; + + case land: + fprintf(stderr, "land\n"); + break; + case lastore: + fprintf(stderr, "lastore\n"); + break; + + case lcmp: + fprintf(stderr, "lcmp\n"); + break; + case lconst_0: + fprintf(stderr, "lconst_0\n"); + break; + case lconst_1: + fprintf(stderr, "lconst_1\n"); + break; + + case ldc: + fprintf(stderr, "ldc %04x\n", read16(code, ip)); + break; + case ldc_w: + fprintf(stderr, "ldc_w %08x\n", read32(code, ip)); + break; + case ldc2_w: + fprintf(stderr, "ldc2_w %04x\n", read16(code, ip)); + break; + + case ldiv_: + fprintf(stderr, "ldiv_\n"); + break; + + case lload: + fprintf(stderr, "lload %02x\n", code[ip++]); + break; + case dload: + fprintf(stderr, "dload %02x\n", code[ip++]); + break; + + case lload_0: + fprintf(stderr, "lload_0\n"); + break; + case dload_0: + fprintf(stderr, "dload_0\n"); + break; + case lload_1: + fprintf(stderr, "lload_1\n"); + break; + case dload_1: + fprintf(stderr, "dload_1\n"); + break; + case lload_2: + fprintf(stderr, "lload_2\n"); + break; + case dload_2: + fprintf(stderr, "dload_2\n"); + break; + case lload_3: + fprintf(stderr, "lload_3\n"); + break; + case dload_3: + fprintf(stderr, "dload_3\n"); + break; + + case lmul: + fprintf(stderr, "lmul\n"); + break; + case lneg: + fprintf(stderr, "lneg\n"); + break; + + case lookupswitch: { + int32_t default_ = read32(code, ip); + int32_t pairCount = read32(code, ip); + fprintf(stderr, + "lookupswitch default: %d pairCount: %d\n", + default_, + pairCount); + + for (int i = 0; i < pairCount; i++) { + int32_t k = read32(code, ip); + int32_t d = read32(code, ip); + fprintf(stderr, "%s key: %02x dest: %2x\n", prefix, k, d); + } + } break; + + case lor: + fprintf(stderr, "lor\n"); + break; + case lrem: + fprintf(stderr, "lrem\n"); + break; + case lreturn: + fprintf(stderr, "lreturn\n"); + break; + case dreturn: + fprintf(stderr, "dreturn\n"); + break; + case lshl: + fprintf(stderr, "lshl\n"); + break; + case lshr: + fprintf(stderr, "lshr\n"); + break; + + case lstore: + fprintf(stderr, "lstore %02x\n", code[ip++]); + break; + case dstore: + fprintf(stderr, "dstore %02x\n", code[ip++]); + break; + + case lstore_0: + fprintf(stderr, "lstore_0\n"); + break; + case dstore_0: + fprintf(stderr, "dstore_0\n"); + break; + case lstore_1: + fprintf(stderr, "lstore_1\n"); + break; + case dstore_1: + fprintf(stderr, "dstore_1\n"); + break; + case lstore_2: + fprintf(stderr, "lstore_2\n"); + break; + case dstore_2: + fprintf(stderr, "dstore_2\n"); + break; + case lstore_3: + fprintf(stderr, "lstore_3\n"); + break; + case dstore_3: + fprintf(stderr, "dstore_3\n"); + break; + + case lsub: + fprintf(stderr, "lsub\n"); + break; + case lushr: + fprintf(stderr, "lushr\n"); + break; + case lxor: + fprintf(stderr, "lxor\n"); + break; + + case monitorenter: + fprintf(stderr, "monitorenter\n"); + break; + case monitorexit: + fprintf(stderr, "monitorexit\n"); + break; + + case multianewarray: { + unsigned type = read16(code, ip); + fprintf(stderr, "multianewarray %04x %02x\n", type, code[ip++]); + } break; + + case new_: + fprintf(stderr, "new %04x\n", read16(code, ip)); + break; + + case newarray: + fprintf(stderr, "newarray %02x\n", code[ip++]); + break; + + case nop: + fprintf(stderr, "nop\n"); + break; + case pop_: + fprintf(stderr, "pop\n"); + break; + case pop2: + fprintf(stderr, "pop2\n"); + break; + + case putfield: + fprintf(stderr, "putfield %04x\n", read16(code, ip)); + break; + case putstatic: + fprintf(stderr, "putstatic %04x\n", read16(code, ip)); + break; + + case ret: + fprintf(stderr, "ret %02x\n", code[ip++]); + break; + + case return_: + fprintf(stderr, "return_\n"); + break; + case saload: + fprintf(stderr, "saload\n"); + break; + case sastore: + fprintf(stderr, "sastore\n"); + break; + + case sipush: + fprintf(stderr, "sipush %04x\n", read16(code, ip)); + break; + + case swap: + fprintf(stderr, "swap\n"); + break; + + case tableswitch: { + int32_t default_ = read32(code, ip); + int32_t bottom = read32(code, ip); + int32_t top = read32(code, ip); + fprintf(stderr, + "tableswitch default: %d bottom: %d top: %d\n", + default_, + bottom, + top); + + for (int i = 0; i < top - bottom + 1; i++) { + int32_t d = read32(code, ip); + fprintf(stderr, "%s key: %d dest: %2x\n", prefix, i + bottom, d); + } + } break; + + case wide: { + switch (code[ip++]) { + case aload: + fprintf(stderr, "wide aload %04x\n", read16(code, ip)); + break; + + case astore: + fprintf(stderr, "wide astore %04x\n", read16(code, ip)); + break; + case iinc: + fprintf(stderr, + "wide iinc %04x %04x\n", + read16(code, ip), + read16(code, ip)); + break; + case iload: + fprintf(stderr, "wide iload %04x\n", read16(code, ip)); + break; + case istore: + fprintf(stderr, "wide istore %04x\n", read16(code, ip)); + break; + case lload: + fprintf(stderr, "wide lload %04x\n", read16(code, ip)); + break; + case lstore: + fprintf(stderr, "wide lstore %04x\n", read16(code, ip)); + break; + case ret: + fprintf(stderr, "wide ret %04x\n", read16(code, ip)); + break; + + default: { + fprintf(stderr, + "unknown wide instruction %02x %04x\n", + instr, + read16(code, ip)); + } + } + } break; + + default: { + fprintf(stderr, "unknown instruction %02x\n", instr); + } + } + } +} + +GcCode* parseCode(Thread* t, Stream& s, GcSingleton* pool) +{ + PROTECT(t, pool); + + unsigned maxStack = s.read2(); + unsigned maxLocals = s.read2(); + unsigned length = s.read4(); + + if (DebugClassReader) { + fprintf(stderr, + " code: maxStack %d maxLocals %d length %d\n", + maxStack, + maxLocals, + length); + } + + GcCode* code = makeCode(t, pool, 0, 0, 0, 0, 0, maxStack, maxLocals, length); + s.read(code->body().begin(), length); + PROTECT(t, code); + + if (DebugClassReader) { + disassembleCode(" ", code->body().begin(), length); + } + + unsigned ehtLength = s.read2(); + if (ehtLength) { + GcExceptionHandlerTable* eht = makeExceptionHandlerTable(t, ehtLength); + for (unsigned i = 0; i < ehtLength; ++i) { + unsigned start = s.read2(); + unsigned end = s.read2(); + unsigned ip = s.read2(); + unsigned catchType = s.read2(); + eht->body()[i] = exceptionHandler(start, end, ip, catchType); + } + + code->setExceptionHandlerTable(t, eht); + } + + unsigned attributeCount = s.read2(); + for (unsigned j = 0; j < attributeCount; ++j) { + GcByteArray* name + = cast(t, singletonObject(t, pool, s.read2() - 1)); + unsigned length = s.read4(); + + if (vm::strcmp(reinterpret_cast("LineNumberTable"), + name->body().begin()) == 0) { + unsigned lntLength = s.read2(); + GcLineNumberTable* lnt = makeLineNumberTable(t, lntLength); + for (unsigned i = 0; i < lntLength; ++i) { + unsigned ip = s.read2(); + unsigned line = s.read2(); + lnt->body()[i] = lineNumber(ip, line); + } + + code->setLineNumberTable(t, lnt); + } else { + s.skip(length); + } + } + + return code; +} + +GcList* addInterfaceMethods(Thread* t, + GcClass* class_, + GcHashMap* virtualMap, + unsigned* virtualCount, + bool makeList) +{ + GcArray* itable = cast(t, class_->interfaceTable()); + if (itable) { + PROTECT(t, class_); + PROTECT(t, virtualMap); + PROTECT(t, itable); + + GcList* list = 0; + PROTECT(t, list); + + GcMethod* method = 0; + PROTECT(t, method); + + GcArray* vtable = 0; + PROTECT(t, vtable); + + unsigned stride = (class_->flags() & ACC_INTERFACE) ? 1 : 2; + for (unsigned i = 0; i < itable->length(); i += stride) { + vtable = cast( + t, cast(t, itable->body()[i])->virtualTable()); + if (vtable) { + for (unsigned j = 0; j < vtable->length(); ++j) { + method = cast(t, vtable->body()[j]); + GcTriple* n + = hashMapFindNode(t, virtualMap, method, methodHash, methodEqual); + if (n == 0) { + method = makeMethod(t, + method->vmFlags(), + method->returnCode(), + method->parameterCount(), + method->parameterFootprint(), + method->flags(), + (*virtualCount)++, + 0, + 0, + method->name(), + method->spec(), + 0, + class_, + method->code()); + + hashMapInsert(t, virtualMap, method, method, methodHash); + + if (makeList) { + if (list == 0) { + list = vm::makeList(t, 0, 0, 0); + } + listAppend(t, list, method); + } + } + } + } + } + + return list; + } + + return 0; +} + +void parseMethodTable(Thread* t, Stream& s, GcClass* class_, GcSingleton* pool) +{ + PROTECT(t, class_); + PROTECT(t, pool); + + GcHashMap* virtualMap = makeHashMap(t, 0, 0); + PROTECT(t, virtualMap); + + unsigned virtualCount = 0; + unsigned declaredVirtualCount = 0; + + GcArray* superVirtualTable = 0; + PROTECT(t, superVirtualTable); + + if ((class_->flags() & ACC_INTERFACE) == 0) { + if (class_->super()) { + superVirtualTable = cast(t, class_->super()->virtualTable()); + } + + if (superVirtualTable) { + virtualCount = superVirtualTable->length(); + for (unsigned i = 0; i < virtualCount; ++i) { + object method = superVirtualTable->body()[i]; + hashMapInsert(t, virtualMap, method, method, methodHash); + } + } + } + + GcList* newVirtuals = makeList(t, 0, 0, 0); + PROTECT(t, newVirtuals); + + unsigned count = s.read2(); + + if (DebugClassReader) { + fprintf(stderr, " method count %d\n", count); + } + + if (count) { + GcArray* methodTable = makeArray(t, count); + PROTECT(t, methodTable); + + GcMethodAddendum* addendum = 0; + PROTECT(t, addendum); + + GcCode* code = 0; + PROTECT(t, code); + + for (unsigned i = 0; i < count; ++i) { + unsigned flags = s.read2(); + unsigned name = s.read2(); + unsigned spec = s.read2(); + + if (DebugClassReader) { + fprintf(stderr, + " method flags %d name %d spec %d '%s%s'\n", + flags, + name, + spec, + cast(t, singletonObject(t, pool, name - 1)) + ->body() + .begin(), + cast(t, singletonObject(t, pool, spec - 1)) + ->body() + .begin()); + } + + addendum = 0; + code = 0; + + unsigned attributeCount = s.read2(); + for (unsigned j = 0; j < attributeCount; ++j) { + GcByteArray* attributeName + = cast(t, singletonObject(t, pool, s.read2() - 1)); + unsigned length = s.read4(); + + if (vm::strcmp(reinterpret_cast("Code"), + attributeName->body().begin()) == 0) { + code = parseCode(t, s, pool); + } else if (vm::strcmp(reinterpret_cast("Exceptions"), + attributeName->body().begin()) == 0) { + if (addendum == 0) { + addendum = makeMethodAddendum(t, pool, 0, 0, 0, 0, 0); + } + unsigned exceptionCount = s.read2(); + GcShortArray* body = makeShortArray(t, exceptionCount); + for (unsigned i = 0; i < exceptionCount; ++i) { + body->body()[i] = s.read2(); + } + addendum->setExceptionTable(t, body); + } else if (vm::strcmp( + reinterpret_cast("AnnotationDefault"), + attributeName->body().begin()) == 0) { + if (addendum == 0) { + addendum = makeMethodAddendum(t, pool, 0, 0, 0, 0, 0); + } + + GcByteArray* body = makeByteArray(t, length); + s.read(reinterpret_cast(body->body().begin()), length); + + addendum->setAnnotationDefault(t, body); + } else if (vm::strcmp(reinterpret_cast("Signature"), + attributeName->body().begin()) == 0) { + if (addendum == 0) { + addendum = makeMethodAddendum(t, pool, 0, 0, 0, 0, 0); + } + + addendum->setSignature(t, singletonObject(t, pool, s.read2() - 1)); + } else if (vm::strcmp(reinterpret_cast( + "RuntimeVisibleAnnotations"), + attributeName->body().begin()) == 0) { + if (addendum == 0) { + addendum = makeMethodAddendum(t, pool, 0, 0, 0, 0, 0); + } + + GcByteArray* body = makeByteArray(t, length); + s.read(reinterpret_cast(body->body().begin()), length); + + addendum->setAnnotationTable(t, body); + } else if (vm::strcmp(reinterpret_cast( + "RuntimeVisibleParameterAnnotations"), + attributeName->body().begin()) == 0) { + if (addendum == 0) { + addendum = makeMethodAddendum(t, pool, 0, 0, 0, 0, 0); + } + + GcByteArray* body = makeByteArray(t, length); + s.read(reinterpret_cast(body->body().begin()), length); + + addendum->setParameterAnnotationTable(t, body); + } else { + s.skip(length); + } + } + + const char* specString = reinterpret_cast( + cast(t, singletonObject(t, pool, spec - 1)) + ->body() + .begin()); + + unsigned parameterCount; + unsigned parameterFootprint; + unsigned returnCode; + scanMethodSpec(t, + specString, + flags & ACC_STATIC, + ¶meterCount, + ¶meterFootprint, + &returnCode); + + GcMethod* method = t->m->processor->makeMethod( + t, + 0, // vm flags + returnCode, + parameterCount, + parameterFootprint, + flags, + 0, // offset + cast(t, singletonObject(t, pool, name - 1)), + cast(t, singletonObject(t, pool, spec - 1)), + addendum, + class_, + code); + + PROTECT(t, method); + + if (methodVirtual(t, method)) { + ++declaredVirtualCount; + + GcTriple* p + = hashMapFindNode(t, virtualMap, method, methodHash, methodEqual); + + if (p) { + method->offset() = cast(t, p->first())->offset(); + + p->setSecond(t, method); + } else { + method->offset() = virtualCount++; + + listAppend(t, newVirtuals, method); + + hashMapInsert(t, virtualMap, method, method, methodHash); + } + + if (UNLIKELY((class_->flags() & ACC_INTERFACE) == 0 + and vm::strcmp(reinterpret_cast("finalize"), + method->name()->body().begin()) == 0 + and vm::strcmp(reinterpret_cast("()V"), + method->spec()->body().begin()) == 0 + and (not emptyMethod(t, method)))) { + class_->vmFlags() |= HasFinalizerFlag; + } + } else { + method->offset() = i; + + if (vm::strcmp(reinterpret_cast(""), + method->name()->body().begin()) == 0) { + method->vmFlags() |= ClassInitFlag; + class_->vmFlags() |= NeedInitFlag; + } else if (vm::strcmp(reinterpret_cast(""), + method->name()->body().begin()) == 0) { + method->vmFlags() |= ConstructorFlag; + } + } + + methodTable->setBodyElement(t, i, method); + } + + class_->setMethodTable(t, methodTable); + } + + GcList* abstractVirtuals + = addInterfaceMethods(t, class_, virtualMap, &virtualCount, true); + + PROTECT(t, abstractVirtuals); + + bool populateInterfaceVtables = false; + + if (declaredVirtualCount == 0 and abstractVirtuals == 0 + and (class_->flags() & ACC_INTERFACE) == 0) { + if (class_->super()) { + // inherit virtual table from superclass + class_->setVirtualTable(t, superVirtualTable); + + if (class_->super()->interfaceTable() + and cast(t, class_->interfaceTable())->length() + == cast(t, class_->super()->interfaceTable()) + ->length()) { + // inherit interface table from superclass + class_->setInterfaceTable(t, class_->super()->interfaceTable()); + } else { + populateInterfaceVtables = true; + } + } else { + // apparently, Object does not have any virtual methods. We + // give it a vtable anyway so code doesn't break elsewhere. + GcArray* vtable = makeArray(t, 0); + class_->setVirtualTable(t, vtable); + } + } else if (virtualCount) { + // generate class vtable + + GcArray* vtable = makeArray(t, virtualCount); + + unsigned i = 0; + if (class_->flags() & ACC_INTERFACE) { + PROTECT(t, vtable); + + for (HashMapIterator it(t, virtualMap); it.hasMore();) { + GcMethod* method = cast(t, it.next()->first()); + assertT(t, vtable->body()[method->offset()] == 0); + vtable->setBodyElement(t, method->offset(), method); + ++i; + } + } else { + populateInterfaceVtables = true; + + if (superVirtualTable) { + for (; i < superVirtualTable->length(); ++i) { + object method = superVirtualTable->body()[i]; + method = hashMapFind(t, virtualMap, method, methodHash, methodEqual); + + vtable->setBodyElement(t, i, method); + } + } + + for (GcPair* p = cast(t, newVirtuals->front()); p; + p = cast(t, p->second())) { + vtable->setBodyElement(t, i, p->first()); + ++i; + } + } + + if (abstractVirtuals) { + PROTECT(t, vtable); + + object originalMethodTable = class_->methodTable(); + PROTECT(t, originalMethodTable); + + unsigned oldLength + = class_->methodTable() + ? cast(t, class_->methodTable())->length() + : 0; + + GcClassAddendum* addendum = getClassAddendum(t, class_, pool); + addendum->declaredMethodCount() = oldLength; + + GcArray* newMethodTable + = makeArray(t, oldLength + abstractVirtuals->size()); + + if (oldLength) { + GcArray* mtable = cast(t, class_->methodTable()); + for (size_t i = 0; i < oldLength; i++) { + newMethodTable->setBodyElement(t, i, mtable->body()[i]); + } + } + + mark(t, newMethodTable, ArrayBody, oldLength); + + unsigned mti = oldLength; + for (GcPair* p = cast(t, abstractVirtuals->front()); p; + p = cast(t, p->second())) { + newMethodTable->setBodyElement(t, mti++, p->first()); + + if ((class_->flags() & ACC_INTERFACE) == 0) { + vtable->setBodyElement(t, i++, p->first()); + } + } + + assertT(t, newMethodTable->length() == mti); + + class_->setMethodTable(t, newMethodTable); + } + + assertT(t, vtable->length() == i); + + class_->setVirtualTable(t, vtable); + } + + if (populateInterfaceVtables) { + // generate interface vtables + GcArray* itable = cast(t, class_->interfaceTable()); + if (itable) { + PROTECT(t, itable); + + for (unsigned i = 0; i < itable->length(); i += 2) { + GcArray* ivtable = cast( + t, cast(t, itable->body()[i])->virtualTable()); + if (ivtable) { + GcArray* vtable = cast(t, itable->body()[i + 1]); + + for (unsigned j = 0; j < ivtable->length(); ++j) { + object method = ivtable->body()[j]; + method + = hashMapFind(t, virtualMap, method, methodHash, methodEqual); + assertT(t, method); + + vtable->setBodyElement(t, j, method); + } + } + } + } + } +} + +void parseAttributeTable(Thread* t, + Stream& s, + GcClass* class_, + GcSingleton* pool) +{ + PROTECT(t, class_); + PROTECT(t, pool); + + unsigned attributeCount = s.read2(); + for (unsigned j = 0; j < attributeCount; ++j) { + GcByteArray* name + = cast(t, singletonObject(t, pool, s.read2() - 1)); + unsigned length = s.read4(); + + if (vm::strcmp(reinterpret_cast("SourceFile"), + name->body().begin()) == 0) { + class_->setSourceFile( + t, cast(t, singletonObject(t, pool, s.read2() - 1))); + } else if (vm::strcmp(reinterpret_cast("Signature"), + name->body().begin()) == 0) { + GcClassAddendum* addendum = getClassAddendum(t, class_, pool); + addendum->setSignature(t, singletonObject(t, pool, s.read2() - 1)); + } else if (vm::strcmp(reinterpret_cast("InnerClasses"), + name->body().begin()) == 0) { + unsigned innerClassCount = s.read2(); + GcArray* table = makeArray(t, innerClassCount); + PROTECT(t, table); + + for (unsigned i = 0; i < innerClassCount; ++i) { + int16_t inner = s.read2(); + int16_t outer = s.read2(); + int16_t name = s.read2(); + int16_t flags = s.read2(); + + GcInnerClassReference* reference = makeInnerClassReference( + t, + inner + ? cast(t, singletonObject(t, pool, inner - 1)) + ->name() + : 0, + outer + ? cast(t, singletonObject(t, pool, outer - 1)) + ->name() + : 0, + name ? cast(t, singletonObject(t, pool, name - 1)) : 0, + flags); + + table->setBodyElement(t, i, reference); + } + + GcClassAddendum* addendum = getClassAddendum(t, class_, pool); + addendum->setInnerClassTable(t, table); + } else if (vm::strcmp( + reinterpret_cast("RuntimeVisibleAnnotations"), + name->body().begin()) == 0) { + GcByteArray* body = makeByteArray(t, length); + PROTECT(t, body); + s.read(reinterpret_cast(body->body().begin()), length); + + GcClassAddendum* addendum = getClassAddendum(t, class_, pool); + addendum->setAnnotationTable(t, body); + } else if (vm::strcmp(reinterpret_cast("BootstrapMethods"), + name->body().begin()) == 0) { + unsigned count = s.read2(); + GcArray* array = makeArray(t, count); + PROTECT(t, array); + + for (unsigned i = 0; i < count; ++i) { + unsigned reference = s.read2() - 1; + unsigned argumentCount = s.read2(); + GcCharArray* element = makeCharArray(t, 1 + argumentCount); + element->body()[0] = reference; + for (unsigned ai = 0; ai < argumentCount; ++ai) { + element->body()[1 + ai] = s.read2() - 1; + } + array->setBodyElement(t, i, element); + } + + GcClassAddendum* addendum = getClassAddendum(t, class_, pool); + addendum->setBootstrapMethodTable(t, array); + } else if (vm::strcmp(reinterpret_cast("EnclosingMethod"), + name->body().begin()) == 0) { + int16_t enclosingClass = s.read2(); + int16_t enclosingMethod = s.read2(); + + GcClassAddendum* addendum = getClassAddendum(t, class_, pool); + + addendum->setEnclosingClass( + t, + cast(t, singletonObject(t, pool, enclosingClass - 1)) + ->name()); + + addendum->setEnclosingMethod( + t, + enclosingMethod + ? cast(t, singletonObject(t, pool, enclosingMethod - 1)) + : 0); + } else { + s.skip(length); + } + } +} + +void updateClassTables(Thread* t, GcClass* newClass, GcClass* oldClass) +{ + GcArray* fieldTable = cast(t, newClass->fieldTable()); + if (fieldTable) { + for (unsigned i = 0; i < fieldTable->length(); ++i) { + cast(t, fieldTable->body()[i])->setClass(t, newClass); + } + } + + GcSingleton* staticTable = newClass->staticTable(); + if (staticTable) { + staticTable->setBodyElement(t, 0, reinterpret_cast(newClass)); + } + + if (newClass->flags() & ACC_INTERFACE) { + GcArray* virtualTable = cast(t, newClass->virtualTable()); + if (virtualTable) { + for (unsigned i = 0; i < virtualTable->length(); ++i) { + GcMethod* m = cast(t, virtualTable->body()[i]); + if (m->class_() == oldClass) { + m->setClass(t, newClass); + } + } + } + } + + GcArray* methodTable = cast(t, newClass->methodTable()); + if (methodTable) { + for (unsigned i = 0; i < methodTable->length(); ++i) { + cast(t, methodTable->body()[i])->setClass(t, newClass); + } + } +} + +void updateBootstrapClass(Thread* t, GcClass* bootstrapClass, GcClass* class_) +{ + expect(t, bootstrapClass != class_); + + // verify that the classes have the same layout + expect(t, bootstrapClass->super() == class_->super()); + + expect(t, bootstrapClass->fixedSize() >= class_->fixedSize()); + + expect(t, (class_->vmFlags() & HasFinalizerFlag) == 0); + + PROTECT(t, bootstrapClass); + PROTECT(t, class_); + + ENTER(t, Thread::ExclusiveState); + + bootstrapClass->vmFlags() &= ~BootstrapFlag; + bootstrapClass->vmFlags() |= class_->vmFlags(); + bootstrapClass->flags() |= class_->flags(); + + bootstrapClass->setArrayElementClass(t, class_->arrayElementClass()); + bootstrapClass->setSuper(t, class_->super()); + bootstrapClass->setInterfaceTable(t, class_->interfaceTable()); + bootstrapClass->setVirtualTable(t, class_->virtualTable()); + bootstrapClass->setFieldTable(t, class_->fieldTable()); + bootstrapClass->setMethodTable(t, class_->methodTable()); + bootstrapClass->setStaticTable(t, class_->staticTable()); + bootstrapClass->setAddendum(t, class_->addendum()); + + updateClassTables(t, bootstrapClass, class_); +} + +GcClass* makeArrayClass(Thread* t, + GcClassLoader* loader, + unsigned dimensions, + GcByteArray* spec, + GcClass* elementClass) +{ + if (type(t, GcJobject::Type)->vmFlags() & BootstrapFlag) { + PROTECT(t, loader); + PROTECT(t, spec); + PROTECT(t, elementClass); + + // Load java.lang.Object if present so we can use its vtable, but + // don't throw an exception if we can't find it. This way, we + // avoid infinite recursion due to trying to create an array to + // make a stack trace for a ClassNotFoundException. + resolveSystemClass( + t, roots(t)->bootLoader(), type(t, GcJobject::Type)->name(), false); + } + + GcArray* vtable = cast(t, type(t, GcJobject::Type)->virtualTable()); + + // From JDK docs: for array classes the public, private, protected modifiers are the same as + // the underlying type, and the final modifier is always set. Testing on OpenJDK shows that + // ACC_ABSTRACT is also set on array classes. + int flags = elementClass->flags() & (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED); + flags |= ACC_FINAL; + flags |= ACC_ABSTRACT; + + GcClass* c = t->m->processor->makeClass(t, + flags, + 0, + 2 * BytesPerWord, + BytesPerWord, + dimensions, + elementClass, + type(t, GcArray::Type)->objectMask(), + spec, + 0, + type(t, GcJobject::Type), + roots(t)->arrayInterfaceTable(), + vtable, + 0, + 0, + 0, + 0, + loader, + vtable->length()); + + PROTECT(t, c); + + t->m->processor->initVtable(t, c); + + return c; +} + +void saveLoadedClass(Thread* t, GcClassLoader* loader, GcClass* c) +{ + PROTECT(t, loader); + PROTECT(t, c); + + ACQUIRE(t, t->m->classLock); + + if (loader->map() == 0) { + GcHashMap* map = makeHashMap(t, 0, 0); + loader->setMap(t, map); + } + + hashMapInsert( + t, cast(t, loader->map()), c->name(), c, byteArrayHash); +} + +GcClass* makeArrayClass(Thread* t, + GcClassLoader* loader, + GcByteArray* spec, + bool throw_, + Gc::Type throwType) +{ + PROTECT(t, loader); + PROTECT(t, spec); + + const char* s = reinterpret_cast(spec->body().begin()); + const char* start = s; + unsigned dimensions = 0; + for (; *s == '['; ++s) + ++dimensions; + + GcByteArray* elementSpec; + switch (*s) { + case 'L': { + ++s; + const char* elementSpecStart = s; + while (*s and *s != ';') + ++s; + if (dimensions > 1) { + elementSpecStart -= dimensions; + ++s; + } + + elementSpec = makeByteArray(t, s - elementSpecStart + 1); + memcpy(elementSpec->body().begin(), + &spec->body()[elementSpecStart - start], + s - elementSpecStart); + elementSpec->body()[s - elementSpecStart] = 0; + } break; + + default: + if (dimensions > 1) { + char c = *s; + elementSpec = makeByteArray(t, dimensions + 1); + unsigned i; + for (i = 0; i < dimensions - 1; ++i) { + elementSpec->body()[i] = '['; + } + elementSpec->body()[i++] = c; + elementSpec->body()[i] = 0; + --dimensions; + } else { + abort(t); + } + } + + GcClass* elementClass + = cast(t, + hashMapFind(t, + roots(t)->bootstrapClassMap(), + elementSpec, + byteArrayHash, + byteArrayEqual)); + + if (elementClass == 0) { + elementClass = resolveClass(t, loader, elementSpec, throw_, throwType); + if (elementClass == 0) + return 0; + } + + PROTECT(t, elementClass); + + ACQUIRE(t, t->m->classLock); + + GcClass* class_ = findLoadedClass(t, elementClass->loader(), spec); + if (class_) { + return class_; + } + + class_ = makeArrayClass( + t, elementClass->loader(), dimensions, spec, elementClass); + + PROTECT(t, class_); + + saveLoadedClass(t, elementClass->loader(), class_); + + return class_; +} + +GcClass* resolveArrayClass(Thread* t, + GcClassLoader* loader, + GcByteArray* spec, + bool throw_, + Gc::Type throwType) +{ + GcClass* c = cast(t, + hashMapFind(t, + roots(t)->bootstrapClassMap(), + spec, + byteArrayHash, + byteArrayEqual)); + + if (c) { + c->setVirtualTable(t, type(t, GcJobject::Type)->virtualTable()); + + return c; + } else { + PROTECT(t, loader); + PROTECT(t, spec); + + c = findLoadedClass(t, roots(t)->bootLoader(), spec); + + if (c) { + return c; + } else { + return makeArrayClass(t, loader, spec, throw_, throwType); + } + } +} + +void removeMonitor(Thread* t, object o) +{ + unsigned hash; + if (DebugMonitors) { + hash = objectHash(t, o); + } + + object m + = hashMapRemove(t, roots(t)->monitorMap(), o, objectHash, objectEqual); + + if (DebugMonitors) { + fprintf(stderr, "dispose monitor %p for object %x\n", m, hash); + } +} + +void removeString(Thread* t, object o) +{ + hashMapRemove(t, roots(t)->stringMap(), o, stringHash, objectEqual); +} + +void bootClass(Thread* t, + Gc::Type type, + int superType, + uint32_t* objectMask, + unsigned fixedSize, + unsigned arrayElementSize, + unsigned vtableLength) +{ + GcClass* super + = (superType >= 0 ? vm::type(t, static_cast(superType)) : 0); + + unsigned maskSize + = ceilingDivide(fixedSize + arrayElementSize, 32 * BytesPerWord); + + GcIntArray* mask; + if (objectMask) { + if (super and super->objectMask() + and super->objectMask()->length() == maskSize + and memcmp(super->objectMask()->body().begin(), + objectMask, + sizeof(uint32_t) * maskSize) == 0) { + mask = vm::type(t, static_cast(superType))->objectMask(); + } else { + mask = makeIntArray(t, maskSize); + memcpy(mask->body().begin(), objectMask, sizeof(uint32_t) * maskSize); + } + } else { + mask = 0; + } + + int flags = 0; + switch(type) { + case Gc::JbyteType: + case Gc::JintType: + case Gc::JshortType: + case Gc::JlongType: + case Gc::JbooleanType: + case Gc::JcharType: + case Gc::JfloatType: + case Gc::JdoubleType: + + case Gc::ByteArrayType: + case Gc::IntArrayType: + case Gc::ShortArrayType: + case Gc::LongArrayType: + case Gc::BooleanArrayType: + case Gc::CharArrayType: + case Gc::FloatArrayType: + case Gc::DoubleArrayType: + // Primitive and array types are final, abstract and public. + flags = ACC_FINAL | ACC_ABSTRACT | ACC_PUBLIC; + default: + break; + } + + super = (superType >= 0 ? vm::type(t, static_cast(superType)) : 0); + + GcClass* class_ = t->m->processor->makeClass(t, + flags, + BootstrapFlag, + fixedSize, + arrayElementSize, + arrayElementSize ? 1 : 0, + 0, + mask, + 0, + 0, + super, + 0, + 0, + 0, + 0, + 0, + 0, + roots(t)->bootLoader(), + vtableLength); + + setType(t, type, class_); +} + +void bootJavaClass(Thread* t, + Gc::Type type, + int superType, + const char* name, + int vtableLength, + object bootMethod) +{ + PROTECT(t, bootMethod); + + GcByteArray* n = makeByteArray(t, name); + PROTECT(t, n); + + GcClass* class_ = vm::type(t, type); + PROTECT(t, class_); + + class_->setName(t, n); + + GcArray* vtable; + if (vtableLength >= 0) { + vtable = makeArray(t, vtableLength); + for (int i = 0; i < vtableLength; ++i) { + vtable->setBodyElement(t, i, bootMethod); + } + } else { + vtable = cast( + t, vm::type(t, static_cast(superType))->virtualTable()); + } + + class_->setVirtualTable(t, vtable); + + t->m->processor->initVtable(t, class_); + + hashMapInsert(t, roots(t)->bootstrapClassMap(), n, class_, byteArrayHash); +} + +void nameClass(Thread* t, Gc::Type type, const char* name) +{ + GcByteArray* n = makeByteArray(t, name); + cast(t, t->m->types->body()[type])->setName(t, n); +} + +void makeArrayInterfaceTable(Thread* t) +{ + GcArray* interfaceTable = makeArray(t, 4); + + interfaceTable->setBodyElement(t, 0, type(t, GcSerializable::Type)); + + interfaceTable->setBodyElement(t, 2, type(t, GcCloneable::Type)); + + roots(t)->setArrayInterfaceTable(t, interfaceTable); +} + +void boot(Thread* t) +{ + Machine* m = t->m; + + m->unsafe = true; + + m->roots = reinterpret_cast(allocate(t, GcRoots::FixedSize, true)); + + object classLoader = allocate(t, GcSystemClassLoader::FixedSize, true); + // sequence point, for gc (don't recombine statements) + roots(t)->setBootLoader(t, reinterpret_cast(classLoader)); + + classLoader = allocate(t, GcSystemClassLoader::FixedSize, true); + // sequence point, for gc (don't recombine statements) + roots(t)->setAppLoader(t, reinterpret_cast(classLoader)); + + m->types = reinterpret_cast( + allocate(t, pad((TypeCount + 2) * BytesPerWord), true)); + m->types->length() = TypeCount; + +#include "type-initializations.cpp" + + GcClass* arrayClass = type(t, GcArray::Type); + setField(t, m->types, 0, arrayClass); + + GcClass* rootsClass = type(t, GcRoots::Type); + setField(t, m->roots, 0, rootsClass); + + GcClass* loaderClass = type(t, GcSystemClassLoader::Type); + setField(t, roots(t)->bootLoader(), 0, loaderClass); + setField(t, roots(t)->appLoader(), 0, loaderClass); + + GcClass* objectClass = type(t, GcJobject::Type); + + GcClass* classClass = type(t, GcClass::Type); + setField(t, classClass, 0, classClass); + classClass->setSuper(t, objectClass); + + GcClass* intArrayClass = type(t, GcIntArray::Type); + setField(t, intArrayClass, 0, classClass); + intArrayClass->setSuper(t, objectClass); + + m->unsafe = false; + + type(t, GcSingleton::Type)->vmFlags() |= SingletonFlag; + + type(t, GcContinuation::Type)->vmFlags() |= ContinuationFlag; + + type(t, GcJreference::Type)->vmFlags() |= ReferenceFlag; + type(t, GcWeakReference::Type)->vmFlags() |= ReferenceFlag + | WeakReferenceFlag; + type(t, GcSoftReference::Type)->vmFlags() |= ReferenceFlag + | WeakReferenceFlag; + type(t, GcPhantomReference::Type)->vmFlags() |= ReferenceFlag + | WeakReferenceFlag; + + type(t, GcJboolean::Type)->vmFlags() |= PrimitiveFlag; + type(t, GcJbyte::Type)->vmFlags() |= PrimitiveFlag; + type(t, GcJchar::Type)->vmFlags() |= PrimitiveFlag; + type(t, GcJshort::Type)->vmFlags() |= PrimitiveFlag; + type(t, GcJint::Type)->vmFlags() |= PrimitiveFlag; + type(t, GcJlong::Type)->vmFlags() |= PrimitiveFlag; + type(t, GcJfloat::Type)->vmFlags() |= PrimitiveFlag; + type(t, GcJdouble::Type)->vmFlags() |= PrimitiveFlag; + type(t, GcJvoid::Type)->vmFlags() |= PrimitiveFlag; + + type(t, GcBooleanArray::Type) + ->setArrayElementClass(t, type(t, GcJboolean::Type)); + type(t, GcByteArray::Type)->setArrayElementClass(t, type(t, GcJbyte::Type)); + type(t, GcCharArray::Type)->setArrayElementClass(t, type(t, GcJchar::Type)); + type(t, GcShortArray::Type)->setArrayElementClass(t, type(t, GcJshort::Type)); + type(t, GcIntArray::Type)->setArrayElementClass(t, type(t, GcJint::Type)); + type(t, GcLongArray::Type)->setArrayElementClass(t, type(t, GcJlong::Type)); + type(t, GcFloatArray::Type)->setArrayElementClass(t, type(t, GcJfloat::Type)); + type(t, GcDoubleArray::Type) + ->setArrayElementClass(t, type(t, GcJdouble::Type)); + + { + GcHashMap* map = makeHashMap(t, 0, 0); + roots(t)->bootLoader()->setMap(t, map); + } + + roots(t)->bootLoader()->as(t)->finder() = m->bootFinder; + + { + GcHashMap* map = makeHashMap(t, 0, 0); + roots(t)->appLoader()->setMap(t, map); + } + + roots(t)->appLoader()->as(t)->finder() = m->appFinder; + + roots(t)->appLoader()->setParent(t, roots(t)->bootLoader()); + + { + GcHashMap* map = makeHashMap(t, 0, 0); + // sequence point, for gc (don't recombine statements) + roots(t)->setBootstrapClassMap(t, map); + } + + { + GcWeakHashMap* map = makeWeakHashMap(t, 0, 0); + // sequence point, for gc (don't recombine statements) + roots(t)->setStringMap(t, map->as(t)); + } + + makeArrayInterfaceTable(t); + + type(t, GcBooleanArray::Type) + ->setInterfaceTable(t, roots(t)->arrayInterfaceTable()); + type(t, GcByteArray::Type) + ->setInterfaceTable(t, roots(t)->arrayInterfaceTable()); + type(t, GcCharArray::Type) + ->setInterfaceTable(t, roots(t)->arrayInterfaceTable()); + type(t, GcShortArray::Type) + ->setInterfaceTable(t, roots(t)->arrayInterfaceTable()); + type(t, GcIntArray::Type) + ->setInterfaceTable(t, roots(t)->arrayInterfaceTable()); + type(t, GcLongArray::Type) + ->setInterfaceTable(t, roots(t)->arrayInterfaceTable()); + type(t, GcFloatArray::Type) + ->setInterfaceTable(t, roots(t)->arrayInterfaceTable()); + type(t, GcDoubleArray::Type) + ->setInterfaceTable(t, roots(t)->arrayInterfaceTable()); + + m->processor->boot(t, 0, 0); + + { + GcCode* bootCode = makeCode(t, 0, 0, 0, 0, 0, 0, 0, 0, 1); + bootCode->body()[0] = impdep1; + object bootMethod + = makeMethod(t, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, bootCode); + PROTECT(t, bootMethod); + +# include "type-java-initializations.cpp" +# include "type-name-initializations.cpp" + + } + +} + +class HeapClient : public Heap::Client { + public: + HeapClient(Machine* m) : m(m) + { + } + + virtual void visitRoots(Heap::Visitor* v) + { + ::visitRoots(m, v); + + postVisit(m->rootThread, v); + } + + virtual void collect(void* context, Heap::CollectionType type) + { + collect(static_cast(context), type); + } + + virtual bool isFixed(void* p) + { + return objectFixed(m->rootThread, static_cast(p)); + } + + virtual unsigned sizeInWords(void* p) + { + Thread* t = m->rootThread; + + object o = static_cast(m->heap->follow(maskAlignedPointer(p))); + + unsigned n = baseSize(t, o, m->heap->follow(objectClass(t, o))); + + if (objectExtended(t, o)) { + ++n; + } + + return n; + } + + virtual unsigned copiedSizeInWords(void* p) + { + Thread* t = m->rootThread; + + object o = static_cast(m->heap->follow(maskAlignedPointer(p))); + assertT(t, not objectFixed(t, o)); + + unsigned n = baseSize(t, o, m->heap->follow(objectClass(t, o))); + + if (objectExtended(t, o) or hashTaken(t, o)) { + ++n; + } + + return n; + } + + virtual void copy(void* srcp, void* dstp) + { + Thread* t = m->rootThread; + + object src = static_cast(m->heap->follow(maskAlignedPointer(srcp))); + assertT(t, not objectFixed(t, src)); + + GcClass* class_ = m->heap->follow(objectClass(t, src)); + + unsigned base = baseSize(t, src, class_); + unsigned n = extendedSize(t, src, base); + + object dst = static_cast(dstp); + + memcpy(dst, src, n * BytesPerWord); + + if (hashTaken(t, src)) { + alias(dst, 0) &= PointerMask; + alias(dst, 0) |= ExtendedMark; + extendedWord(t, dst, base) = takeHash(t, src); + } + } + + virtual void walk(void* p, Heap::Walker* w) + { + object o = static_cast(m->heap->follow(maskAlignedPointer(p))); + ::walk(m->rootThread, w, o, 0); + } + + void dispose() + { + m->heap->free(this, sizeof(*this)); + } + + private: + Machine* m; +}; + +void doCollect(Thread* t, Heap::CollectionType type, int pendingAllocation) +{ + expect(t, not t->m->collecting); + + t->m->collecting = true; + THREAD_RESOURCE0(t, t->m->collecting = false); + +#ifdef VM_STRESS + bool stress = (t->getFlags() & Thread::StressFlag) != 0; + if (not stress) + t->setFlag(Thread::StressFlag); +#endif + + Machine* m = t->m; + + m->unsafe = true; + m->heap->collect( + type, + footprint(m->rootThread), + pendingAllocation - (t->m->heapPoolIndex * ThreadHeapSizeInWords)); + m->unsafe = false; + + postCollect(m->rootThread); + + killZombies(t, m->rootThread); + + for (unsigned i = 0; i < m->heapPoolIndex; ++i) { + m->heap->free(m->heapPool[i], ThreadHeapSizeInBytes); + } + m->heapPoolIndex = 0; + + if (m->heap->limitExceeded()) { + // if we're out of memory, disallow further allocations of fixed + // objects: + m->fixedFootprint = FixedFootprintThresholdInBytes; + } else { + m->fixedFootprint = 0; + } + +#ifdef VM_STRESS + if (not stress) + t->clearFlag(Thread::StressFlag); +#endif + + GcFinalizer* finalizeQueue = t->m->finalizeQueue; + t->m->finalizeQueue = 0; + for (; finalizeQueue; + finalizeQueue = cast(t, finalizeQueue->next())) { + void (*function)(Thread*, object); + memcpy(&function, &finalizeQueue->finalize(), BytesPerWord); + function(t, finalizeQueue->target()); + } + +#ifndef SGX + if ((roots(t)->objectsToFinalize() or roots(t)->objectsToClean()) + and m->finalizeThread == 0 and t->state != Thread::ExitState) { + m->finalizeThread = m->processor->makeThread( + m, roots(t)->finalizerThread(), m->rootThread); + + addThread(t, m->finalizeThread); + + if (not startThread(t, m->finalizeThread)) { + removeThread(t, m->finalizeThread); + m->finalizeThread = 0; + } + } +#endif +} + +uint64_t invokeLoadClass(Thread* t, uintptr_t* arguments) +{ + GcMethod* method = cast(t, reinterpret_cast(arguments[0])); + object loader = reinterpret_cast(arguments[1]); + object specString = reinterpret_cast(arguments[2]); + + return reinterpret_cast( + t->m->processor->invoke(t, method, loader, specString)); +} + +bool isInitializing(Thread* t, GcClass* c) +{ + for (Thread::ClassInitStack* s = t->classInitStack; s; s = s->next) { + if (s->class_ == c) { + return true; + } + } + return false; +} + +object findInTable(Thread* t, + GcArray* table, + GcByteArray* name, + GcByteArray* spec, + GcByteArray* (*getName)(Thread*, object), + GcByteArray* (*getSpec)(Thread*, object)) +{ + if (table) { + for (unsigned i = 0; i < table->length(); ++i) { + object o = table->body()[i]; + if (vm::strcmp(getName(t, o)->body().begin(), name->body().begin()) == 0 + and vm::strcmp(getSpec(t, o)->body().begin(), spec->body().begin()) + == 0) { + return o; + } + } + + if (false) { + fprintf( + stderr, "%s %s not in\n", name->body().begin(), spec->body().begin()); + + for (unsigned i = 0; i < table->length(); ++i) { + object o = table->body()[i]; + fprintf(stderr, + "\t%s %s\n", + getName(t, o)->body().begin(), + getSpec(t, o)->body().begin()); + } + } + } + + return 0; +} + +void updatePackageMap(Thread* t, GcClass* class_) +{ + PROTECT(t, class_); + + if (roots(t)->packageMap() == 0) { + GcHashMap* map = makeHashMap(t, 0, 0); + // sequence point, for gc (don't recombine statements) + roots(t)->setPackageMap(t, map); + } + + GcByteArray* className = class_->name(); + if ('[' != className->body()[0]) { + THREAD_RUNTIME_ARRAY(t, char, packageName, className->length()); + + char* s = reinterpret_cast(className->body().begin()); + char* p = strrchr(s, '/'); + + if (p) { + int length = (p - s) + 1; + memcpy( + RUNTIME_ARRAY_BODY(packageName), className->body().begin(), length); + RUNTIME_ARRAY_BODY(packageName)[length] = 0; + + GcByteArray* key + = vm::makeByteArray(t, "%s", RUNTIME_ARRAY_BODY(packageName)); + PROTECT(t, key); + + hashMapRemove( + t, roots(t)->packageMap(), key, byteArrayHash, byteArrayEqual); + + GcByteArray* source = class_->source(); + if (source) { + // note that we strip the "file:" prefix, since OpenJDK's + // Package.defineSystemPackage expects an unadorned filename: + const unsigned PrefixLength = 5; + unsigned sourceNameLength = source->length() - PrefixLength; + THREAD_RUNTIME_ARRAY(t, char, sourceName, sourceNameLength); + memcpy(RUNTIME_ARRAY_BODY(sourceName), + &source->body()[PrefixLength], + sourceNameLength); + + source = vm::makeByteArray(t, "%s", RUNTIME_ARRAY_BODY(sourceName)); + } else { + source = vm::makeByteArray(t, "avian-dummy-package-source"); + } + + hashMapInsert(t, roots(t)->packageMap(), key, source, byteArrayHash); + } + } +} + +} // namespace + +namespace vm { + +Machine::Machine(System* system, + Heap* heap, + Finder* bootFinder, + Finder* appFinder, + Processor* processor, + Classpath* classpath, + const char** properties, + unsigned propertyCount, + const char** arguments, + unsigned argumentCount, + unsigned stackSizeInBytes) + : vtable(&javaVMVTable), + system(system), + heapClient(new (heap->allocate(sizeof(HeapClient))) HeapClient(this)), + heap(heap), + bootFinder(bootFinder), + appFinder(appFinder), + processor(processor), + classpath(classpath), + rootThread(0), + exclusive(0), + finalizeThread(0), + jniReferences(0), + propertyCount(propertyCount), + arguments(arguments), + argumentCount(argumentCount), + threadCount(0), + activeCount(0), + liveCount(0), + daemonCount(0), + fixedFootprint(0), + stackSizeInBytes(stackSizeInBytes), + localThread(0), + stateLock(0), + heapLock(0), + classLock(0), + referenceLock(0), + shutdownLock(0), + libraries(0), + errorLog(0), + bootimage(0), + types(0), + roots(0), + finalizers(0), + tenuredFinalizers(0), + finalizeQueue(0), + weakReferences(0), + tenuredWeakReferences(0), + unsafe(false), + collecting(false), + triedBuiltinOnLoad(false), + dumpedHeapOnOOM(false), + alive(true), + heapPoolIndex(0) +{ + heap->setClient(heapClient); + + populateJNITables(&javaVMVTable, &jniEnvVTable); + + // Copying the properties memory (to avoid memory crashes) + this->properties = (char**)heap->allocate(sizeof(char*) * propertyCount); + for (unsigned int i = 0; i < propertyCount; i++) { + size_t length = strlen(properties[i]) + 1; // +1 for null-terminating char + this->properties[i] = (char*)heap->allocate(sizeof(char) * length); + memcpy(this->properties[i], properties[i], length); + } + + const char* bootstrapProperty = findProperty(this, BOOTSTRAP_PROPERTY); + const char* bootstrapPropertyDup + = bootstrapProperty ? strdup(bootstrapProperty) : 0; + const char* bootstrapPropertyEnd + = bootstrapPropertyDup + + (bootstrapPropertyDup ? strlen(bootstrapPropertyDup) : 0); + char* codeLibraryName = (char*)bootstrapPropertyDup; + char* codeLibraryNameEnd = 0; + if (codeLibraryName && (codeLibraryNameEnd + = strchr(codeLibraryName, system->pathSeparator()))) + *codeLibraryNameEnd = 0; + + if (not system->success(system->make(&localThread)) + or not system->success(system->make(&stateLock)) + or not system->success(system->make(&heapLock)) + or not system->success(system->make(&classLock)) + or not system->success(system->make(&referenceLock)) + or not system->success(system->make(&shutdownLock)) + or not system->success(system->load(&libraries, bootstrapPropertyDup))) { + system->abort(); + } + + System::Library* additionalLibrary = 0; + while (codeLibraryNameEnd && codeLibraryNameEnd + 1 < bootstrapPropertyEnd) { + codeLibraryName = codeLibraryNameEnd + 1; + codeLibraryNameEnd = strchr(codeLibraryName, system->pathSeparator()); + if (codeLibraryNameEnd) + *codeLibraryNameEnd = 0; + + if (!system->success(system->load(&additionalLibrary, codeLibraryName))) + system->abort(); + libraries->setNext(additionalLibrary); + } + + if (bootstrapPropertyDup) + free((void*)bootstrapPropertyDup); +} + +void Machine::dispose() +{ + localThread->dispose(); + stateLock->dispose(); + heapLock->dispose(); + classLock->dispose(); + referenceLock->dispose(); + shutdownLock->dispose(); + + if (libraries) { + libraries->disposeAll(); + } + + for (Reference* r = jniReferences; r;) { + Reference* tmp = r; + r = r->next; + heap->free(tmp, sizeof(*tmp)); + } + + for (unsigned i = 0; i < heapPoolIndex; ++i) { + heap->free(heapPool[i], ThreadHeapSizeInBytes); + } + + if (bootimage) { + heap->free(bootimage, bootimageSize); + } + + heap->free(arguments, sizeof(const char*) * argumentCount); + + for (unsigned int i = 0; i < propertyCount; i++) { + heap->free(properties[i], sizeof(char) * (strlen(properties[i]) + 1)); + } + heap->free(properties, sizeof(const char*) * propertyCount); + + static_cast(heapClient)->dispose(); + + heap->free(this, sizeof(*this)); +} + +Thread::Thread(Machine* m, GcThread* javaThread, Thread* parent) + : vtable(&(m->jniEnvVTable)), + m(m), + parent(parent), + peer(0), + child(0), + waitNext(0), + state(NoState), + criticalLevel(0), + systemThread(0), + lock(0), + javaThread(javaThread), + exception(0), + heapIndex(0), + heapOffset(0), + protector(0), + classInitStack(0), + libraryLoadStack(0), + runnable(this), + defaultHeap( + static_cast(m->heap->allocate(ThreadHeapSizeInBytes))), + heap(defaultHeap), + backupHeapIndex(0), + flags(ActiveFlag) +{ +} + +void Thread::init() +{ + memset(defaultHeap, 0, ThreadHeapSizeInBytes); + memset(backupHeap, 0, ThreadBackupHeapSizeInBytes); + + if (parent == 0) { + assertT(this, m->rootThread == 0); + assertT(this, javaThread == 0); + + m->rootThread = this; + m->unsafe = true; + + if (not m->system->success(m->system->attach(&runnable))) { + abort(this); + } + + BootImage* image = 0; + uint8_t* code = 0; + const char* imageFunctionName = findProperty(m, "avian.bootimage"); + if (imageFunctionName) { + bool lzma = strncmp("lzma:", imageFunctionName, 5) == 0; + const char* symbolName = lzma ? imageFunctionName + 5 : imageFunctionName; + + void* imagep = m->libraries->resolve(symbolName); + if (imagep) { + uint8_t* (*imageFunction)(size_t*); + memcpy(&imageFunction, &imagep, BytesPerWord); + + size_t size = 0; + uint8_t* imageBytes = imageFunction(&size); + if (lzma) { +#ifdef AVIAN_USE_LZMA + m->bootimage = image = reinterpret_cast(decodeLZMA( + m->system, m->heap, imageBytes, size, &(m->bootimageSize))); +#else + abort(this); +#endif + } else { + image = reinterpret_cast(imageBytes); + } + + const char* codeFunctionName = findProperty(m, "avian.codeimage"); + if (codeFunctionName) { + void* codep = m->libraries->resolve(codeFunctionName); + if (codep) { + uint8_t* (*codeFunction)(size_t*); + memcpy(&codeFunction, &codep, BytesPerWord); + + code = codeFunction(&size); + } + } + } + } + + m->unsafe = false; + + enter(this, ActiveState); + + if (image and code) { + m->processor->boot(this, image, code); + makeArrayInterfaceTable(this); + } else { + boot(this); + } + + GcWeakHashMap* map = makeWeakHashMap(this, 0, 0); + // sequence point, for gc (don't recombine statements) + roots(this)->setByteArrayMap(this, map->as(this)); + + map = makeWeakHashMap(this, 0, 0); + // sequence point, for gc (don't recombine statements) + roots(this)->setMonitorMap(this, map->as(this)); + + GcVector* v = makeVector(this, 0, 0); + // sequence point, for gc (don't recombine statements) + roots(this)->setClassRuntimeDataTable(this, v); + + v = makeVector(this, 0, 0); + // sequence point, for gc (don't recombine statements) + roots(this)->setMethodRuntimeDataTable(this, v); + + v = makeVector(this, 0, 0); + // sequence point, for gc (don't recombine statements) + roots(this)->setJNIMethodTable(this, v); + + v = makeVector(this, 0, 0); + // sequence point, for gc (don't recombine statements) + roots(this)->setJNIFieldTable(this, v); + + m->localThread->set(this); + } + + expect(this, m->system->success(m->system->make(&lock))); +} + +void Thread::exit() +{ + if (state != Thread::ExitState and state != Thread::ZombieState) { + enter(this, Thread::ExclusiveState); + + if (m->liveCount == 1) { + turnOffTheLights(this); + } else { + javaThread->peer() = 0; + + enter(this, Thread::ZombieState); + } + } +} + +void Thread::dispose() +{ + if (lock) { + lock->dispose(); + } + + if (systemThread) { + systemThread->dispose(); + } + + --m->threadCount; + + m->heap->free(defaultHeap, ThreadHeapSizeInBytes); + + m->processor->dispose(this); +} + +void shutDown(Thread* t) +{ + ACQUIRE(t, t->m->shutdownLock); + + GcPair* hooks = roots(t)->shutdownHooks(); + PROTECT(t, hooks); + + roots(t)->setShutdownHooks(t, 0); + + GcPair* h = hooks; + PROTECT(t, h); + for (; h; h = cast(t, h->second())) { + startThread(t, cast(t, h->first())); + } + + // wait for hooks to exit + h = hooks; + for (; h; h = cast(t, h->second())) { + while (true) { + Thread* ht + = reinterpret_cast(cast(t, h->first())->peer()); + + { + ACQUIRE(t, t->m->stateLock); + + if (ht == 0 or ht->state == Thread::ZombieState + or ht->state == Thread::JoinedState) { + break; + } else { + ENTER(t, Thread::IdleState); + t->m->stateLock->wait(t->systemThread, 0); + } + } + } + } + + // tell finalize thread to exit and wait for it to do so + { + ACQUIRE(t, t->m->stateLock); + Thread* finalizeThread = t->m->finalizeThread; + if (finalizeThread) { + t->m->finalizeThread = 0; + t->m->stateLock->notifyAll(t->systemThread); + + while (finalizeThread->state != Thread::ZombieState + and finalizeThread->state != Thread::JoinedState) { + ENTER(t, Thread::IdleState); + t->m->stateLock->wait(t->systemThread, 0); + } + } + } + + // interrupt daemon threads and tell them to die + + // todo: be more aggressive about killing daemon threads, e.g. at + // any GC point, not just at waits/sleeps + { + ACQUIRE(t, t->m->stateLock); + + t->m->alive = false; + + visitAll(t, t->m->rootThread, interruptDaemon); + } +} + +void enter(Thread* t, Thread::State s) +{ + stress(t); + + if (s == t->state) + return; + + if (t->state == Thread::ExitState) { + // once in exit state, we stay that way + return; + } + +#ifdef USE_ATOMIC_OPERATIONS +#define INCREMENT atomicIncrement +#define ACQUIRE_LOCK ACQUIRE_RAW(t, t->m->stateLock) +#define STORE_LOAD_MEMORY_BARRIER storeLoadMemoryBarrier() +#else +#define INCREMENT(pointer, value) *(pointer) += value; +#define ACQUIRE_LOCK +#define STORE_LOAD_MEMORY_BARRIER + + ACQUIRE_RAW(t, t->m->stateLock); +#endif // not USE_ATOMIC_OPERATIONS + + switch (s) { + case Thread::ExclusiveState: { + ACQUIRE_LOCK; + + while (t->m->exclusive) { + // another thread got here first. + ENTER(t, Thread::IdleState); + t->m->stateLock->wait(t->systemThread, 0); + } + + switch (t->state) { + case Thread::ActiveState: + break; + + case Thread::IdleState: { + INCREMENT(&(t->m->activeCount), 1); + } break; + + default: + abort(t); + } + + t->state = Thread::ExclusiveState; + t->m->exclusive = t; + + STORE_LOAD_MEMORY_BARRIER; + + while (t->m->activeCount > 1) { + t->m->stateLock->wait(t->systemThread, 0); + } + } break; + + case Thread::IdleState: + if (LIKELY(t->state == Thread::ActiveState)) { + // fast path + assertT(t, t->m->activeCount > 0); + INCREMENT(&(t->m->activeCount), -1); + + t->state = s; + + STORE_LOAD_MEMORY_BARRIER; + + if (t->m->exclusive) { + ACQUIRE_LOCK; + + t->m->stateLock->notifyAll(t->systemThread); + } + + break; + } else { + // fall through to slow path + } + + case Thread::ZombieState: { + ACQUIRE_LOCK; + + switch (t->state) { + case Thread::ExclusiveState: { + assertT(t, t->m->exclusive == t); + t->m->exclusive = 0; + } break; + + case Thread::ActiveState: + break; + + default: + abort(t); + } + + assertT(t, t->m->activeCount > 0); + INCREMENT(&(t->m->activeCount), -1); + + if (s == Thread::ZombieState) { + assertT(t, t->m->liveCount > 0); + --t->m->liveCount; + + if (t->getFlags() & Thread::DaemonFlag) { + --t->m->daemonCount; + } + } + + t->state = s; + + t->m->stateLock->notifyAll(t->systemThread); + } break; + + case Thread::ActiveState: + if (LIKELY(t->state == Thread::IdleState and t->m->exclusive == 0)) { + // fast path + INCREMENT(&(t->m->activeCount), 1); + + t->state = s; + + STORE_LOAD_MEMORY_BARRIER; + + if (t->m->exclusive) { + // another thread has entered the exclusive state, so we + // return to idle and use the slow path to become active + enter(t, Thread::IdleState); + } else { + break; + } + } + + { + ACQUIRE_LOCK; + + switch (t->state) { + case Thread::ExclusiveState: { + assertT(t, t->m->exclusive == t); + + t->state = s; + t->m->exclusive = 0; + + t->m->stateLock->notifyAll(t->systemThread); + } break; + + case Thread::NoState: + case Thread::IdleState: { + while (t->m->exclusive) { + t->m->stateLock->wait(t->systemThread, 0); + } + + INCREMENT(&(t->m->activeCount), 1); + if (t->state == Thread::NoState) { + ++t->m->liveCount; + ++t->m->threadCount; + } + t->state = s; + } break; + + default: + abort(t); + } + } + break; + + case Thread::ExitState: { + ACQUIRE_LOCK; + + switch (t->state) { + case Thread::ExclusiveState: { + assertT(t, t->m->exclusive == t); + // exit state should also be exclusive, so don't set exclusive = 0 + + t->m->stateLock->notifyAll(t->systemThread); + } break; + + case Thread::ActiveState: + break; + + default: + abort(t); + } + + assertT(t, t->m->activeCount > 0); + INCREMENT(&(t->m->activeCount), -1); + + t->state = s; + + while (t->m->liveCount - t->m->daemonCount > 1) { + t->m->stateLock->wait(t->systemThread, 0); + } + } break; + + default: + abort(t); + } +} + +object allocate2(Thread* t, unsigned sizeInBytes, bool objectMask) +{ + return allocate3( + t, + t->m->heap, + ceilingDivide(sizeInBytes, BytesPerWord) > ThreadHeapSizeInWords + ? Machine::FixedAllocation + : Machine::MovableAllocation, + sizeInBytes, + objectMask); +} + +object allocate3(Thread* t, + Alloc* allocator, + Machine::AllocationType type, + unsigned sizeInBytes, + bool objectMask) +{ + expect(t, t->criticalLevel == 0); + + if (UNLIKELY(t->getFlags() & Thread::UseBackupHeapFlag)) { + expect(t, + t->backupHeapIndex + ceilingDivide(sizeInBytes, BytesPerWord) + <= ThreadBackupHeapSizeInWords); + + object o = reinterpret_cast(t->backupHeap + t->backupHeapIndex); + t->backupHeapIndex += ceilingDivide(sizeInBytes, BytesPerWord); + fieldAtOffset(o, 0) = 0; + return o; + } else if (UNLIKELY(t->getFlags() & Thread::TracingFlag)) { + expect(t, + t->heapIndex + ceilingDivide(sizeInBytes, BytesPerWord) + <= ThreadHeapSizeInWords); + return allocateSmall(t, sizeInBytes); + } + + ACQUIRE_RAW(t, t->m->stateLock); + + while (t->m->exclusive and t->m->exclusive != t) { + // another thread wants to enter the exclusive state, either for a + // collection or some other reason. We give it a chance here. + ENTER(t, Thread::IdleState); + + while (t->m->exclusive) { + t->m->stateLock->wait(t->systemThread, 0); + } + } + + do { + switch (type) { + case Machine::MovableAllocation: + if (t->heapIndex + ceilingDivide(sizeInBytes, BytesPerWord) + > ThreadHeapSizeInWords) { + t->heap = 0; + if ((not t->m->heap->limitExceeded()) + and t->m->heapPoolIndex < ThreadHeapPoolSize) { + t->heap = static_cast( + t->m->heap->tryAllocate(ThreadHeapSizeInBytes)); + + if (t->heap) { + memset(t->heap, 0, ThreadHeapSizeInBytes); + + t->m->heapPool[t->m->heapPoolIndex++] = t->heap; + t->heapOffset += t->heapIndex; + t->heapIndex = 0; + } + } + } + break; + + case Machine::FixedAllocation: + if (t->m->fixedFootprint + sizeInBytes > FixedFootprintThresholdInBytes) { + t->heap = 0; + } + break; + + case Machine::ImmortalAllocation: + break; + } + + int pendingAllocation = t->m->heap->fixedFootprint( + ceilingDivide(sizeInBytes, BytesPerWord), objectMask); + + if (t->heap == 0 or t->m->heap->limitExceeded(pendingAllocation)) { + // fprintf(stderr, "gc"); + // vmPrintTrace(t); + collect(t, Heap::MinorCollection, pendingAllocation); + } + + if (t->m->heap->limitExceeded(pendingAllocation)) { + throw_(t, roots(t)->outOfMemoryError()); + } + } while (type == Machine::MovableAllocation + and t->heapIndex + ceilingDivide(sizeInBytes, BytesPerWord) + > ThreadHeapSizeInWords); + + switch (type) { + case Machine::MovableAllocation: { + return allocateSmall(t, sizeInBytes); + } + + case Machine::FixedAllocation: { + object o = static_cast(t->m->heap->allocateFixed( + allocator, ceilingDivide(sizeInBytes, BytesPerWord), objectMask)); + + memset(o, 0, sizeInBytes); + + alias(o, 0) = FixedMark; + + t->m->fixedFootprint += t->m->heap->fixedFootprint( + ceilingDivide(sizeInBytes, BytesPerWord), objectMask); + + return o; + } + + case Machine::ImmortalAllocation: { + object o = static_cast(t->m->heap->allocateImmortalFixed( + allocator, ceilingDivide(sizeInBytes, BytesPerWord), objectMask)); + + memset(o, 0, sizeInBytes); + + alias(o, 0) = FixedMark; + + return o; + } + + default: + abort(t); + } +} + +void collect(Thread* t, Heap::CollectionType type, int pendingAllocation) +{ + ENTER(t, Thread::ExclusiveState); + + unsigned pending = pendingAllocation + - (t->m->heapPoolIndex * ThreadHeapSizeInWords); + + if (t->m->heap->limitExceeded(pending)) { + type = Heap::MajorCollection; + } + + doCollect(t, type, pendingAllocation); + + if (t->m->heap->limitExceeded(pending)) { + // try once more, giving the heap a chance to squeeze everything + // into the smallest possible space: + doCollect(t, Heap::MajorCollection, pendingAllocation); + } +} + +object makeNewGeneral(Thread* t, GcClass* class_) +{ + assertT(t, t->state == Thread::ActiveState); + + PROTECT(t, class_); + + object instance = makeNew(t, class_); + PROTECT(t, instance); + + if (class_->vmFlags() & WeakReferenceFlag) { + ACQUIRE(t, t->m->referenceLock); + + cast(t, instance)->vmNext() = t->m->weakReferences; + t->m->weakReferences = cast(t, instance); + } + + if (class_->vmFlags() & HasFinalizerFlag) { + addFinalizer(t, instance, 0); + } + + return instance; +} + +void popResources(Thread* t) +{ + while (t->resource != t->checkpoint->resource) { + Thread::Resource* r = t->resource; + t->resource = r->next; + r->release(); + } + + t->protector = t->checkpoint->protector; +} + +GcByteArray* makeByteArrayV(Thread* t, const char* format, va_list a, int size) +{ + THREAD_RUNTIME_ARRAY(t, char, buffer, size); + + int r = vm::vsnprintf(RUNTIME_ARRAY_BODY(buffer), size - 1, format, a); + if (r >= 0 and r < size - 1) { + GcByteArray* s = makeByteArray(t, strlen(RUNTIME_ARRAY_BODY(buffer)) + 1); + memcpy(s->body().begin(), RUNTIME_ARRAY_BODY(buffer), s->length()); + return s; + } else { + return 0; + } +} + +GcByteArray* makeByteArray(Thread* t, const char* format, ...) +{ + int size = 256; + while (true) { + va_list a; + va_start(a, format); + GcByteArray* s = makeByteArrayV(t, format, a, size); + va_end(a); + + if (s) { + return s; + } else { + size *= 2; + } + } +} + +GcString* makeString(Thread* t, const char* format, ...) +{ + int size = 256; + while (true) { + va_list a; + va_start(a, format); + GcByteArray* s = makeByteArrayV(t, format, a, size); + va_end(a); + + if (s) { + return t->m->classpath->makeString(t, s, 0, s->length() - 1); + } else { + size *= 2; + } + } +} + +int stringUTFLength(Thread* t, + GcString* string, + unsigned start, + unsigned length) +{ + unsigned result = 0; + + if (length) { + object data = string->data(); + if (objectClass(t, data) == type(t, GcByteArray::Type)) { + result = length; + } else { + GcCharArray* a = cast(t, data); + for (unsigned i = 0; i < length; ++i) { + uint16_t c = a->body()[string->offset(t) + start + i]; + if (c == 0) + result += 1; // null char (was 2 bytes in Java) + else if (c < 0x80) + result += 1; // ASCII char + else if (c < 0x800) + result += 2; // two-byte char + else + result += 3; // three-byte char + } + } + } + + return result; +} + +void stringChars(Thread* t, + GcString* string, + unsigned start, + unsigned length, + char* chars) +{ + if (length) { + object data = string->data(); + if (objectClass(t, data) == type(t, GcByteArray::Type)) { + GcByteArray* b = cast(t, data); + memcpy(chars, &b->body()[string->offset(t) + start], length); + } else { + GcCharArray* c = cast(t, data); + for (unsigned i = 0; i < length; ++i) { + chars[i] = c->body()[string->offset(t) + start + i]; + } + } + } + chars[length] = 0; +} + +void stringChars(Thread* t, + GcString* string, + unsigned start, + unsigned length, + uint16_t* chars) +{ + if (length) { + object data = string->data(); + if (objectClass(t, data) == type(t, GcByteArray::Type)) { + GcByteArray* b = cast(t, data); + for (unsigned i = 0; i < length; ++i) { + chars[i] = b->body()[string->offset(t) + start + i]; + } + } else { + GcCharArray* c = cast(t, data); + memcpy(chars, + &c->body()[string->offset(t) + start], + length * sizeof(uint16_t)); + } + } + chars[length] = 0; +} + +void stringUTFChars(Thread* t, + GcString* string, + unsigned start, + unsigned length, + char* chars, + unsigned charsLength UNUSED) +{ + assertT(t, + static_cast(stringUTFLength(t, string, start, length)) + == charsLength); + + object data = string->data(); + if (objectClass(t, data) == type(t, GcByteArray::Type)) { + GcByteArray* b = cast(t, data); + memcpy(chars, &b->body()[string->offset(t) + start], length); + chars[length] = 0; + } else { + GcCharArray* cs = cast(t, data); + int j = 0; + for (unsigned i = 0; i < length; ++i) { + uint16_t c = cs->body()[string->offset(t) + start + i]; + if (!c) { // null char + chars[j++] = 0; + } else if (c < 0x80) { // ASCII char + chars[j++] = static_cast(c); + } else if (c < 0x800) { // two-byte char + chars[j++] = static_cast(0x0c0 | (c >> 6)); + chars[j++] = static_cast(0x080 | (c & 0x03f)); + } else { // three-byte char + chars[j++] = static_cast(0x0e0 | ((c >> 12) & 0x0f)); + chars[j++] = static_cast(0x080 | ((c >> 6) & 0x03f)); + chars[j++] = static_cast(0x080 | (c & 0x03f)); + } + } + chars[j] = 0; + } +} + +uint64_t resolveBootstrap(Thread* t, uintptr_t* arguments) +{ + GcByteArray* name + = cast(t, reinterpret_cast(arguments[0])); + + resolveSystemClass(t, roots(t)->bootLoader(), name); + + return 1; +} + +bool isAssignableFrom(Thread* t, GcClass* a, GcClass* b) +{ + assertT(t, a); + assertT(t, b); + + if (a == b) + return true; + + if (a->flags() & ACC_INTERFACE) { + if (b->vmFlags() & BootstrapFlag) { + uintptr_t arguments[] = {reinterpret_cast(b->name())}; + + if (run(t, resolveBootstrap, arguments) == 0) { + t->exception = 0; + return false; + } + } + + GcArray* itable = cast(t, b->interfaceTable()); + if (itable) { + unsigned stride = (b->flags() & ACC_INTERFACE) ? 1 : 2; + for (unsigned i = 0; i < itable->length(); i += stride) { + if (itable->body()[i] == a) { + return true; + } + } + } + } else if (a->arrayDimensions()) { + if (b->arrayDimensions()) { + return isAssignableFrom( + t, a->arrayElementClass(), b->arrayElementClass()); + } + } else if ((a->vmFlags() & PrimitiveFlag) == (b->vmFlags() & PrimitiveFlag)) { + for (; b; b = b->super()) { + if (b == a) { + return true; + } + } + } + + return false; +} + +bool instanceOf(Thread* t, GcClass* class_, object o) +{ + if (o == 0) { + return false; + } else { + return isAssignableFrom(t, class_, objectClass(t, o)); + } +} + +GcMethod* classInitializer(Thread* t, GcClass* class_) +{ + if (GcArray* mtable = cast(t, class_->methodTable())) { + PROTECT(t, mtable); + for (unsigned i = 0; i < mtable->length(); ++i) { + GcMethod* o = cast(t, mtable->body()[i]); + + if (o->vmFlags() & ClassInitFlag) { + return o; + } + } + } + return 0; +} + +unsigned fieldCode(Thread* t, unsigned javaCode) +{ + switch (javaCode) { + case 'B': + return ByteField; + case 'C': + return CharField; + case 'D': + return DoubleField; + case 'F': + return FloatField; + case 'I': + return IntField; + case 'J': + return LongField; + case 'S': + return ShortField; + case 'V': + return VoidField; + case 'Z': + return BooleanField; + case 'L': + case '[': + return ObjectField; + + default: + abort(t); + } +} + +unsigned fieldType(Thread* t, unsigned code) +{ + switch (code) { + case VoidField: + return VOID_TYPE; + case ByteField: + case BooleanField: + return INT8_TYPE; + case CharField: + case ShortField: + return INT16_TYPE; + case DoubleField: + return DOUBLE_TYPE; + case FloatField: + return FLOAT_TYPE; + case IntField: + return INT32_TYPE; + case LongField: + return INT64_TYPE; + case ObjectField: + return POINTER_TYPE; + + default: + abort(t); + } +} + +unsigned primitiveSize(Thread* t, unsigned code) +{ + switch (code) { + case VoidField: + return 0; + case ByteField: + case BooleanField: + return 1; + case CharField: + case ShortField: + return 2; + case FloatField: + case IntField: + return 4; + case DoubleField: + case LongField: + return 8; + + default: + abort(t); + } +} + +GcClass* parseClass(Thread* t, + GcClassLoader* loader, + const uint8_t* data, + unsigned size, + Gc::Type throwType) +{ + PROTECT(t, loader); + + 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, data, size); + + uint32_t magic = s.read4(); + expect(t, magic == 0xCAFEBABE); + unsigned minorVer = s.read2(); // minor version + unsigned majorVer = s.read2(); // major version + if (DebugClassReader) { + fprintf(stderr, "read class (minor %d major %d)\n", minorVer, majorVer); + } + + GcSingleton* pool = parsePool(t, s); + PROTECT(t, pool); + + unsigned flags = s.read2(); + unsigned name = s.read2(); + + GcClass* class_ = (GcClass*)makeClass( + t, + flags, + 0, // VM flags + 0, // fixed size + 0, // array size + 0, // array dimensions + 0, // array element class + 0, // runtime data index + 0, // object mask + cast(t, singletonObject(t, pool, name - 1))->name(), + 0, // source file + 0, // super + 0, // interfaces + 0, // vtable + 0, // fields + 0, // methods + 0, // addendum + 0, // static table + loader, + 0, // source + 0); // vtable length + PROTECT(t, class_); + + unsigned super = s.read2(); + if (super) { + GcClass* sc = resolveClass( + t, + loader, + cast(t, singletonObject(t, pool, super - 1))->name(), + true, + throwType); + + class_->setSuper(t, sc); + + class_->vmFlags() |= (sc->vmFlags() & (ReferenceFlag | WeakReferenceFlag + | HasFinalizerFlag | NeedInitFlag)); + } + + if (DebugClassReader) { + fprintf(stderr, " flags %d name %d super %d\n", flags, name, super); + } + + parseInterfaceTable(t, s, class_, pool, throwType); + + parseFieldTable(t, s, class_, pool); + + parseMethodTable(t, s, class_, pool); + + parseAttributeTable(t, s, class_, pool); + + GcArray* vtable = cast(t, class_->virtualTable()); + unsigned vtableLength = (vtable ? vtable->length() : 0); + + GcClass* real = t->m->processor->makeClass(t, + class_->flags(), + class_->vmFlags(), + class_->fixedSize(), + class_->arrayElementSize(), + class_->arrayDimensions(), + class_->arrayElementClass(), + class_->objectMask(), + class_->name(), + class_->sourceFile(), + class_->super(), + class_->interfaceTable(), + class_->virtualTable(), + class_->fieldTable(), + class_->methodTable(), + class_->addendum(), + class_->staticTable(), + class_->loader(), + vtableLength); + + PROTECT(t, real); + + t->m->processor->initVtable(t, real); + + updateClassTables(t, real, class_); + + if (roots(t)->poolMap()) { + object bootstrapClass = hashMapFind(t, + roots(t)->bootstrapClassMap(), + class_->name(), + byteArrayHash, + byteArrayEqual); + + hashMapInsert(t, + roots(t)->poolMap(), + bootstrapClass ? bootstrapClass : real, + pool, + objectHash); + } + + return real; +} + +uint64_t runParseClass(Thread* t, uintptr_t* arguments) +{ + GcClassLoader* loader + = cast(t, reinterpret_cast(arguments[0])); + System::Region* region = reinterpret_cast(arguments[1]); + Gc::Type throwType = static_cast(arguments[2]); + + return reinterpret_cast( + parseClass(t, loader, region->start(), region->length(), throwType)); +} + +GcClass* resolveSystemClass(Thread* t, + GcClassLoader* loader, + GcByteArray* spec, + bool throw_, + Gc::Type throwType) +{ + PROTECT(t, loader); + PROTECT(t, spec); + + ACQUIRE(t, t->m->classLock); + + GcClass* class_ = cast(t, + hashMapFind(t, + cast(t, loader->map()), + spec, + byteArrayHash, + byteArrayEqual)); + + if (class_ == 0) { + PROTECT(t, class_); + + if (loader->parent()) { + class_ = resolveSystemClass(t, loader->parent(), spec, false); + if (class_) { + return class_; + } + } + + if (spec->body()[0] == '[') { + class_ = resolveArrayClass(t, loader, spec, throw_, throwType); + } else { + GcSystemClassLoader* sysLoader = loader->as(t); + PROTECT(t, sysLoader); + + THREAD_RUNTIME_ARRAY(t, char, file, spec->length() + 6); + memcpy( + RUNTIME_ARRAY_BODY(file), spec->body().begin(), spec->length() - 1); + memcpy(RUNTIME_ARRAY_BODY(file) + spec->length() - 1, ".class", 7); + + System::Region* region = static_cast(sysLoader->finder()) + ->find(RUNTIME_ARRAY_BODY(file)); + + if (region) { + if (Verbose) { + fprintf(stderr, "parsing %s\n", spec->body().begin()); + } + + { + THREAD_RESOURCE(t, System::Region*, region, region->dispose()); + + uintptr_t arguments[] = {reinterpret_cast(loader), + reinterpret_cast(region), + static_cast(throwType)}; + + // parse class file + class_ = cast( + t, reinterpret_cast(runRaw(t, runParseClass, arguments))); + + if (UNLIKELY(t->exception)) { + if (throw_) { + GcThrowable* e = t->exception; + t->exception = 0; + vm::throw_(t, e); + } else { + t->exception = 0; + return 0; + } + } + } + + if (Verbose) { + fprintf( + stderr, "done parsing %s: %p\n", spec->body().begin(), class_); + } + + { + const char* source = static_cast(sysLoader->finder()) + ->sourceUrl(RUNTIME_ARRAY_BODY(file)); + + if (source) { + unsigned length = strlen(source); + GcByteArray* array = makeByteArray(t, length + 1); + memcpy(array->body().begin(), source, length); + array = internByteArray(t, array); + + class_->setSource(t, array); + } + } + + GcClass* bootstrapClass + = cast(t, + hashMapFind(t, + roots(t)->bootstrapClassMap(), + spec, + byteArrayHash, + byteArrayEqual)); + + if (bootstrapClass) { + PROTECT(t, bootstrapClass); + + updateBootstrapClass(t, bootstrapClass, class_); + class_ = bootstrapClass; + } + } + } + + if (class_) { + hashMapInsert( + t, cast(t, loader->map()), spec, class_, byteArrayHash); + + updatePackageMap(t, class_); + } else if (throw_) { + throwNew(t, throwType, "%s", spec->body().begin()); + } + } + + return class_; +} + +GcClass* findLoadedClass(Thread* t, GcClassLoader* loader, GcByteArray* spec) +{ + PROTECT(t, loader); + PROTECT(t, spec); + + ACQUIRE(t, t->m->classLock); + + return loader->map() + ? cast(t, + hashMapFind(t, + cast(t, loader->map()), + spec, + byteArrayHash, + byteArrayEqual)) + : 0; +} + +GcClass* resolveClass(Thread* t, + GcClassLoader* loader, + GcByteArray* spec, + bool throw_, + Gc::Type throwType) +{ + if (objectClass(t, loader) == type(t, GcSystemClassLoader::Type)) { + return resolveSystemClass(t, loader, spec, throw_, throwType); + } else { + PROTECT(t, loader); + PROTECT(t, spec); + + GcClass* c = findLoadedClass(t, loader, spec); + if (c) { + return c; + } + + if (spec->body()[0] == '[') { + c = resolveArrayClass(t, loader, spec, throw_, throwType); + } else { + if (roots(t)->loadClassMethod() == 0) { + GcMethod* m = resolveMethod(t, + roots(t)->bootLoader(), + "java/lang/ClassLoader", + "loadClass", + "(Ljava/lang/String;)Ljava/lang/Class;"); + + if (m) { + roots(t)->setLoadClassMethod(t, m); + + GcClass* classLoaderClass = type(t, GcClassLoader::Type); + + if (classLoaderClass->vmFlags() & BootstrapFlag) { + resolveSystemClass( + t, roots(t)->bootLoader(), classLoaderClass->name()); + } + } + } + + GcMethod* method = findVirtualMethod( + t, roots(t)->loadClassMethod(), objectClass(t, loader)); + + PROTECT(t, method); + + THREAD_RUNTIME_ARRAY(t, char, s, spec->length()); + replace('/', + '.', + RUNTIME_ARRAY_BODY(s), + reinterpret_cast(spec->body().begin())); + + GcString* specString = makeString(t, "%s", RUNTIME_ARRAY_BODY(s)); + PROTECT(t, specString); + + uintptr_t arguments[] = {reinterpret_cast(method), + reinterpret_cast(loader), + reinterpret_cast(specString)}; + + GcJclass* jc = cast( + t, reinterpret_cast(runRaw(t, invokeLoadClass, arguments))); + + if (LIKELY(jc)) { + c = jc->vmClass(); + } else if (t->exception) { + if (throw_) { + GcThrowable* e + = type(t, throwType) == objectClass(t, t->exception) + ? t->exception + : makeThrowable(t, throwType, specString, 0, t->exception); + t->exception = 0; + vm::throw_(t, e); + } else { + t->exception = 0; + } + } + } + + if (LIKELY(c)) { + PROTECT(t, c); + + saveLoadedClass(t, loader, c); + } else if (throw_) { + throwNew(t, throwType, "%s", spec->body().begin()); + } + + return c; + } +} + +GcMethod* resolveMethod(Thread* t, + GcClass* class_, + const char* methodName, + const char* methodSpec) +{ + PROTECT(t, class_); + + GcByteArray* name = makeByteArray(t, methodName); + PROTECT(t, name); + + GcByteArray* spec = makeByteArray(t, methodSpec); + + GcMethod* method + = cast(t, findMethodInClass(t, class_, name, spec)); + + if (method == 0) { + throwNew(t, + GcNoSuchMethodError::Type, + "%s %s not found in %s", + methodName, + methodSpec, + class_->name()->body().begin()); + } else { + return method; + } +} + +GcField* resolveField(Thread* t, + GcClass* class_, + const char* fieldName, + const char* fieldSpec) +{ + PROTECT(t, class_); + + GcByteArray* name = makeByteArray(t, fieldName); + PROTECT(t, name); + + GcByteArray* spec = makeByteArray(t, fieldSpec); + PROTECT(t, spec); + + GcField* field = cast( + t, findInInterfaces(t, class_, name, spec, findFieldInClass)); + + GcClass* c = class_; + PROTECT(t, c); + + for (; c != 0 and field == 0; c = c->super()) { + field = cast(t, findFieldInClass(t, c, name, spec)); + } + + if (field == 0) { + throwNew(t, + GcNoSuchFieldError::Type, + "%s %s not found in %s", + fieldName, + fieldSpec, + class_->name()->body().begin()); + } else { + return field; + } +} + +bool classNeedsInit(Thread* t, GcClass* c) +{ + if (c->vmFlags() & NeedInitFlag) { + if (c->vmFlags() & InitFlag) { + // the class is currently being initialized. If this the thread + // which is initializing it, we should not try to initialize it + // recursively. Otherwise, we must wait for the responsible + // thread to finish. + for (Thread::ClassInitStack* s = t->classInitStack; s; s = s->next) { + if (s->class_ == c) { + return false; + } + } + } + return true; + } else { + return false; + } +} + +bool preInitClass(Thread* t, GcClass* c) +{ + int flags = c->vmFlags(); + + loadMemoryBarrier(); + + if (flags & NeedInitFlag) { + PROTECT(t, c); + ACQUIRE(t, t->m->classLock); + + if (c->vmFlags() & NeedInitFlag) { + if (c->vmFlags() & InitFlag) { + // If the class is currently being initialized and this the thread + // which is initializing it, we should not try to initialize it + // recursively. + if (isInitializing(t, c)) { + return false; + } + + // some other thread is on the job - wait for it to finish. + while (c->vmFlags() & InitFlag) { + ENTER(t, Thread::IdleState); + t->m->classLock->wait(t->systemThread, 0); + } + } else if (c->vmFlags() & InitErrorFlag) { + throwNew( + t, GcNoClassDefFoundError::Type, "%s", c->name()->body().begin()); + } else { + c->vmFlags() |= InitFlag; + return true; + } + } + } + return false; +} + +void postInitClass(Thread* t, GcClass* c) +{ + PROTECT(t, c); + ACQUIRE(t, t->m->classLock); + + if (t->exception + and instanceOf(t, type(t, GcException::Type), t->exception)) { + c->vmFlags() |= NeedInitFlag | InitErrorFlag; + c->vmFlags() &= ~InitFlag; + + GcThrowable* exception = t->exception; + t->exception = 0; + + GcExceptionInInitializerError* initExecption + = makeThrowable(t, GcExceptionInInitializerError::Type, 0, 0, exception) + ->as(t); + + initExecption->setException(t, exception->cause()); + + throw_(t, initExecption->as(t)); + } else { + c->vmFlags() &= ~(NeedInitFlag | InitFlag); + } + t->m->classLock->notifyAll(t->systemThread); +} + +void initClass(Thread* t, GcClass* c) +{ + PROTECT(t, c); + + GcClass* super = c->super(); + if (super) { + initClass(t, super); + } + + if (preInitClass(t, c)) { + OBJECT_RESOURCE(t, c, postInitClass(t, cast(t, c))); + + GcMethod* initializer = classInitializer(t, c); + + if (initializer) { + Thread::ClassInitStack stack(t, c); + + t->m->processor->invoke(t, initializer, 0); + } + } +} + +GcClass* resolveObjectArrayClass(Thread* t, + GcClassLoader* loader, + GcClass* elementClass) +{ + PROTECT(t, loader); + PROTECT(t, elementClass); + + { + GcClass* arrayClass + = cast(t, getClassRuntimeData(t, elementClass)->arrayClass()); + if (arrayClass) { + return arrayClass; + } + } + + GcByteArray* elementSpec = elementClass->name(); + PROTECT(t, elementSpec); + + GcByteArray* spec; + if (elementSpec->body()[0] == '[') { + spec = makeByteArray(t, elementSpec->length() + 1); + spec->body()[0] = '['; + memcpy( + &spec->body()[1], elementSpec->body().begin(), elementSpec->length()); + } else { + spec = makeByteArray(t, elementSpec->length() + 3); + spec->body()[0] = '['; + spec->body()[1] = 'L'; + memcpy(&spec->body()[2], + elementSpec->body().begin(), + elementSpec->length() - 1); + spec->body()[elementSpec->length() + 1] = ';'; + spec->body()[elementSpec->length() + 2] = 0; + } + + GcClass* arrayClass = resolveClass(t, loader, spec); + + getClassRuntimeData(t, elementClass)->setArrayClass(t, arrayClass); + + return arrayClass; +} + +object makeObjectArray(Thread* t, GcClass* elementClass, unsigned count) +{ + GcClass* arrayClass + = resolveObjectArrayClass(t, elementClass->loader(), elementClass); + + PROTECT(t, arrayClass); + + object array = makeArray(t, count); + setObjectClass(t, array, arrayClass); + + return array; +} + +static GcByteArray* getFieldName(Thread* t, object obj) +{ + return reinterpret_cast(cast(t, obj)->name()); +} + +static GcByteArray* getFieldSpec(Thread* t, object obj) +{ + return reinterpret_cast(cast(t, obj)->spec()); +} + +static GcByteArray* getMethodName(Thread* t, object obj) +{ + return reinterpret_cast(cast(t, obj)->name()); +} + +static GcByteArray* getMethodSpec(Thread* t, object obj) +{ + return reinterpret_cast(cast(t, obj)->spec()); +} + +object findFieldInClass(Thread* t, + GcClass* class_, + GcByteArray* name, + GcByteArray* spec) +{ + return findInTable(t, + cast(t, class_->fieldTable()), + name, + spec, + getFieldName, + getFieldSpec); +} + +object findMethodInClass(Thread* t, + GcClass* class_, + GcByteArray* name, + GcByteArray* spec) +{ + return findInTable(t, + cast(t, class_->methodTable()), + name, + spec, + getMethodName, + getMethodSpec); +} + +object findInHierarchyOrNull( + Thread* t, + GcClass* class_, + GcByteArray* name, + GcByteArray* spec, + object (*find)(Thread*, GcClass*, GcByteArray*, GcByteArray*)) +{ + GcClass* originalClass = class_; + + object o = 0; + if ((class_->flags() & ACC_INTERFACE) and class_->virtualTable()) { + o = findInTable(t, + cast(t, class_->virtualTable()), + name, + spec, + getMethodName, + getMethodSpec); + } + + if (o == 0) { + for (; o == 0 and class_; class_ = class_->super()) { + o = find(t, class_, name, spec); + } + + if (o == 0 and find == findFieldInClass) { + o = findInInterfaces(t, originalClass, name, spec, find); + } + } + + return o; +} + +unsigned parameterFootprint(Thread* t, const char* s, bool static_) +{ + unsigned footprint = 0; + for (MethodSpecIterator it(t, s); it.hasNext();) { + switch (*it.next()) { + case 'J': + case 'D': + footprint += 2; + break; + + default: + ++footprint; + break; + } + } + + if (not static_) { + ++footprint; + } + return footprint; +} + +void addFinalizer(Thread* t, object target, void (*finalize)(Thread*, object)) +{ + PROTECT(t, target); + + ACQUIRE(t, t->m->referenceLock); + + void* function; + memcpy(&function, &finalize, BytesPerWord); + + GcFinalizer* f = makeFinalizer(t, 0, function, 0, 0, 0); + f->target() = target; + f->next() = t->m->finalizers; + t->m->finalizers = f; +} + +GcMonitor* objectMonitor(Thread* t, object o, bool createNew) +{ + assertT(t, t->state == Thread::ActiveState); + + object m = hashMapFind(t, roots(t)->monitorMap(), o, objectHash, objectEqual); + + if (m) { + if (DebugMonitors) { + fprintf(stderr, "found monitor %p for object %x\n", m, objectHash(t, o)); + } + + return cast(t, m); + } else if (createNew) { + PROTECT(t, o); + PROTECT(t, m); + + { + ENTER(t, Thread::ExclusiveState); + + m = hashMapFind(t, roots(t)->monitorMap(), o, objectHash, objectEqual); + + if (m) { + if (DebugMonitors) { + fprintf( + stderr, "found monitor %p for object %x\n", m, objectHash(t, o)); + } + + return cast(t, m); + } + + object head = makeMonitorNode(t, 0, 0); + m = makeMonitor(t, 0, 0, 0, head, head, 0); + + if (DebugMonitors) { + fprintf(stderr, "made monitor %p for object %x\n", m, objectHash(t, o)); + } + + hashMapInsert(t, roots(t)->monitorMap(), o, m, objectHash); + + addFinalizer(t, o, removeMonitor); + } + + return cast(t, m); + } else { + return 0; + } +} + +object intern(Thread* t, object s) +{ + PROTECT(t, s); + + ACQUIRE(t, t->m->referenceLock); + + GcTriple* n + = hashMapFindNode(t, roots(t)->stringMap(), s, stringHash, stringEqual); + + if (n) { + return cast(t, n->first())->target(); + } else { + hashMapInsert(t, roots(t)->stringMap(), s, 0, stringHash); + addFinalizer(t, s, removeString); + return s; + } +} + +object clone(Thread* t, object o) +{ + PROTECT(t, o); + + GcClass* class_ = objectClass(t, o); + unsigned size = baseSize(t, o, class_) * BytesPerWord; + object clone; + + if (class_->arrayElementSize()) { + clone = static_cast(allocate(t, size, class_->objectMask())); + memcpy(clone, o, size); + // clear any object header flags: + setObjectClass(t, o, objectClass(t, o)); + } else if (instanceOf(t, type(t, GcCloneable::Type), o)) { + clone = make(t, class_); + memcpy(reinterpret_cast(clone) + 1, + reinterpret_cast(o) + 1, + size - BytesPerWord); + } else { + GcByteArray* classNameSlash = objectClass(t, o)->name(); + THREAD_RUNTIME_ARRAY(t, char, classNameDot, classNameSlash->length()); + replace('/', + '.', + RUNTIME_ARRAY_BODY(classNameDot), + reinterpret_cast(classNameSlash->body().begin())); + throwNew(t, + GcCloneNotSupportedException::Type, + "%s", + RUNTIME_ARRAY_BODY(classNameDot)); + } + + return clone; +} + +void walk(Thread* t, Heap::Walker* w, object o, unsigned start) +{ + GcClass* class_ = t->m->heap->follow(objectClass(t, o)); + GcIntArray* objectMask = t->m->heap->follow(class_->objectMask()); + + bool more = true; + + if (objectMask) { + unsigned fixedSize = class_->fixedSize(); + unsigned arrayElementSize = class_->arrayElementSize(); + unsigned arrayLength = (arrayElementSize ? fieldAtOffset( + o, fixedSize - BytesPerWord) + : 0); + + THREAD_RUNTIME_ARRAY(t, uint32_t, mask, objectMask->length()); + memcpy(RUNTIME_ARRAY_BODY(mask), + objectMask->body().begin(), + objectMask->length() * 4); + + more = ::walk(t, + w, + RUNTIME_ARRAY_BODY(mask), + fixedSize, + arrayElementSize, + arrayLength, + start); + } else if (class_->vmFlags() & SingletonFlag) { + GcSingleton* s = cast(t, o); + unsigned length = s->length(); + if (length) { + more = ::walk(t, + w, + singletonMask(t, s), + (singletonCount(t, s) + 2) * BytesPerWord, + 0, + 0, + start); + } else if (start == 0) { + more = w->visit(0); + } + } else if (start == 0) { + more = w->visit(0); + } + + if (more and class_->vmFlags() & ContinuationFlag) { + t->m->processor->walkContinuationBody(t, w, o, start); + } +} + +int walkNext(Thread* t, object o, int previous) +{ + class Walker : public Heap::Walker { + public: + Walker() : value(-1) + { + } + + bool visit(unsigned offset) + { + value = offset; + return false; + } + + int value; + } walker; + + walk(t, &walker, o, previous + 1); + return walker.value; +} + +void visitRoots(Machine* m, Heap::Visitor* v) +{ + v->visit(&(m->types)); + v->visit(&(m->roots)); + + for (Thread* t = m->rootThread; t; t = t->peer) { + ::visitRoots(t, v); + } + + for (Reference* r = m->jniReferences; r; r = r->next) { + if (not r->weak) { + v->visit(&(r->target)); + } + } +} + +void logTrace(FILE* f, const char* fmt, ...) +{ + va_list a; + va_start(a, fmt); +#ifdef PLATFORM_WINDOWS + const unsigned length = _vscprintf(fmt, a); +#else + const unsigned length = vsnprintf(0, 0, fmt, a); +#endif + va_end(a); + + RUNTIME_ARRAY(char, buffer, length + 1); + va_start(a, fmt); + vsnprintf(RUNTIME_ARRAY_BODY(buffer), length + 1, fmt, a); + va_end(a); + RUNTIME_ARRAY_BODY(buffer)[length] = 0; + + ::fprintf(f, "%s", RUNTIME_ARRAY_BODY(buffer)); +#ifdef PLATFORM_WINDOWS + ::OutputDebugStringA(RUNTIME_ARRAY_BODY(buffer)); +#endif +} + +void printTrace(Thread* t, GcThrowable* exception) +{ + if (exception == 0) { + exception = makeThrowable(t, GcNullPointerException::Type); + } + + for (GcThrowable* e = exception; e; e = e->cause()) { + if (e != exception) { + logTrace(errorLog(t), "caused by: "); + } + + logTrace(errorLog(t), "%s", objectClass(t, e)->name()->body().begin()); + + if (e->message()) { + GcString* m = e->message(); + THREAD_RUNTIME_ARRAY(t, char, message, m->length(t) + 1); + stringChars(t, m, RUNTIME_ARRAY_BODY(message)); + logTrace(errorLog(t), ": %s\n", RUNTIME_ARRAY_BODY(message)); + } else { + logTrace(errorLog(t), "\n"); + } + + object trace = e->trace(); + if (trace) { + for (unsigned i = 0; i < objectArrayLength(t, trace); ++i) { + GcTraceElement* e + = cast(t, objectArrayBody(t, trace, i)); + GcMethod* m = cast(t, e->method()); + const int8_t* class_ = m->class_()->name()->body().begin(); + const int8_t* method = m->name()->body().begin(); + int line = t->m->processor->lineNumber(t, m, e->ip()); + + logTrace(errorLog(t), " at %s.%s ", class_, method); + + switch (line) { + case NativeLine: + logTrace(errorLog(t), "(native)\n"); + break; + case UnknownLine: + logTrace(errorLog(t), "(unknown line)\n"); + break; + default: + logTrace(errorLog(t), "(line %d)\n", line); + } + } + } + + if (e == e->cause()) { + break; + } + } + + ::fflush(errorLog(t)); +} + +object makeTrace(Thread* t, Processor::StackWalker* walker) +{ + class Visitor : public Processor::StackVisitor { + public: + Visitor(Thread* t) : t(t), trace(0), index(0), protector(t, &trace) + { + } + + virtual bool visit(Processor::StackWalker* walker) + { + if (trace == 0) { + trace = makeObjectArray(t, walker->count()); + assertT(t, trace); + } + + GcTraceElement* e = makeTraceElement(t, walker->method(), walker->ip()); + assertT(t, index < objectArrayLength(t, trace)); + reinterpret_cast(trace)->setBodyElement(t, index, e); + ++index; + return true; + } + + Thread* t; + object trace; + unsigned index; + Thread::SingleProtector protector; + } v(t); + + walker->walk(&v); + + return v.trace ? v.trace : makeObjectArray(t, 0); +} + +object makeTrace(Thread* t, Thread* target) +{ + class Visitor : public Processor::StackVisitor { + public: + Visitor(Thread* t) : t(t), trace(0) + { + } + + virtual bool visit(Processor::StackWalker* walker) + { + trace = vm::makeTrace(t, walker); + return false; + } + + Thread* t; + object trace; + } v(t); + + t->m->processor->walkStack(target, &v); + + return v.trace ? v.trace : makeObjectArray(t, 0); +} + +void runFinalizeThread(Thread* t) +{ + GcFinalizer* finalizeList = 0; + PROTECT(t, finalizeList); + + GcCleaner* cleanList = 0; + PROTECT(t, cleanList); + + while (true) { + { + ACQUIRE(t, t->m->stateLock); + + while (t->m->finalizeThread and roots(t)->objectsToFinalize() == 0 + and roots(t)->objectsToClean() == 0) { + ENTER(t, Thread::IdleState); + t->m->stateLock->wait(t->systemThread, 0); + } + + if (t->m->finalizeThread == 0) { + return; + } else { + finalizeList = roots(t)->objectsToFinalize(); + roots(t)->setObjectsToFinalize(t, 0); + + cleanList = roots(t)->objectsToClean(); + roots(t)->setObjectsToClean(t, 0); + } + } + + for (; finalizeList; finalizeList = finalizeList->queueNext()) { + finalizeObject(t, finalizeList->queueTarget(), "finalize"); + } + + for (; cleanList; cleanList = cleanList->queueNext()) { + finalizeObject(t, cleanList, "clean"); + } + } +} + +object parseUtf8(Thread* t, const char* data, unsigned length) +{ + class Client : public Stream::Client { + public: + Client(Thread* t) : t(t) + { + } + + virtual void handleError() + { + if (false) + abort(t); + } + + private: + Thread* t; + } client(t); + + Stream s(&client, reinterpret_cast(data), length); + + return ::parseUtf8(t, s, length); +} + +object parseUtf8(Thread* t, GcByteArray* array) +{ + for (unsigned i = 0; i < array->length() - 1; ++i) { + if (array->body()[i] & 0x80) { + goto slow_path; + } + } + + return array; + +slow_path: + class Client : public Stream::Client { + public: + Client(Thread* t) : t(t) + { + } + + virtual void handleError() + { + if (false) + abort(t); + } + + private: + Thread* t; + } client(t); + + class MyStream : public AbstractStream { + public: + class MyProtector : public Thread::Protector { + public: + MyProtector(Thread* t, MyStream* s) : Protector(t), s(s) + { + } + + virtual void visit(Heap::Visitor* v) + { + v->visit(&(s->array)); + } + + MyStream* s; + }; + + MyStream(Thread* t, Client* client, GcByteArray* array) + : AbstractStream(client, array->length() - 1), + array(array), + protector(t, this) + { + } + + virtual void copy(uint8_t* dst, unsigned offset, unsigned size) + { + memcpy(dst, &array->body()[offset], size); + } + + GcByteArray* array; + MyProtector protector; + } s(t, &client, array); + + return ::parseUtf8(t, s, array->length() - 1); +} + +GcMethod* getCaller(Thread* t, unsigned target, bool skipMethodInvoke) +{ + if (static_cast(target) == -1) { + target = 2; + } + + class Visitor : public Processor::StackVisitor { + public: + Visitor(Thread* t, unsigned target, bool skipMethodInvoke) + : t(t), + method(0), + count(0), + target(target), + skipMethodInvoke(skipMethodInvoke) + { + } + + virtual bool visit(Processor::StackWalker* walker) + { + if (skipMethodInvoke + and walker->method()->class_() == type(t, GcJmethod::Type) + and strcmp(walker->method()->name()->body().begin(), + reinterpret_cast("invoke")) == 0) { + return true; + } + + if (count == target) { + method = walker->method(); + return false; + } else { + ++count; + return true; + } + } + + Thread* t; + GcMethod* method; + unsigned count; + unsigned target; + bool skipMethodInvoke; + } v(t, target, skipMethodInvoke); + + t->m->processor->walkStack(t, &v); + + return v.method; +} + +GcClass* defineClass(Thread* t, + GcClassLoader* loader, + const uint8_t* buffer, + unsigned length) +{ + PROTECT(t, loader); + + GcClass* c = parseClass(t, loader, buffer, length); + + // char name[byteArrayLength(t, className(t, c))]; + // memcpy(name, &byteArrayBody(t, className(t, c), 0), + // byteArrayLength(t, className(t, c))); + // replace('/', '-', name); + + // const unsigned BufferSize = 1024; + // char path[BufferSize]; + // snprintf(path, BufferSize, "/tmp/avian-define-class/%s.class", name); + + // FILE* file = fopen(path, "wb"); + // if (file) { + // fwrite(buffer, length, 1, file); + // fclose(file); + // } + + PROTECT(t, c); + + saveLoadedClass(t, loader, c); + + return c; +} + +void populateMultiArray(Thread* t, + object array, + int32_t* counts, + unsigned index, + unsigned dimensions) +{ + if (index + 1 == dimensions or counts[index] == 0) { + return; + } + + PROTECT(t, array); + + GcByteArray* spec = objectClass(t, array)->name(); + PROTECT(t, spec); + + GcByteArray* elementSpec = makeByteArray(t, spec->length() - 1); + memcpy(elementSpec->body().begin(), &spec->body()[1], spec->length() - 1); + + GcClass* class_ + = resolveClass(t, objectClass(t, array)->loader(), elementSpec); + PROTECT(t, class_); + + for (int32_t i = 0; i < counts[index]; ++i) { + GcArray* a = makeArray( + t, + ceilingDivide(counts[index + 1] * class_->arrayElementSize(), + BytesPerWord)); + a->length() = counts[index + 1]; + setObjectClass(t, a, class_); + setField(t, array, ArrayBody + (i * BytesPerWord), a); + + populateMultiArray(t, a, counts, index + 1, dimensions); + } +} + +object interruptLock(Thread* t, GcThread* thread) +{ + object lock = thread->interruptLock(); + + loadMemoryBarrier(); + + if (lock == 0) { + PROTECT(t, thread); + ACQUIRE(t, t->m->referenceLock); + + if (thread->interruptLock() == 0) { + object head = makeMonitorNode(t, 0, 0); + GcMonitor* lock = makeMonitor(t, 0, 0, 0, head, head, 0); + + storeStoreMemoryBarrier(); + + thread->setInterruptLock(t, lock); + } + } + + return thread->interruptLock(); +} + +void clearInterrupted(Thread* t) +{ + monitorAcquire(t, cast(t, interruptLock(t, t->javaThread))); + t->javaThread->interrupted() = false; + monitorRelease(t, cast(t, interruptLock(t, t->javaThread))); +} + +void threadInterrupt(Thread* t, GcThread* thread) +{ + PROTECT(t, thread); + + monitorAcquire(t, cast(t, interruptLock(t, thread))); + Thread* p = reinterpret_cast(thread->peer()); + if (p) { + interrupt(t, p); + } + thread->interrupted() = true; + monitorRelease(t, cast(t, interruptLock(t, thread))); +} + +bool threadIsInterrupted(Thread* t, GcThread* thread, bool clear) +{ + PROTECT(t, thread); + + monitorAcquire(t, cast(t, interruptLock(t, thread))); + bool v = thread->interrupted(); + if (clear) { + thread->interrupted() = false; + } + monitorRelease(t, cast(t, interruptLock(t, thread))); + return v; +} + +GcJclass* getDeclaringClass(Thread* t, GcClass* c) +{ + GcClassAddendum* addendum = c->addendum(); + if (addendum) { + GcArray* table = cast(t, addendum->innerClassTable()); + if (table) { + for (unsigned i = 0; i < table->length(); ++i) { + GcInnerClassReference* reference + = cast(t, table->body()[i]); + if (reference->outer() + and strcmp(reference->inner()->body().begin(), + c->name()->body().begin()) == 0) { + return getJClass(t, resolveClass(t, c->loader(), reference->outer())); + } + } + } + } + + return 0; +} + +// Called when interpreting invokedynamic. `invocation` points to +// static data in the bootstrap method table, which in turn points to +// a bootstrap method and stores additional data to be passed to +// it. `resolveDynamic` will then call this bootstrap method after +// resolving the arguments as required. The called method is assumed +// to be a lambda `metafactory` or `altMetafactory`. +// +// Note that capture/bridging etc happens within the bootstrap method, +// this is just the code that dispatches to it. +// +// Returns the CallSite returned by the bootstrap method. +GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation) +{ + PROTECT(t, invocation); + + // Use the invocation's Class to get the bootstrap method table and get a classloader. + GcClass* c = invocation->class_(); + PROTECT(t, c); + + // First element points to the bootstrap method. The rest are static data passed to the BSM. + GcCharArray* bootstrapArray = cast( + t, + cast(t, c->addendum()->bootstrapMethodTable()) + ->body()[invocation->bootstrap()]); + + PROTECT(t, bootstrapArray); + + // Resolve the bootstrap method itself. + GcMethod* bootstrap = cast(t, + resolve(t, + c->loader(), + invocation->pool(), + bootstrapArray->body()[0], + findMethodInClass, + GcNoSuchMethodError::Type)); + PROTECT(t, bootstrap); + + // Caller context info to be passed to the bootstrap method. + GcLookup* lookup + = makeLookup(t, c, ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC); + PROTECT(t, lookup); + + // The name of the linked-to method. + GcByteArray* nameBytes = invocation->template_()->name(); + GcString* name + = t->m->classpath->makeString(t, nameBytes, 0, nameBytes->length() - 1); + PROTECT(t, name); + + // This is the type of the linked-to method (e.g. lambda). + GcMethodType* type = makeMethodType( + t, c->loader(), invocation->template_()->spec(), 0, 0, 0); + PROTECT(t, type); + + // `array` stores either + // 1. All the arguments to be passed to the bootstrap method in the case of `metafactory` + // 2. The vararg object array to be passed to `altMetafactory` + GcArray* array = makeArray(t, bootstrap->parameterCount()); + PROTECT(t, array); + + // These are common arguments to metafactory and altMetafactory + unsigned argument = 0; + array->setBodyElement(t, argument++, lookup); + array->setBodyElement(t, argument++, name); + array->setBodyElement(t, argument++, type); + + THREAD_RUNTIME_ARRAY(t, char, specBuffer, bootstrap->spec()->length()); + + const char* spec; + // `argArray` stores the final arguments to be passed to the bootstrap method. + // Later in this function we iterate through the method signature + + // bootstrap array and resolve the arguments as required into `array`. + // + // In the case of a `metafactory` call: + // `argArray = [caller, invokedName, invokedType, methodType, methodImplementation, instantiatedType]` + // `array = argArray` + // + // In the case of an `altMetafactory` call: + // `argArray = [caller, invokedName, invokedType, array]` + // `array = [methodType, methodImplementation, instantiatedType, flags, ...]` + GcArray* argArray = array; + PROTECT(t, argArray); + + // Check if the bootstrap method's signature matches that of an altMetafactory + if (::strcmp(reinterpret_cast(bootstrap->spec()->body().begin()), + "(Ljava/lang/invoke/MethodHandles$Lookup;" + "Ljava/lang/String;" + "Ljava/lang/invoke/MethodType;" + "[Ljava/lang/Object;)" + "Ljava/lang/invoke/CallSite;") == 0) { + // If so, create a new array to store the varargs in, and hardcode the BSM signature. + array = makeArray(t, bootstrapArray->length() - 1); + spec = "(Ljava/lang/invoke/MethodHandles$Lookup;" + "Ljava/lang/String;" + "Ljava/lang/invoke/MethodType;" + "Ljava/lang/invoke/MethodType;" + "Ljava/lang/invoke/MethodHandle;" + "Ljava/lang/invoke/MethodType;" + "I" + "I" + "[Ljava/lang/Class;" + "I" + "[Ljava/lang/invoke/MethodType;" + ")Ljava/lang/invoke/CallSite;"; + } else if (bootstrap->parameterCount() == 2 + bootstrapArray->length()) { + // We're calling the simpler `metafactory`. 2 + bootstrapArray->length() is the + // arguments to the bootstrap method (bootstrapArray->length() - 1), plus the 3 static + // arguments (lookup, name, type). + memcpy(RUNTIME_ARRAY_BODY(specBuffer), + bootstrap->spec()->body().begin(), + bootstrap->spec()->length()); + spec = RUNTIME_ARRAY_BODY(specBuffer); + } else { + abort(t); + } + + MethodSpecIterator it(t, spec); + + // Skip over the already handled 3 arguments. + for (unsigned i = 0; i < argument; ++i) + it.next(); + + // If we're calling altMetafactory then we reset the argument + // offset, because we are filling the vararg array instead of the + // final argument array. + if (argArray != array) { + argument = 0; + } + + // `i` iterates through the bootstrap arguments (the +1 is because we skip + // the boostrap method's name), `it` iterates through the corresponding types + // in the method signature + unsigned i = 0; + while (i + 1 < bootstrapArray->length() && it.hasNext()) { + const char* p = it.next(); + + switch (*p) { + case 'L': { + const char* const methodType = "Ljava/lang/invoke/MethodType;"; + const char* const methodHandle = "Ljava/lang/invoke/MethodHandle;"; + if (strncmp(p, methodType, strlen(methodType)) == 0) { + GcMethodType* type = makeMethodType( + t, + c->loader(), + cast( + t, + singletonObject( + t, invocation->pool(), bootstrapArray->body()[i + 1])), + 0, + 0, + 0); + + array->setBodyElement(t, i + argument, type); + } else if (strncmp(p, methodHandle, strlen(methodHandle)) == 0) { + GcReference* reference = cast( + t, + singletonObject( + t, invocation->pool(), bootstrapArray->body()[i + 1])); + int kind = reference->kind(); + + GcMethod* method = cast(t, + resolve(t, + c->loader(), + invocation->pool(), + bootstrapArray->body()[i + 1], + findMethodInClass, + GcNoSuchMethodError::Type)); + + GcMethodHandle* handle + = makeMethodHandle(t, kind, c->loader(), method, 0); + + array->setBodyElement(t, i + argument, handle); + } else { + abort(t); + } + } break; + + case 'I': + case 'F': { + GcInt* box = makeInt( + t, + singletonValue(t, invocation->pool(), bootstrapArray->body()[i + 1])); + + array->setBodyElement(t, i + argument, box); + } break; + + case 'J': + case 'D': { + uint64_t v; + memcpy( + &v, + &singletonValue(t, invocation->pool(), bootstrapArray->body()[i + 1]), + 8); + + GcLong* box = makeLong(t, v); + + array->setBodyElement(t, i + argument, box); + } break; + + default: + abort(t); + } + + ++i; + } + + GcMethodHandle* handle + = (bootstrap->flags() & ACC_STATIC) + ? 0 + : makeMethodHandle(t, REF_invokeSpecial, c->loader(), bootstrap, 0); + + // If we're calling altMetafactory we set the fourth argument to the vararg array. + if (argArray != array) { + argArray->setBodyElement(t, 3, array); + } + + // Finally we make the bootstrap call. + return cast( + t, t->m->processor->invokeArray(t, bootstrap, handle, argArray)); +} + +void noop() +{ +} + +#include "type-constructors.cpp" + +} // namespace vm + +// for debugging +AVIAN_EXPORT void vmfPrintTrace(Thread* t, FILE* out) +{ + class Visitor : public Processor::StackVisitor { + public: + Visitor(Thread* t, FILE* out) : t(t), out(out) + { + } + + virtual bool visit(Processor::StackWalker* walker) + { + const int8_t* class_ = walker->method()->class_()->name()->body().begin(); + const int8_t* method = walker->method()->name()->body().begin(); + int line = t->m->processor->lineNumber(t, walker->method(), walker->ip()); + + fprintf(out, " at %s.%s ", class_, method); + + switch (line) { + case NativeLine: + fprintf(out, "(native)\n"); + break; + case UnknownLine: + fprintf(out, "(unknown line)\n"); + break; + default: + fprintf(out, "(line %d)\n", line); + } + + return true; + } + + Thread* t; + FILE* out; + } v(t, out); + + fprintf(out, "debug trace for thread %p\n", t); + + t->m->processor->walkStack(t, &v); + + fflush(out); +} + +AVIAN_EXPORT void vmPrintTrace(Thread* t) +{ + vmfPrintTrace(t, stderr); +} + +// also for debugging +AVIAN_EXPORT void* vmAddressFromLine(GcMethod* m, unsigned line) +{ + GcCode* code = m->code(); + printf("code: %p\n", code); + GcLineNumberTable* lnt = code->lineNumberTable(); + printf("lnt: %p\n", lnt); + + if (lnt) { + unsigned last = 0; + unsigned bottom = 0; + unsigned top = lnt->length(); + for (unsigned i = bottom; i < top; i++) { + uint64_t ln = lnt->body()[i]; + if (lineNumberLine(ln) == line) + return reinterpret_cast(lineNumberIp(ln)); + else if (lineNumberLine(ln) > line) + return reinterpret_cast(last); + last = lineNumberIp(ln); + } + } + return 0; +} diff --git a/sgx-jvm/avian/src/main.cpp b/sgx-jvm/avian/src/main.cpp new file mode 100644 index 0000000000..dd6b616666 --- /dev/null +++ b/sgx-jvm/avian/src/main.cpp @@ -0,0 +1,295 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "stdlib.h" +#include "stdio.h" +#include "string.h" +#include "jni.h" + +#include +#include "avian/finder.h" + +#include + +#if (defined __MINGW32__) || (defined _MSC_VER) +#define PATH_SEPARATOR ';' +#else +#define PATH_SEPARATOR ':' +#endif + +#ifdef _MSC_VER + +#define not! +#define or || +#define and && +#define xor ^ + +#endif // not _MSC_VER + +#ifdef BOOT_LIBRARY + +// since we aren't linking against libstdc++, we must implement this +// ourselves: +extern "C" void __cxa_pure_virtual(void) +{ + abort(); +} + +// we link against a System implmentation, which requires this at link +// time, but it should not be used at runtime: +extern "C" uint64_t vmNativeCall(void*, void*, unsigned, unsigned) +{ + abort(); + // abort is not declared __declspec(noreturn) on MSVC, so we have to + // pretend it might return to make the compiler happy: + return 0; +} + +#endif // BOOT_LIBRARY + +namespace { + +const char* mainClass(const char* jar) +{ + using namespace vm; + + System* system = makeSystem(); + + class MyAllocator : public avian::util::Alloc { + public: + MyAllocator(System* s) : s(s) + { + } + + virtual void* allocate(size_t size) + { + void* p = s->tryAllocate(size); + if (p == 0) { + abort(s); + } + return p; + } + + virtual void free(const void* p, size_t) + { + s->free(p); + } + + System* s; + } allocator(system); + + Finder* finder = makeFinder(system, &allocator, jar, 0); + + char* result = 0; + + System::Region* region = finder->find("META-INF/MANIFEST.MF"); + if (region) { + size_t start = 0; + size_t length; + while (readLine(region->start(), region->length(), &start, &length)) { + const unsigned PrefixLength = 12; + if (strncasecmp("Main-Class: ", + reinterpret_cast(region->start() + start), + PrefixLength) == 0) { + result = static_cast(malloc(length + 1 - PrefixLength)); + memcpy(result, + region->start() + start + PrefixLength, + length - PrefixLength); + result[length - PrefixLength] = 0; + break; + } + start += length; + } + + region->dispose(); + } + + finder->dispose(); + + system->dispose(); + + return result; +} + +void usageAndExit(const char* name) +{ + fprintf( + stderr, + "usage: %s\n" + "\t[{-cp|-classpath} ]\n" + "\t[-Xmx]\n" + "\t[-Xss]\n" + "\t[-Xbootclasspath/p:]\n" + "\t[-Xbootclasspath:]\n" + "\t[-Xbootclasspath/a:]\n" + "\t[-D= ...]\n" + "\t{|-jar } [ ...]\n", + name); + exit(-1); +} + +} // namespace + +int main(int ac, const char** av) +{ + JavaVMInitArgs vmArgs; + vmArgs.version = JNI_VERSION_1_2; + vmArgs.nOptions = 1; + vmArgs.ignoreUnrecognized = JNI_TRUE; + + const char* class_ = 0; + const char* jar = 0; + int argc = 0; + const char** argv = 0; + const char* classpath = "."; + + for (int i = 1; i < ac; ++i) { + if (strcmp(av[i], "-cp") == 0 or strcmp(av[i], "-classpath") == 0) { + if (i + 1 == ac) + usageAndExit(av[0]); + classpath = av[++i]; + } else if (strcmp(av[i], "-jar") == 0) { + if (i + 1 == ac) + usageAndExit(av[0]); + jar = av[++i]; + } else if (strncmp(av[i], "-X", 2) == 0 or strncmp(av[i], "-D", 2) == 0) { + ++vmArgs.nOptions; + } else if (strcmp(av[i], "-client") == 0 or strcmp(av[i], "-server") == 0) { + // ignore + } else if (strcmp(av[i], "-version") == 0) { + fprintf(stderr, "Avian " AVIAN_VERSION "\n"); + exit(0); + } else { + if (jar == 0) { + class_ = av[i++]; + } + if (i < ac) { + argc = ac - i; + argv = av + i; + i = ac; + } + } + } + + if (jar) { + classpath = jar; + + class_ = mainClass(jar); + + if (class_ == 0) { + fprintf(stderr, "Main-Class manifest header not found in %s\n", jar); + exit(-1); + } + } + +#ifdef BOOT_LIBRARY + ++vmArgs.nOptions; +#endif + +#ifdef BOOT_IMAGE + vmArgs.nOptions += 2; +#endif + +#ifdef BOOT_BUILTINS + ++vmArgs.nOptions; +#endif + + RUNTIME_ARRAY(JavaVMOption, options, vmArgs.nOptions); + vmArgs.options = RUNTIME_ARRAY_BODY(options); + + unsigned optionIndex = 0; + +#ifdef BOOT_IMAGE + vmArgs.options[optionIndex++].optionString + = const_cast("-Davian.bootimage=bootimageBin"); + + vmArgs.options[optionIndex++].optionString + = const_cast("-Davian.codeimage=codeimageBin"); +#endif + +#ifdef BOOT_LIBRARY + vmArgs.options[optionIndex++].optionString + = const_cast("-Davian.bootstrap=" BOOT_LIBRARY); +#endif + +#ifdef BOOT_BUILTINS + vmArgs.options[optionIndex++].optionString + = const_cast("-Davian.builtins=" BOOT_BUILTINS); +#endif + +#define CLASSPATH_PROPERTY "-Djava.class.path=" + + 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); + + vmArgs.options[optionIndex++].optionString + = RUNTIME_ARRAY_BODY(classpathPropertyBuffer); + + for (int i = 1; i < ac; ++i) { + if (strncmp(av[i], "-X", 2) == 0 or strncmp(av[i], "-D", 2) == 0) { + vmArgs.options[optionIndex++].optionString = const_cast(av[i]); + } + } + + if (class_ == 0) { + usageAndExit(av[0]); + } + + JavaVM* vm; + void* env; + JNI_CreateJavaVM(&vm, &env, &vmArgs); + JNIEnv* e = static_cast(env); + + jclass c = 0; + if (not e->ExceptionCheck()) { + c = e->FindClass(class_); + } + + if (jar) { + free(const_cast(class_)); + } + + 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(argc, stringClass, 0); + if (not e->ExceptionCheck()) { + for (int i = 0; i < argc; ++i) { + e->SetObjectArrayElement(a, i, e->NewStringUTF(argv[i])); + } + + e->CallStaticVoidMethod(c, m, a); + } + } + } + } + + int exitCode = 0; + if (e->ExceptionCheck()) { + exitCode = -1; + e->ExceptionDescribe(); + } + + vm->DestroyJavaVM(); + + return exitCode; +} diff --git a/sgx-jvm/avian/src/openjdk/caseSensitive/WS2tcpip.h b/sgx-jvm/avian/src/openjdk/caseSensitive/WS2tcpip.h new file mode 100644 index 0000000000..5caab3e7f6 --- /dev/null +++ b/sgx-jvm/avian/src/openjdk/caseSensitive/WS2tcpip.h @@ -0,0 +1 @@ +#include "ws2tcpip.h" diff --git a/sgx-jvm/avian/src/openjdk/caseSensitive/Wincon.h b/sgx-jvm/avian/src/openjdk/caseSensitive/Wincon.h new file mode 100644 index 0000000000..40d2ad2a68 --- /dev/null +++ b/sgx-jvm/avian/src/openjdk/caseSensitive/Wincon.h @@ -0,0 +1,3 @@ +// Console_md.c #includes "Wincon.h", which only matches "wincon.h" on +// a case insensive filesystem, so we redirect here. +#include "wincon.h" diff --git a/sgx-jvm/avian/src/openjdk/jni_md.h b/sgx-jvm/avian/src/openjdk/jni_md.h new file mode 100644 index 0000000000..555c772249 --- /dev/null +++ b/sgx-jvm/avian/src/openjdk/jni_md.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef JNI_MD_H +#define JNI_MD_H + +#include "stdint.h" + +#if (defined __MINGW32__) || (defined _MSC_VER) +#define JNIEXPORT __declspec(dllexport) +#define JNICALL __stdcall +#else // not (defined __MINGW32__) || (defined _MSC_VER) +#define JNIEXPORT __attribute__((visibility("default"))) __attribute__((used)) +#define JNICALL +#endif // not (defined __MINGW32__) || (defined _MSC_VER) + +#define JNIIMPORT + +typedef int32_t jint; +typedef int64_t jlong; +typedef int8_t jbyte; + +#endif // JNI_MD_H diff --git a/sgx-jvm/avian/src/openjdk/my_java_props_macosx.c b/sgx-jvm/avian/src/openjdk/my_java_props_macosx.c new file mode 100644 index 0000000000..f6833283f6 --- /dev/null +++ b/sgx-jvm/avian/src/openjdk/my_java_props_macosx.c @@ -0,0 +1,34 @@ +#include "java_props_macosx.h" + +PreferredToolkit +getPreferredToolkit() +{ + return XToolkit; +} + +void +setOSNameAndVersion(java_props_t* props) +{ + props->os_name = strdup("iOS"); + props->os_version = strdup("Unknown"); +} + +void +setProxyProperties(java_props_t* props) +{ + // ignore +} + +void +setUserHome(java_props_t* props) +{ + // ignore +} + +char* +setupMacOSXLocale(int cat) +{ + return 0; +} + +char* environ[0]; diff --git a/sgx-jvm/avian/src/openjdk/my_management.c b/sgx-jvm/avian/src/openjdk/my_management.c new file mode 100644 index 0000000000..7f117bf58b --- /dev/null +++ b/sgx-jvm/avian/src/openjdk/my_management.c @@ -0,0 +1,2 @@ +#define JNI_OnLoad management_JNI_OnLoad +#include "management.c" diff --git a/sgx-jvm/avian/src/openjdk/my_net_util.c b/sgx-jvm/avian/src/openjdk/my_net_util.c new file mode 100644 index 0000000000..11728df231 --- /dev/null +++ b/sgx-jvm/avian/src/openjdk/my_net_util.c @@ -0,0 +1,20 @@ +#define JNI_OnLoad net_JNI_OnLoad +#include "net_util.c" + +#ifdef _WIN32 + +#undef IN6_SET_ADDR_UNSPECIFIED +#define IN6_SET_ADDR_UNSPECIFIED(a) \ + memset((a)->s6_bytes,0,sizeof(struct in6_addr)) + +void +IN6ADDR_SETANY(struct sockaddr_in6 *a) +{ + a->sin6_family = AF_INET6; + a->sin6_port = 0; + a->sin6_flowinfo = 0; + IN6_SET_ADDR_UNSPECIFIED(&a->sin6_addr); + a->sin6_scope_id = 0; +} + +#endif diff --git a/sgx-jvm/avian/src/openjdk/stubs.cpp b/sgx-jvm/avian/src/openjdk/stubs.cpp new file mode 100644 index 0000000000..c2175fdd34 --- /dev/null +++ b/sgx-jvm/avian/src/openjdk/stubs.cpp @@ -0,0 +1,18 @@ +#include "avian/machine.h" + +using namespace vm; + +extern "C" AVIAN_EXPORT jint JNICALL net_JNI_OnLoad(JavaVM*, void*) +{ + return 0; +} + +extern "C" AVIAN_EXPORT jint JNICALL management_JNI_OnLoad(JavaVM*, void*) +{ + return 0; +} + +extern "C" char* findJavaTZ_md(const char*, const char*) +{ + return 0; +} diff --git a/sgx-jvm/avian/src/powerpc-regs.S b/sgx-jvm/avian/src/powerpc-regs.S new file mode 100644 index 0000000000..da5940f403 --- /dev/null +++ b/sgx-jvm/avian/src/powerpc-regs.S @@ -0,0 +1,64 @@ +#define r0 0 +#define r1 1 +#define r2 2 +#define r3 3 +#define r4 4 +#define r5 5 +#define r6 6 +#define r7 7 +#define r8 8 +#define r9 9 +#define r10 10 +#define r11 11 +#define r12 12 +#define r13 13 +#define r14 14 +#define r15 15 +#define r16 16 +#define r17 17 +#define r18 18 +#define r19 19 +#define r20 20 +#define r21 21 +#define r22 22 +#define r23 23 +#define r24 24 +#define r25 25 +#define r26 26 +#define r27 27 +#define r28 28 +#define r29 29 +#define r30 30 +#define r31 31 +#define f0 0 +#define f1 1 +#define f2 2 +#define f3 3 +#define f4 4 +#define f5 5 +#define f6 6 +#define f7 7 +#define f8 8 +#define f9 9 +#define f10 10 +#define f11 11 +#define f12 12 +#define f13 13 +#define f14 14 +#define f15 15 +#define f16 16 +#define f17 17 +#define f18 18 +#define f19 19 +#define f20 20 +#define f21 21 +#define f22 22 +#define f23 23 +#define f24 24 +#define f25 25 +#define f26 26 +#define f27 27 +#define f28 28 +#define f29 29 +#define f30 30 +#define f31 31 diff --git a/sgx-jvm/avian/src/process.cpp b/sgx-jvm/avian/src/process.cpp new file mode 100644 index 0000000000..ba6cfd5809 --- /dev/null +++ b/sgx-jvm/avian/src/process.cpp @@ -0,0 +1,310 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/process.h" + +#include + +using namespace vm; + +namespace { + +unsigned mangledSize(int8_t c) +{ + switch (c) { + case '_': + case ';': + case '[': + return 2; + + case '$': + return 6; + + default: + return 1; + } +} + +unsigned mangle(int8_t c, char* dst) +{ + switch (c) { + case '/': + dst[0] = '_'; + return 1; + + case '_': + dst[0] = '_'; + dst[1] = '1'; + return 2; + + case ';': + dst[0] = '_'; + dst[1] = '2'; + return 2; + + case '[': + dst[0] = '_'; + dst[1] = '3'; + return 2; + + case '$': + memcpy(dst, "_00024", 6); + return 6; + + default: + dst[0] = c; + return 1; + } +} + +unsigned jniNameLength(Thread* t UNUSED, GcMethod* method, bool decorate) +{ + unsigned size = 0; + + GcByteArray* className = method->class_()->name(); + for (unsigned i = 0; i < className->length() - 1; ++i) { + size += mangledSize(className->body()[i]); + } + + ++size; + + GcByteArray* methodName = method->name(); + for (unsigned i = 0; i < methodName->length() - 1; ++i) { + size += mangledSize(methodName->body()[i]); + } + + if (decorate) { + size += 2; + + GcByteArray* methodSpec = method->spec(); + for (unsigned i = 1; + i < methodSpec->length() - 1 and methodSpec->body()[i] != ')'; + ++i) { + size += mangledSize(methodSpec->body()[i]); + } + } + + return size; +} + +void makeJNIName(Thread* t UNUSED, + const char* prefix, + unsigned prefixLength, + char* name, + GcMethod* method, + bool decorate) +{ + memcpy(name, prefix, prefixLength); + name += prefixLength; + + GcByteArray* className = method->class_()->name(); + for (unsigned i = 0; i < className->length() - 1; ++i) { + name += mangle(className->body()[i], name); + } + + *(name++) = '_'; + + GcByteArray* methodName = method->name(); + for (unsigned i = 0; i < methodName->length() - 1; ++i) { + name += mangle(methodName->body()[i], name); + } + + if (decorate) { + *(name++) = '_'; + *(name++) = '_'; + + GcByteArray* methodSpec = method->spec(); + for (unsigned i = 1; + i < methodSpec->length() - 1 and methodSpec->body()[i] != ')'; + ++i) { + name += mangle(methodSpec->body()[i], name); + } + } + + *(name++) = 0; +} + +void* resolveNativeMethod(Thread* t, + const char* undecorated, + const char* decorated) +{ + for (System::Library* lib = t->m->libraries; lib; lib = lib->next()) { + void* p = lib->resolve(undecorated); + if (p) { + return p; + } else { + p = lib->resolve(decorated); + if (p) { + return p; + } + } + } + + return 0; +} + +void* resolveNativeMethod(Thread* t, + GcMethod* method, + const char* prefix, + unsigned prefixLength, + int footprint UNUSED) +{ + unsigned undecoratedSize = prefixLength + jniNameLength(t, method, false); + // extra 6 is for code below: + THREAD_RUNTIME_ARRAY(t, char, undecorated, undecoratedSize + 1 + 6); + makeJNIName(t, + prefix, + prefixLength, + RUNTIME_ARRAY_BODY(undecorated) + 1, + method, + false); + + unsigned decoratedSize = prefixLength + jniNameLength(t, method, true); + // extra 6 is for code below: + THREAD_RUNTIME_ARRAY(t, char, decorated, decoratedSize + 1 + 6); + makeJNIName( + t, prefix, prefixLength, RUNTIME_ARRAY_BODY(decorated) + 1, method, true); + + void* p = resolveNativeMethod(t, + RUNTIME_ARRAY_BODY(undecorated) + 1, + RUNTIME_ARRAY_BODY(decorated) + 1); + if (p) { + return p; + } + +#ifdef PLATFORM_WINDOWS + // on windows, we also try the _%s@%d and %s@%d variants + if (footprint == -1) { + footprint = method->parameterFootprint() + 1; + if (method->flags() & ACC_STATIC) { + ++footprint; + } + } + + *RUNTIME_ARRAY_BODY(undecorated) = '_'; + vm::snprintf(RUNTIME_ARRAY_BODY(undecorated) + undecoratedSize + 1, + 5, + "@%d", + footprint * BytesPerWord); + + *RUNTIME_ARRAY_BODY(decorated) = '_'; + vm::snprintf(RUNTIME_ARRAY_BODY(decorated) + decoratedSize + 1, + 5, + "@%d", + footprint * BytesPerWord); + + p = resolveNativeMethod( + t, RUNTIME_ARRAY_BODY(undecorated), RUNTIME_ARRAY_BODY(decorated)); + if (p) { + return p; + } + + // one more try without the leading underscore + p = resolveNativeMethod(t, + RUNTIME_ARRAY_BODY(undecorated) + 1, + RUNTIME_ARRAY_BODY(decorated) + 1); + if (p) { + return p; + } +#endif + + return 0; +} + +GcNative* resolveNativeMethod(Thread* t, GcMethod* method) +{ + void* p = resolveNativeMethod(t, method, "Avian_", 6, 3); + if (p) { + return makeNative(t, p, true); + } + + p = resolveNativeMethod(t, method, "Java_", 5, -1); + if (p) { + return makeNative(t, p, false); + } + + return 0; +} + +} // namespace + +namespace vm { + +void resolveNative(Thread* t, GcMethod* method) +{ + PROTECT(t, method); + + assertT(t, method->flags() & ACC_NATIVE); + + initClass(t, method->class_()); + + if (getMethodRuntimeData(t, method)->native() == 0) { + GcNative* native = resolveNativeMethod(t, method); + if (UNLIKELY(native == 0)) { + throwNew(t, + GcUnsatisfiedLinkError::Type, + "%s.%s%s", + method->class_()->name()->body().begin(), + method->name()->body().begin(), + method->spec()->body().begin()); + } + + PROTECT(t, native); + + GcMethodRuntimeData* runtimeData = getMethodRuntimeData(t, method); + + // ensure other threads only see the methodRuntimeDataNative field + // populated once the object it points to has been populated: + storeStoreMemoryBarrier(); + + runtimeData->setNative(t, native); + } +} + +int findLineNumber(Thread* t UNUSED, GcMethod* method, unsigned ip) +{ + if (method->flags() & ACC_NATIVE) { + return NativeLine; + } + + // our parameter indicates the instruction following the one we care + // about, so we back up first: + --ip; + + GcLineNumberTable* lnt = method->code()->lineNumberTable(); + if (lnt) { + unsigned bottom = 0; + unsigned top = lnt->length(); + for (unsigned span = top - bottom; span; span = top - bottom) { + unsigned middle = bottom + (span / 2); + uint64_t ln = lnt->body()[middle]; + + if (ip >= lineNumberIp(ln) + and (middle + 1 == lnt->length() + or ip < lineNumberIp(lnt->body()[middle + 1]))) { + return lineNumberLine(ln); + } else if (ip < lineNumberIp(ln)) { + top = middle; + } else if (ip > lineNumberIp(ln)) { + bottom = middle + 1; + } + } + + if (top < lnt->length()) { + return lineNumberLine(lnt->body()[top]); + } else { + return UnknownLine; + } + } else { + return UnknownLine; + } +} + +} // namespace vm diff --git a/sgx-jvm/avian/src/system/CMakeLists.txt b/sgx-jvm/avian/src/system/CMakeLists.txt new file mode 100644 index 0000000000..5c6f2a58fa --- /dev/null +++ b/sgx-jvm/avian/src/system/CMakeLists.txt @@ -0,0 +1,7 @@ + +if (MSVC) + #todo: support mingw compiler + add_library(avian_system windows.cpp windows/crash.cpp) +else() + add_library(avian_system posix.cpp posix/crash.cpp) +endif() diff --git a/sgx-jvm/avian/src/system/posix.cpp b/sgx-jvm/avian/src/system/posix.cpp new file mode 100644 index 0000000000..64a667887c --- /dev/null +++ b/sgx-jvm/avian/src/system/posix.cpp @@ -0,0 +1,1002 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef __STDC_CONSTANT_MACROS +#define __STDC_CONSTANT_MACROS +#endif + +#include "sys/types.h" +#ifdef __APPLE__ +#include "CoreFoundation/CoreFoundation.h" +#include "sys/ucontext.h" +#undef assert +#elif defined(__ANDROID__) +#include /* for sigcontext */ +#include /* for stack_t */ +typedef struct ucontext { + unsigned long uc_flags; + struct ucontext* uc_link; + stack_t uc_stack; + struct sigcontext uc_mcontext; + unsigned long uc_sigmask; +} ucontext_t; +#else +#if defined __FreeBSD__ +#include "limits.h" +#endif +#include "ucontext.h" +#endif + +#include "sys/mman.h" + +#include "sys/stat.h" +#include "sys/time.h" +#include "time.h" +#include "fcntl.h" +#include "dlfcn.h" +#include "errno.h" +#include "unistd.h" +#include "pthread.h" +#include "signal.h" +#include "stdint.h" +#include "dirent.h" +#include "sched.h" + +#include +#include + +#include +#include +#include +#include + +#define ACQUIRE(x) MutexResource MAKE_NAME(mutexResource_)(x) + +using namespace vm; +using namespace avian::util; + +namespace { + +class MutexResource { + public: + MutexResource(pthread_mutex_t& m) : m(&m) + { + pthread_mutex_lock(&m); + } + + ~MutexResource() + { + pthread_mutex_unlock(m); + } + + private: + pthread_mutex_t* m; +}; + +const int VisitSignal = SIGUSR1; +const unsigned VisitSignalIndex = 0; +const int InterruptSignal = SIGUSR2; +const unsigned InterruptSignalIndex = 1; +const int PipeSignal = SIGPIPE; +const unsigned PipeSignalIndex = 2; + +const int signals[] = {VisitSignal, InterruptSignal, PipeSignal}; + +const unsigned SignalCount = 3; + +class MySystem; +MySystem* globalSystem; + +void handleSignal(int signal, siginfo_t* info, void* context); + +void* run(void* r) +{ + static_cast(r)->run(); + return 0; +} + +void pathOfExecutable(System* s, const char** retBuf, unsigned* size) +{ +#ifdef __APPLE__ + CFBundleRef bundle = CFBundleGetMainBundle(); + CFURLRef url = CFBundleCopyExecutableURL(bundle); + CFStringRef path = CFURLCopyPath(url); + path = CFURLCreateStringByReplacingPercentEscapes( + kCFAllocatorDefault, path, CFSTR("")); + CFIndex pathSize = CFStringGetMaximumSizeOfFileSystemRepresentation(path); + char* buffer = reinterpret_cast(allocate(s, pathSize)); + if (CFStringGetFileSystemRepresentation(path, buffer, pathSize)) { + *size = pathSize; + *retBuf = buffer; + } else { + abort(); + } +#else + if (s) + *size = 0; + *retBuf = NULL; +#endif +} + +const bool Verbose = false; + +const unsigned Notified = 1 << 0; + +class MySystem : public System { + public: + class Thread : public System::Thread { + public: + Thread(System* s, System::Runnable* r) : s(s), r(r), next(0), flags(0) + { + pthread_mutex_init(&mutex, 0); + pthread_cond_init(&condition, 0); + } + + virtual void interrupt() + { + ACQUIRE(mutex); + + r->setInterrupted(true); + + pthread_kill(thread, InterruptSignal); + + // pthread_kill won't necessarily wake a thread blocked in + // pthread_cond_{timed}wait (it does on Linux but not Mac OS), + // so we signal the condition as well: + int rv UNUSED = pthread_cond_signal(&condition); + expect(s, rv == 0); + } + + virtual bool getAndClearInterrupted() + { + ACQUIRE(mutex); + + bool interrupted = r->interrupted(); + + r->setInterrupted(false); + + return interrupted; + } + + virtual void join() + { + int rv UNUSED = pthread_join(thread, 0); + expect(s, rv == 0); + } + + virtual void dispose() + { + pthread_mutex_destroy(&mutex); + pthread_cond_destroy(&condition); + ::free(this); + } + + pthread_t thread; + pthread_mutex_t mutex; + pthread_cond_t condition; + System* s; + System::Runnable* r; + Thread* next; + unsigned flags; + }; + + class Mutex : public System::Mutex { + public: + Mutex(System* s) : s(s) + { + pthread_mutex_init(&mutex, 0); + } + + virtual void acquire() + { + pthread_mutex_lock(&mutex); + } + + virtual void release() + { + pthread_mutex_unlock(&mutex); + } + + virtual void dispose() + { + pthread_mutex_destroy(&mutex); + ::free(this); + } + + System* s; + pthread_mutex_t mutex; + }; + + class Monitor : public System::Monitor { + public: + Monitor(System* s) : s(s), owner_(0), first(0), last(0), depth(0) + { + pthread_mutex_init(&mutex, 0); + } + + virtual bool tryAcquire(System::Thread* context) + { + Thread* t = static_cast(context); + + if (owner_ == t) { + ++depth; + return true; + } else { + switch (pthread_mutex_trylock(&mutex)) { + case EBUSY: + return false; + + case 0: + owner_ = t; + ++depth; + return true; + + default: + sysAbort(s); + } + } + } + + virtual void acquire(System::Thread* context) + { + Thread* t = static_cast(context); + + if (owner_ != t) { + pthread_mutex_lock(&mutex); + owner_ = t; + } + ++depth; + } + + virtual void release(System::Thread* context) + { + Thread* t = static_cast(context); + + if (owner_ == t) { + if (--depth == 0) { + owner_ = 0; + pthread_mutex_unlock(&mutex); + } + } else { + sysAbort(s); + } + } + + void append(Thread* t) + { +#ifndef NDEBUG + for (Thread* x = first; x; x = x->next) { + expect(s, t != x); + } +#endif + + if (last) { + expect(s, t != last); + last->next = t; + last = t; + } else { + first = last = t; + } + } + + void remove(Thread* t) + { + Thread* previous = 0; + for (Thread* current = first; current;) { + if (t == current) { + if (current == first) { + first = t->next; + } else { + expect(s, previous != t->next); + previous->next = t->next; + } + + if (current == last) { + last = previous; + } + + t->next = 0; + + break; + } else { + previous = current; + current = current->next; + } + } + +#ifndef NDEBUG + for (Thread* x = first; x; x = x->next) { + expect(s, t != x); + } +#endif + } + + virtual void wait(System::Thread* context, int64_t time) + { + wait(context, time, false); + } + + virtual bool waitAndClearInterrupted(System::Thread* context, int64_t time) + { + return wait(context, time, true); + } + + bool wait(System::Thread* context, int64_t time, bool clearInterrupted) + { + Thread* t = static_cast(context); + + if (owner_ == t) { + // Initialized here to make gcc 4.2 a happy compiler + bool interrupted = false; + bool notified = false; + unsigned depth = 0; + + { + ACQUIRE(t->mutex); + + expect(s, (t->flags & Notified) == 0); + + interrupted = t->r->interrupted(); + if (interrupted and clearInterrupted) { + t->r->setInterrupted(false); + } + + append(t); + + depth = this->depth; + this->depth = 0; + owner_ = 0; + pthread_mutex_unlock(&mutex); + + if (not interrupted) { + // pretend anything greater than one million years (in + // milliseconds) is infinity so as to avoid overflow: + if (time and time < INT64_C(31536000000000000)) { + int64_t then = s->now() + time; + timespec ts = {static_cast(then / 1000), + static_cast((then % 1000) * 1000 * 1000)}; + int rv UNUSED + = pthread_cond_timedwait(&(t->condition), &(t->mutex), &ts); + expect(s, rv == 0 or rv == ETIMEDOUT or rv == EINTR); + } else { + int rv UNUSED = pthread_cond_wait(&(t->condition), &(t->mutex)); + expect(s, rv == 0 or rv == EINTR); + } + + interrupted = t->r->interrupted(); + if (interrupted and clearInterrupted) { + t->r->setInterrupted(false); + } + } + + notified = ((t->flags & Notified) != 0); + } + + pthread_mutex_lock(&mutex); + + { + ACQUIRE(t->mutex); + t->flags = 0; + } + + if (not notified) { + remove(t); + } else { +#ifndef NDEBUG + for (Thread* x = first; x; x = x->next) { + expect(s, t != x); + } +#endif + } + + t->next = 0; + + owner_ = t; + this->depth = depth; + + return interrupted; + } else { + sysAbort(s); + } + } + + void doNotify(Thread* t) + { + ACQUIRE(t->mutex); + + t->flags |= Notified; + int rv UNUSED = pthread_cond_signal(&(t->condition)); + expect(s, rv == 0); + } + + virtual void notify(System::Thread* context) + { + Thread* t = static_cast(context); + + if (owner_ == t) { + if (first) { + Thread* t = first; + first = first->next; + if (t == last) { + expect(s, first == 0); + last = 0; + } + + doNotify(t); + } + } else { + sysAbort(s); + } + } + + virtual void notifyAll(System::Thread* context) + { + Thread* t = static_cast(context); + + if (owner_ == t) { + for (Thread* t = first; t; t = t->next) { + doNotify(t); + } + first = last = 0; + } else { + sysAbort(s); + } + } + + virtual System::Thread* owner() + { + return owner_; + } + + virtual void dispose() + { + expect(s, owner_ == 0); + pthread_mutex_destroy(&mutex); + ::free(this); + } + + System* s; + pthread_mutex_t mutex; + Thread* owner_; + Thread* first; + Thread* last; + unsigned depth; + }; + + class Local : public System::Local { + public: + Local(System* s) : s(s) + { + int r UNUSED = pthread_key_create(&key, 0); + expect(s, r == 0); + } + + virtual void* get() + { + return pthread_getspecific(key); + } + + virtual void set(void* p) + { + int r UNUSED = pthread_setspecific(key, p); + expect(s, r == 0); + } + + virtual void dispose() + { + int r UNUSED = pthread_key_delete(key); + expect(s, r == 0); + + ::free(this); + } + + System* s; + pthread_key_t key; + }; + + class Region : public System::Region { + public: + Region(System* s, uint8_t* start, size_t length) + : s(s), start_(start), length_(length) + { + } + + virtual const uint8_t* start() + { + return start_; + } + + virtual size_t length() + { + return length_; + } + + virtual void dispose() + { + if (start_) { + munmap(start_, length_); + } + ::free(this); + } + + System* s; + uint8_t* start_; + size_t length_; + }; + + class Directory : public System::Directory { + public: + Directory(System* s, DIR* directory) : s(s), directory(directory) + { + } + + virtual const char* next() + { + if (directory) { + dirent* e = readdir(directory); + if (e) { + return e->d_name; + } + } + return 0; + } + + virtual void dispose() + { + if (directory) { + closedir(directory); + } + ::free(this); + } + + System* s; + DIR* directory; + }; + + class Library : public System::Library { + public: + Library(System* s, + void* p, + const char* name, + unsigned nameLength, + bool isMain) + : s(s), + p(p), + mainExecutable(isMain), + name_(name), + nameLength(nameLength), + next_(0) + { + } + + virtual void* resolve(const char* function) + { + return dlsym(p, function); + } + + virtual const char* name() + { + return name_; + } + + virtual System::Library* next() + { + return next_; + } + + virtual void setNext(System::Library* lib) + { + next_ = lib; + } + + virtual void disposeAll() + { + if (Verbose) { + fprintf(stderr, "close %p\n", p); + } + + if (not mainExecutable) + dlclose(p); + + if (next_) { + next_->disposeAll(); + } + + if (name_) { + ::free(const_cast(name_)); + } + + ::free(this); + } + + System* s; + void* p; + bool mainExecutable; + const char* name_; + unsigned nameLength; + System::Library* next_; + }; + + MySystem(bool reentrant) : reentrant(reentrant), threadVisitor(0), visitTarget(0) + { + if (not reentrant) { + expect(this, globalSystem == 0); + globalSystem = this; + + expect(this, registerHandler(InterruptSignalIndex)); + expect(this, registerHandler(VisitSignalIndex)); + expect(this, registerHandler(PipeSignalIndex)); + + expect(this, make(&visitLock) == 0); + } + } + + // Returns true on success, false on failure + bool unregisterHandler(int index) + { + return sigaction(signals[index], oldHandlers + index, 0) == 0; + } + + // Returns true on success, false on failure + bool registerHandler(int index) + { + struct sigaction sa; + memset(&sa, 0, sizeof(struct sigaction)); + sigemptyset(&(sa.sa_mask)); + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = handleSignal; + + return sigaction(signals[index], &sa, oldHandlers + index) == 0; + } + + virtual void* tryAllocate(size_t sizeInBytes) + { + return malloc(sizeInBytes); + } + + virtual void free(const void* p) + { + if (p) + ::free(const_cast(p)); + } + + virtual bool success(Status s) { + return s == 0; + } + + virtual Status attach(Runnable* r) + { + Thread* t = new (allocate(this, sizeof(Thread))) Thread(this, r); + t->thread = pthread_self(); + r->attach(t); + return 0; + } + + virtual Status start(Runnable* r) + { + Thread* t = new (allocate(this, sizeof(Thread))) Thread(this, r); + r->attach(t); + int rv UNUSED = pthread_create(&(t->thread), 0, run, r); + expect(this, rv == 0); + return 0; + } + + virtual Status make(System::Mutex** m) + { + *m = new (allocate(this, sizeof(Mutex))) Mutex(this); + return 0; + } + + virtual Status make(System::Monitor** m) + { + *m = new (allocate(this, sizeof(Monitor))) Monitor(this); + return 0; + } + + virtual Status make(System::Local** l) + { + *l = new (allocate(this, sizeof(Local))) Local(this); + return 0; + } + + virtual Status visit(System::Thread* st UNUSED, + System::Thread* sTarget, + ThreadVisitor* visitor) + { + expect(this, not reentrant); + + assertT(this, st != sTarget); + + Thread* target = static_cast(sTarget); + +#ifdef __APPLE__ + // On Mac OS, signals sent using pthread_kill are never delivered + // if the target thread is blocked (e.g. acquiring a lock or + // waiting on a condition), so we can't rely on it and must use + // the Mach-specific thread execution API instead. + + mach_port_t port = pthread_mach_thread_np(target->thread); + + if (thread_suspend(port)) + return -1; + + THREAD_STATE_TYPE state; + mach_msg_type_number_t stateCount = THREAD_STATE_COUNT; + kern_return_t rv + = thread_get_state(port, + THREAD_STATE, + reinterpret_cast(&state), + &stateCount); + + if (rv == 0) { + visitor->visit(reinterpret_cast(THREAD_STATE_IP(state)), + reinterpret_cast(THREAD_STATE_STACK(state)), + reinterpret_cast(THREAD_STATE_LINK(state))); + } + + thread_resume(port); + + return rv ? -1 : 0; +#else // not __APPLE__ + Thread* t = static_cast(st); + + ACQUIRE_MONITOR(t, visitLock); + + while (threadVisitor) + visitLock->wait(t, 0); + + threadVisitor = visitor; + visitTarget = target; + + int rv = pthread_kill(target->thread, VisitSignal); + + int result; + if (rv == 0) { + while (visitTarget) + visitLock->wait(t, 0); + + result = 0; + } else { + visitTarget = 0; + + result = -1; + } + + threadVisitor = 0; + + globalSystem->visitLock->notifyAll(t); + + return result; +#endif // not __APPLE__ + } + + virtual Status map(System::Region** region, const char* name) + { + Status status = 1; + + int fd = ::open(name, O_RDONLY); + if (fd != -1) { + struct stat s; + int r = fstat(fd, &s); + if (r != -1) { + void* data = mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (data) { + *region = new (allocate(this, sizeof(Region))) + Region(this, static_cast(data), s.st_size); + status = 0; + } + } + close(fd); + } + + return status; + } + + virtual Status open(System::Directory** directory, const char* name) + { + Status status = 1; + + DIR* d = opendir(name); + if (d) { + *directory = new (allocate(this, sizeof(Directory))) Directory(this, d); + status = 0; + } + + return status; + } + + virtual FileType stat(const char* name, size_t* length) + { +#ifdef __FreeBSD__ + // Now the hack below causes the error "Dereferencing type-punned + // pointer will break strict aliasing rules", so another workaround + // is needed... + struct stat ss; + struct stat* s = &ss; +#else + // Ugly Hack Alert: It seems that the Apple iOS Simulator's stat + // implementation writes beyond the end of the struct stat we pass + // it, which can clobber unrelated parts of the stack. Perhaps + // this is due to some kind of header/library mismatch, but I've + // been unable to track it down so far. The workaround is to give + // it 8 words more than it should need, where 8 is a number I just + // made up and seems to work. + void* array[ceilingDivide(sizeof(struct stat), sizeof(void*)) + 8]; + struct stat* s = reinterpret_cast(array); +#endif + + int r = ::stat(name, s); + if (r == 0) { + if (S_ISREG(s->st_mode)) { + *length = s->st_size; + return TypeFile; + } else if (S_ISDIR(s->st_mode)) { + *length = 0; + return TypeDirectory; + } else { + *length = 0; + return TypeUnknown; + } + } else { + *length = 0; + return TypeDoesNotExist; + } + } + + virtual const char* libraryPrefix() + { + return SO_PREFIX; + } + + virtual const char* librarySuffix() + { + return SO_SUFFIX; + } + + virtual const char* toAbsolutePath(AllocOnly* allocator, const char* name) + { + if (name[0] == '/') { + return copy(allocator, name); + } else { + char buffer[PATH_MAX]; + return append(allocator, getcwd(buffer, PATH_MAX), "/", name); + } + } + + virtual Status load(System::Library** lib, const char* name) + { + unsigned nameLength = (name ? strlen(name) : 0); + bool isMain = name == 0; + if (isMain) { + pathOfExecutable(this, &name, &nameLength); + } + void* p = dlopen(name, RTLD_LAZY | RTLD_LOCAL); + + if (p) { + if (Verbose) { + fprintf(stderr, "open %s as %p\n", name, p); + } + + char* n; + if (name) { + n = static_cast(allocate(this, nameLength + 1)); + memcpy(n, name, nameLength + 1); + if (isMain) { + free(name); + } + } else { + n = 0; + } + + *lib = new (allocate(this, sizeof(Library))) + Library(this, p, n, nameLength, isMain); + + return 0; + } else { + if (Verbose) { + fprintf(stderr, "dlerror opening %s: %s\n", name, dlerror()); + } + return 1; + } + } + + virtual char pathSeparator() + { + return ':'; + } + + virtual char fileSeparator() + { + return '/'; + } + + virtual int64_t now() + { + timeval tv = {0, 0}; + gettimeofday(&tv, 0); + return (static_cast(tv.tv_sec) * 1000) + + (static_cast(tv.tv_usec) / 1000); + } + + virtual void yield() + { + sched_yield(); + } + + virtual void exit(int code) + { + ::exit(code); + } + + virtual void abort() + { + avian::system::crash(); + } + + virtual void dispose() + { + if (not reentrant) { + visitLock->dispose(); + + expect(this, unregisterHandler(InterruptSignalIndex)); + expect(this, unregisterHandler(VisitSignalIndex)); + expect(this, unregisterHandler(PipeSignalIndex)); + globalSystem = 0; + } + + ::free(this); + } + + struct sigaction oldHandlers[SignalCount]; + + bool reentrant; + ThreadVisitor* threadVisitor; + Thread* visitTarget; + System::Monitor* visitLock; +}; + +void handleSignal(int signal, siginfo_t*, void* context) +{ + ucontext_t* c = static_cast(context); + + void* ip = reinterpret_cast(IP_REGISTER(c)); + void* stack = reinterpret_cast(STACK_REGISTER(c)); + void* link = reinterpret_cast(LINK_REGISTER(c)); + + switch (signal) { + case VisitSignal: { + globalSystem->threadVisitor->visit(ip, stack, link); + + System::Thread* t = globalSystem->visitTarget; + globalSystem->visitTarget = 0; + + ACQUIRE_MONITOR(t, globalSystem->visitLock); + globalSystem->visitLock->notifyAll(t); + } break; + + case InterruptSignal: + case PipeSignal: + break; + + default: + abort(); + } +} + +} // namespace + +namespace vm { + +AVIAN_EXPORT System* makeSystem(bool reentrant) +{ + return new (malloc(sizeof(MySystem))) MySystem(reentrant); +} + +} // namespace vm diff --git a/sgx-jvm/avian/src/system/posix/crash.cpp b/sgx-jvm/avian/src/system/posix/crash.cpp new file mode 100644 index 0000000000..c275e6fb29 --- /dev/null +++ b/sgx-jvm/avian/src/system/posix/crash.cpp @@ -0,0 +1,22 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include + +namespace avian { +namespace system { + +NO_RETURN void crash() +{ + abort(); +} + +} // namespace system +} // namespace avian diff --git a/sgx-jvm/avian/src/system/posix/memory.cpp b/sgx-jvm/avian/src/system/posix/memory.cpp new file mode 100644 index 0000000000..4b00666bed --- /dev/null +++ b/sgx-jvm/avian/src/system/posix/memory.cpp @@ -0,0 +1,63 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include + +#include + +#include "sys/mman.h" + +namespace avian { +namespace system { + +const size_t Memory::PageSize = 1 << 12; + +util::Slice Memory::allocate(size_t sizeInBytes, + Permissions perms) +{ + unsigned prot = 0; + if(perms & Read) { + prot |= PROT_READ; + } + if(perms & Write) { + prot |= PROT_WRITE; + } + if(perms & Execute) { + prot |= PROT_EXEC; + } +#ifdef MAP_32BIT + // map to the lower 32 bits of memory when possible so as to avoid + // expensive relative jumps + const unsigned Extra = MAP_32BIT; +#else + const unsigned Extra = 0; +#endif + + void* p = mmap(0, + sizeInBytes, + prot, + MAP_PRIVATE | MAP_ANON | Extra, + -1, + 0); + + if (p == MAP_FAILED) { + return util::Slice(0, 0); + } else { + return util::Slice(static_cast(p), sizeInBytes); + } +} + +void Memory::free(util::Slice pages) +{ + munmap(const_cast(pages.begin()), pages.count); +} + +} // namespace system +} // namespace avian diff --git a/sgx-jvm/avian/src/system/posix/signal.cpp b/sgx-jvm/avian/src/system/posix/signal.cpp new file mode 100644 index 0000000000..9d8b7c7463 --- /dev/null +++ b/sgx-jvm/avian/src/system/posix/signal.cpp @@ -0,0 +1,227 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "signal.h" +#include "sys/types.h" +#ifdef __APPLE__ +#include "CoreFoundation/CoreFoundation.h" +#include "sys/ucontext.h" +#undef assert +#elif defined(__ANDROID__) +#include /* for sigcontext */ +#include /* for stack_t */ +typedef struct ucontext { + unsigned long uc_flags; + struct ucontext* uc_link; + stack_t uc_stack; + struct sigcontext uc_mcontext; + unsigned long uc_sigmask; +} ucontext_t; +#else +#if defined __FreeBSD__ +#include "limits.h" +#endif +#include "ucontext.h" +#endif + +#include "avian/arch.h" +#include +#include + +namespace avian { +namespace system { + +namespace posix { + +const int InvalidSignal = -1; +const int SegFaultSignal = SIGSEGV; +const unsigned SegFaultSignalIndex = 0; +#ifdef __APPLE__ +const int AltSegFaultSignal = SIGBUS; +#else +const int AltSegFaultSignal = InvalidSignal; +#endif +const unsigned AltSegFaultSignalIndex = 1; +const int DivideByZeroSignal = SIGFPE; +const unsigned DivideByZeroSignalIndex = 2; + +const int signals[] = {SegFaultSignal, AltSegFaultSignal, DivideByZeroSignal}; + +const unsigned SignalCount = 3; +} + +struct SignalRegistrar::Data { + Handler* handlers[posix::SignalCount]; + struct sigaction oldHandlers[posix::SignalCount]; + + bool registerHandler(Handler* handler, int index); + + Data() + { + if (instance) { + crash(); + } + + instance = this; + } + + ~Data() + { + instance = 0; + } + + static SignalRegistrar::Data* instance; +}; + +SignalRegistrar::Data* SignalRegistrar::Data::instance = 0; + +namespace posix { + +using namespace vm; + +void handleSignal(int signal, siginfo_t*, void* context) +{ + ucontext_t* c = static_cast(context); + + void* ip = reinterpret_cast(IP_REGISTER(c)); + void* stack = reinterpret_cast(STACK_REGISTER(c)); + void* thread = reinterpret_cast(THREAD_REGISTER(c)); +#ifdef FRAME_REGISTER + void* frame = reinterpret_cast(FRAME_REGISTER(c)); +#else + void* frame = 0; +#endif + + unsigned index; + + switch (signal) { + case SegFaultSignal: + case AltSegFaultSignal: + case DivideByZeroSignal: { + switch (signal) { + case SegFaultSignal: + index = SegFaultSignalIndex; + break; + + case AltSegFaultSignal: + index = AltSegFaultSignalIndex; + break; + + case DivideByZeroSignal: + index = DivideByZeroSignalIndex; + break; + + default: + crash(); + } + + bool jump = SignalRegistrar::Data::instance->handlers[index]->handleSignal( + &ip, &frame, &stack, &thread); + + if (jump) { + // I'd like to use setcontext here (and get rid of the + // sigprocmask call), but it doesn't work on my Linux x86_64 + // system, and I can't tell from the documentation if it's even + // supposed to work. + + sigset_t set; + sigemptyset(&set); + sigaddset(&set, signal); + pthread_sigmask(SIG_UNBLOCK, &set, 0); + + vmJump(ip, frame, stack, thread, 0, 0); + } else { + crash(); + } + } break; + + default: + crash(); + } +} + +} // namespace posix + +SignalRegistrar::SignalRegistrar() +{ + data = new (malloc(sizeof(Data))) Data(); +} + +SignalRegistrar::~SignalRegistrar() +{ + data->~Data(); + free(data); +} + +bool SignalRegistrar::Data::registerHandler(Handler* handler, int index) +{ + if (handler) { + handlers[index] = handler; + + struct sigaction sa; + memset(&sa, 0, sizeof(struct sigaction)); + sigemptyset(&(sa.sa_mask)); + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = posix::handleSignal; + + return sigaction(posix::signals[index], &sa, oldHandlers + index) == 0; + } else if (handlers[index]) { + handlers[index] = 0; + return sigaction(posix::signals[index], oldHandlers + index, 0) == 0; + } else { + return false; + } +} + +bool SignalRegistrar::registerHandler(Signal signal, Handler* handler) +{ + switch (signal) { + case SegFault: + if (!data->registerHandler(handler, posix::SegFaultSignalIndex)) { + return false; + } + if (posix::AltSegFaultSignal != posix::InvalidSignal) { + return data->registerHandler(handler, posix::AltSegFaultSignalIndex); + } else { + return true; + } + case DivideByZero: + return data->registerHandler(handler, posix::DivideByZeroSignalIndex); + default: + crash(); + } +} + +bool SignalRegistrar::unregisterHandler(Signal signal) +{ + switch (signal) { + case SegFault: + if (!data->registerHandler(0, posix::SegFaultSignalIndex)) { + return false; + } + if (posix::AltSegFaultSignal != posix::InvalidSignal) { + return data->registerHandler(0, posix::AltSegFaultSignalIndex); + } else { + return true; + } + case DivideByZero: + return data->registerHandler(0, posix::DivideByZeroSignalIndex); + default: + crash(); + } +} + +void SignalRegistrar::setCrashDumpDirectory(const char*) +{ + // Do nothing, not currently supported on posix +} + +} // namespace system +} // namespace avian diff --git a/sgx-jvm/avian/src/system/sgx.cpp b/sgx-jvm/avian/src/system/sgx.cpp new file mode 100644 index 0000000000..62a1547856 --- /dev/null +++ b/sgx-jvm/avian/src/system/sgx.cpp @@ -0,0 +1,519 @@ +// (C) 2016 R3 CEV Ltd +// +// Platform implementation for an Intel SGX enclave, which is similar to having no platform at all. + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define PATH_MAX 256 + +using namespace vm; +using namespace avian::util; + +// Weak link against the JAR functions that the Avian embedder must provide. +// This lets us look it up at runtime without having it be present in libavian.a +extern "C" __attribute__((weak)) const uint8_t* embedded_file_boot_jar(size_t* size); +extern "C" __attribute__((weak)) const uint8_t* embedded_file_app_jar(size_t* size); + +extern "C" const uint8_t* javahomeJar(size_t* size); + +namespace { + void abort_with(const char *msg) { + printf("%s\n", msg); + while(true); + } + + class MySystem; + MySystem* globalSystem; + const bool Verbose = false; + + class MySystem : public System { + public: + class Thread : public System::Thread { + public: + Thread* next; + + Thread(System* s UNUSED, System::Runnable* r UNUSED) : next(0) + { + } + + virtual void interrupt() + { + printf("Thread::Interrupt()\n"); + } + + virtual bool getAndClearInterrupted() + { + printf("Thread::getAndClearInterrupted()\n"); + return false; + } + + virtual void join() + { + printf("Thread::Join()\n"); + } + + virtual void dispose() + { + } + }; + + class Mutex : public System::Mutex { + public: + Mutex(System* s) : s(s) + { + + } + + virtual void acquire() + { + + } + + virtual void release() + { + + } + + virtual void dispose() + { + + } + + System* s; + }; + + class Monitor : public System::Monitor { + public: + Monitor(System* s) : s(s), owner_(0), first(0), last(0), depth(0) + { + + } + + virtual bool tryAcquire(System::Thread* context UNUSED) + { + return true; + } + + virtual void acquire(System::Thread* context UNUSED) + { + } + + virtual void release(System::Thread* context UNUSED) + { + } + + void append(Thread* t) + { + for (Thread* x = first; x; x = x->next) { + expect(s, t != x); + } + + if (last) { + expect(s, t != last); + last->next = t; + last = t; + } else { + first = last = t; + } + } + + void remove(Thread* t) + { + Thread* previous = 0; + for (Thread* current = first; current;) { + if (t == current) { + if (current == first) { + first = t->next; + } else { + expect(s, previous != t->next); + previous->next = t->next; + } + + if (current == last) { + last = previous; + } + + t->next = 0; + + break; + } else { + previous = current; + current = current->next; + } + } + + for (Thread* x = first; x; x = x->next) { + expect(s, t != x); + } + } + + virtual void wait(System::Thread* context UNUSED, int64_t time UNUSED) + { + wait(context, time, false); + } + + virtual bool waitAndClearInterrupted(System::Thread* context UNUSED, int64_t time UNUSED) + { + return wait(context, time, true); + } + + bool wait(System::Thread* context UNUSED, int64_t time UNUSED, bool clearInterrupted UNUSED) + { + abort_with("STUB: Thread::Wait()"); + return true; + } + + void doNotify(Thread* t UNUSED) + { + printf("STUB: Thread::Notify()\n"); + } + + virtual void notify(System::Thread* context UNUSED) + { + } + + virtual void notifyAll(System::Thread* context UNUSED) + { + } + + virtual System::Thread* owner() + { + return owner_; + } + + virtual void dispose() + { + expect(s, owner_ == 0); + ::free(this); + } + + System* s; + Thread* owner_; + Thread* first; + Thread* last; + unsigned depth; + }; + + class Local : public System::Local { + public: + void *value; + + Local(System* s) : s(s) + { + } + + virtual void* get() + { + return value; + } + + virtual void set(void* p) + { + value = p; + } + + virtual void dispose() + { + ::free(this); + } + + System* s; + }; + + class Region : public System::Region { + public: + Region(System* s, uint8_t* start, size_t length) + : s(s), start_(start), length_(length) + { + } + + virtual const uint8_t* start() + { + return start_; + } + + virtual size_t length() + { + return length_; + } + + virtual void dispose() + { + if (start_) { + printf("STUB: munmap\n"); + //munmap(start_, length_); + } + ::free(this); + } + + System* s; + uint8_t* start_; + size_t length_; + }; + + class Directory : public System::Directory { + public: + Directory(System* s, void* directory UNUSED) : s(s) + { + } + + virtual const char* next() + { + return 0; + } + + virtual void dispose() + { + ::free(this); + } + + System* s; + }; + + class Library : public System::Library { + public: + Library(System* s UNUSED) : next_(0) {} + + virtual void* resolve(const char* function) + { + const void *ptr = NULL; + if (!strcmp(function, "embedded_file_boot_jar")) { + return (void *) &embedded_file_boot_jar; + } if (!strcmp(function, "embedded_file_app_jar")) { + return (void *) &embedded_file_app_jar; + } if (!strcmp(function, "javahomeJar")) { + return (void *) &javahomeJar; + } else if ((ptr = dlsym(NULL, function))) { + return (void *) ptr; + } else { + // If you seem to be hitting a JNI call you're sure should exist, try uncommenting this. + // It is expected that some resolutions won't work as multiple names are tried for each + // native call, which is why we don't spam them all to the logs here. + // + // printf("Could not resolve file/function %s, check dispatch tables\n", function); + return NULL; + } + } + + virtual const char* name() + { + return "main"; + } + + virtual System::Library* next() + { + return next_; + } + + virtual void setNext(System::Library* lib) + { + next_ = lib; + } + + virtual void disposeAll() + { + if (next_) { + next_->disposeAll(); + } + + ::free(this); + } + + System* s; + System::Library* next_; + }; + + MySystem(bool reentrant) : reentrant(reentrant), threadVisitor(0), visitTarget(0) + { + if (not reentrant) { + expect(this, globalSystem == 0); + globalSystem = this; + expect(this, make(&visitLock) == 0); + } + } + + bool unregisterHandler(int index UNUSED) + { + return true; + } + + // Returns true on success, false on failure + bool registerHandler(int index UNUSED) + { + printf("System::registerHandler(%d)\n", index); + return true; + } + + virtual void* tryAllocate(size_t sizeInBytes) + { + return malloc(sizeInBytes); + } + + virtual void free(const void* p) + { + if (p) + ::free(const_cast(p)); + } + + virtual bool success(Status s) { + return s == 0; + } + + virtual Status attach(Runnable* r) + { + Thread* t = new (allocate(this, sizeof(Thread))) Thread(this, r); + r->attach(t); + return 0; + } + + virtual Status start(Runnable* r) + { + Thread* t = new (allocate(this, sizeof(Thread))) Thread(this, r); + r->attach(t); + printf("System::start (thread!!)\n"); + // We implement threads as blocking calls! This is of course totally wrong, but with extra threads + // patched out in a few places, it's hopefully sufficient. + r->run(); + return 0; + } + + virtual Status make(System::Mutex** m) + { + *m = new (allocate(this, sizeof(Mutex))) Mutex(this); + return 0; + } + + virtual Status make(System::Monitor** m) + { + *m = new (allocate(this, sizeof(Monitor))) Monitor(this); + return 0; + } + + virtual Status make(System::Local** l) + { + *l = new (allocate(this, sizeof(Local))) Local(this); + return 0; + } + + virtual Status visit(System::Thread* st UNUSED, + System::Thread* sTarget UNUSED, + ThreadVisitor* visitor UNUSED) + { + printf("System::visit (threads)\n"); + return 0; + } + + virtual Status map(System::Region** region UNUSED, const char* name) + { + printf("System::map(%s)\n", name); + return 0; + } + + virtual Status open(System::Directory** directory UNUSED, const char* name) + { + printf("System::open(%s)\n", name); + return 1; + } + + virtual FileType stat(const char* name, size_t* length) + { + // Avian does a stat on the current directory during startup but doesn't seem to care about the result, + // so suppress stub logging of stat(".") + if (strcmp(name, ".")) + printf("System::stat(%s)\n", name); + *length = 0; + return TypeDoesNotExist; + } + + virtual const char* libraryPrefix() + { + return SO_PREFIX; + } + + virtual const char* librarySuffix() + { + return SO_SUFFIX; + } + + virtual const char* toAbsolutePath(AllocOnly* allocator, const char* name) + { + return copy(allocator, name); + } + + virtual Status load(System::Library** lib, const char* name) + { + if (name != NULL) { + printf("System::load(%s)", name); + while(1); + } + + // Request to get a System::Library for the main process. + *lib = new (allocate(this, sizeof(Library))) Library(this); + return 0; + } + + virtual char pathSeparator() + { + return ':'; + } + + virtual char fileSeparator() + { + return '/'; + } + + virtual int64_t now() + { + timeval tv = {0, 0}; + gettimeofday(&tv, 0); + return (static_cast(tv.tv_sec) * 1000) + (static_cast(tv.tv_usec) / 1000); + } + + virtual void yield() + { + + } + + virtual void exit(int code UNUSED) + { + abort_with("exit()"); + } + + virtual void abort() + { + abort_with("abort!"); + } + + virtual void dispose() + { + if (not reentrant) { + visitLock->dispose(); + globalSystem = 0; + } + + ::free(this); + } + + bool reentrant; + ThreadVisitor* threadVisitor; + Thread* visitTarget; + System::Monitor* visitLock; + }; +} // namespace + +namespace vm { + AVIAN_EXPORT System* makeSystem(bool reentrant) + { + return new (malloc(sizeof(MySystem))) MySystem(reentrant); + } +} // namespace vm diff --git a/sgx-jvm/avian/src/system/sgx/memory.cpp b/sgx-jvm/avian/src/system/sgx/memory.cpp new file mode 100644 index 0000000000..b91f84da50 --- /dev/null +++ b/sgx-jvm/avian/src/system/sgx/memory.cpp @@ -0,0 +1,23 @@ +#include +#include + +namespace avian { + namespace system { + util::Slice Memory::allocate(size_t sizeInBytes, Permissions) + { + void* p = malloc(sizeInBytes); + + if (p == NULL) { + return util::Slice(0, 0); + } else { + return util::Slice(static_cast(p), sizeInBytes); + } + } + + void Memory::free(util::Slice slice) + { + ::free(slice.begin()); + } + + } // namespace system +} // namespace avian diff --git a/sgx-jvm/avian/src/system/sgx/signal.cpp b/sgx-jvm/avian/src/system/sgx/signal.cpp new file mode 100644 index 0000000000..dcb02686e1 --- /dev/null +++ b/sgx-jvm/avian/src/system/sgx/signal.cpp @@ -0,0 +1,34 @@ +#include "avian/arch.h" +#include +#include + +extern "C" { + void debug_print(const char *msg); +} + +namespace avian { + namespace system { + SignalRegistrar::SignalRegistrar() + { + } + + SignalRegistrar::~SignalRegistrar() + { + } + + bool SignalRegistrar::registerHandler(Signal signal UNUSED, Handler* handler UNUSED) + { + return true; + } + + bool SignalRegistrar::unregisterHandler(Signal signal UNUSED) + { + return true; + } + + void SignalRegistrar::setCrashDumpDirectory(const char*) + { + } + } // namespace system +} // namespace avian + diff --git a/sgx-jvm/avian/src/system/windows.cpp b/sgx-jvm/avian/src/system/windows.cpp new file mode 100644 index 0000000000..5833c28274 --- /dev/null +++ b/sgx-jvm/avian/src/system/windows.cpp @@ -0,0 +1,1033 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "sys/stat.h" +#include "windows.h" + +#ifdef _MSC_VER +#define S_ISREG(x) ((x)&_S_IFREG) +#define S_ISDIR(x) ((x)&_S_IFDIR) +#define FTIME _ftime_s +#else +#define FTIME _ftime +#endif + +#undef max +#undef min + +#include "avian/arch.h" +#include +#include +#include +#include + +#if defined(WINAPI_FAMILY) + +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + +#define WaitForSingleObject(hHandle, dwMilliseconds) \ + WaitForSingleObjectEx((hHandle), (dwMilliseconds), FALSE) + +#define CreateEvent(lpEventAttributes, bManualReset, bInitialState, lpName) \ + CreateEventEx((lpEventAttributes), \ + (lpName), \ + ((bManualReset) ? CREATE_EVENT_MANUAL_RESET : 0) \ + | ((bInitialState) ? CREATE_EVENT_INITIAL_SET : 0), \ + EVENT_ALL_ACCESS) + +#define CreateMutex(lpEventAttributes, bInitialOwner, lpName) \ + CreateMutexEx((lpEventAttributes), \ + (lpName), \ + (bInitialOwner) ? CREATE_MUTEX_INITIAL_OWNER : 0, \ + MUTEX_ALL_ACCESS) + +#include "thread-emulation.h" + +#endif + +#if defined(WINAPI_PARTITION_PHONE) \ + && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE) +// Headers in Windows Phone 8 DevKit contain severe error, so let's define +// needed functions on our own +extern "C" { +WINBASEAPI +_Ret_maybenull_ HANDLE WINAPI + CreateFileMappingFromApp(_In_ HANDLE hFile, + _In_opt_ PSECURITY_ATTRIBUTES SecurityAttributes, + _In_ ULONG PageProtection, + _In_ ULONG64 MaximumSize, + _In_opt_ PCWSTR Name); + +WINBASEAPI +_Ret_maybenull_ __out_data_source(FILE) PVOID WINAPI + MapViewOfFileFromApp(_In_ HANDLE hFileMappingObject, + _In_ ULONG DesiredAccess, + _In_ ULONG64 FileOffset, + _In_ SIZE_T NumberOfBytesToMap); + +WINBASEAPI +BOOL WINAPI UnmapViewOfFile(_In_ LPCVOID lpBaseAddress); +} +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE) + +#else + +#ifndef WINAPI_PARTITION_DESKTOP +#define WINAPI_PARTITION_DESKTOP 1 +#endif + +#ifndef WINAPI_FAMILY_PARTITION +#define WINAPI_FAMILY_PARTITION(x) (x) +#endif + +#endif + +#define ACQUIRE(s, x) MutexResource MAKE_NAME(mutexResource_)(s, x) + +using namespace vm; + +namespace { + +class MutexResource { + public: + MutexResource(System* s, HANDLE m) : s(s), m(m) + { + int r UNUSED = WaitForSingleObject(m, INFINITE); + assertT(s, r == WAIT_OBJECT_0); + } + + ~MutexResource() + { + bool success UNUSED = ReleaseMutex(m); + assertT(s, success); + } + + private: + System* s; + HANDLE m; +}; + +class MySystem; +MySystem* globalSystem; + +DWORD WINAPI run(void* r) +{ + static_cast(r)->run(); + return 0; +} + +const bool Verbose = false; + +const unsigned Waiting = 1 << 0; +const unsigned Notified = 1 << 1; + +class MySystem : public System { + public: + class Thread : public System::Thread { + public: + Thread(System* s, System::Runnable* r) : s(s), r(r), next(0), flags(0) + { + mutex = CreateMutex(0, false, 0); + assertT(s, mutex); + + event = CreateEvent(0, true, false, 0); + assertT(s, event); + } + + virtual void interrupt() + { + ACQUIRE(s, mutex); + + r->setInterrupted(true); + + if (flags & Waiting) { + int r UNUSED = SetEvent(event); + assertT(s, r != 0); + } + } + + virtual bool getAndClearInterrupted() + { + ACQUIRE(s, mutex); + + bool interrupted = r->interrupted(); + + r->setInterrupted(false); + + return interrupted; + } + + virtual void join() + { + int r UNUSED = WaitForSingleObject(thread, INFINITE); + assertT(s, r == WAIT_OBJECT_0); + } + + virtual void dispose() + { + CloseHandle(event); + CloseHandle(mutex); + CloseHandle(thread); + ::free(this); + } + + HANDLE thread; + HANDLE mutex; + HANDLE event; + System* s; + System::Runnable* r; + Thread* next; + unsigned flags; + }; + + class Mutex : public System::Mutex { + public: + Mutex(System* s) : s(s) + { + mutex = CreateMutex(0, false, 0); + assertT(s, mutex); + } + + virtual void acquire() + { + int r UNUSED = WaitForSingleObject(mutex, INFINITE); + assertT(s, r == WAIT_OBJECT_0); + } + + virtual void release() + { + bool success UNUSED = ReleaseMutex(mutex); + assertT(s, success); + } + + virtual void dispose() + { + CloseHandle(mutex); + ::free(this); + } + + System* s; + HANDLE mutex; + }; + + class Monitor : public System::Monitor { + public: + Monitor(System* s) : s(s), owner_(0), first(0), last(0), depth(0) + { + mutex = CreateMutex(0, false, 0); + assertT(s, mutex); + } + + virtual bool tryAcquire(System::Thread* context) + { + Thread* t = static_cast(context); + assertT(s, t); + + if (owner_ == t) { + ++depth; + return true; + } else { + switch (WaitForSingleObject(mutex, 0)) { + case WAIT_TIMEOUT: + return false; + + case WAIT_OBJECT_0: + owner_ = t; + ++depth; + return true; + + default: + sysAbort(s); + } + } + } + + virtual void acquire(System::Thread* context) + { + Thread* t = static_cast(context); + assertT(s, t); + + if (owner_ != t) { + int r UNUSED = WaitForSingleObject(mutex, INFINITE); + assertT(s, r == WAIT_OBJECT_0); + owner_ = t; + } + ++depth; + } + + virtual void release(System::Thread* context) + { + Thread* t = static_cast(context); + assertT(s, t); + + if (owner_ == t) { + if (--depth == 0) { + owner_ = 0; + bool success UNUSED = ReleaseMutex(mutex); + assertT(s, success); + } + } else { + sysAbort(s); + } + } + + void append(Thread* t) + { +#ifndef NDEBUG + for (Thread* x = first; x; x = x->next) { + expect(s, t != x); + } +#endif + + if (last) { + last->next = t; + last = t; + } else { + first = last = t; + } + } + + void remove(Thread* t) + { + Thread* previous = 0; + for (Thread* current = first; current;) { + if (t == current) { + if (current == first) { + first = t->next; + } else { + previous->next = t->next; + } + + if (current == last) { + last = previous; + } + + t->next = 0; + + break; + } else { + previous = current; + current = current->next; + } + } + +#ifndef NDEBUG + for (Thread* x = first; x; x = x->next) { + expect(s, t != x); + } +#endif + } + + virtual void wait(System::Thread* context, int64_t time) + { + wait(context, time, false); + } + + virtual bool waitAndClearInterrupted(System::Thread* context, int64_t time) + { + return wait(context, time, true); + } + + bool wait(System::Thread* context, int64_t time, bool clearInterrupted) + { + Thread* t = static_cast(context); + assertT(s, t); + + if (owner_ == t) { + // Initialized here to make gcc 4.2 a happy compiler + bool interrupted = false; + bool notified = false; + unsigned depth = 0; + + int r UNUSED; + + { + ACQUIRE(s, t->mutex); + + expect(s, (t->flags & Notified) == 0); + + interrupted = t->r->interrupted(); + if (interrupted and clearInterrupted) { + t->r->setInterrupted(false); + } + + t->flags |= Waiting; + + append(t); + + depth = this->depth; + this->depth = 0; + owner_ = 0; + + bool success UNUSED = ReleaseMutex(mutex); + assertT(s, success); + + if (not interrupted) { + success = ResetEvent(t->event); + assertT(s, success); + + success = ReleaseMutex(t->mutex); + assertT(s, success); + + r = WaitForSingleObject(t->event, (time ? time : INFINITE)); + assertT(s, r == WAIT_OBJECT_0 or r == WAIT_TIMEOUT); + + r = WaitForSingleObject(t->mutex, INFINITE); + assertT(s, r == WAIT_OBJECT_0); + + interrupted = t->r->interrupted(); + if (interrupted and clearInterrupted) { + t->r->setInterrupted(false); + } + } + + notified = ((t->flags & Notified) != 0); + } + + r = WaitForSingleObject(mutex, INFINITE); + assertT(s, r == WAIT_OBJECT_0); + + { + ACQUIRE(s, t->mutex); + t->flags = 0; + } + + if (not notified) { + remove(t); + } else { +#ifndef NDEBUG + for (Thread* x = first; x; x = x->next) { + expect(s, t != x); + } +#endif + } + + t->next = 0; + + owner_ = t; + this->depth = depth; + + return interrupted; + } else { + sysAbort(s); + } + } + + void doNotify(Thread* t) + { + ACQUIRE(s, t->mutex); + + t->flags |= Notified; + + bool success UNUSED = SetEvent(t->event); + assertT(s, success); + } + + virtual void notify(System::Thread* context) + { + Thread* t = static_cast(context); + assertT(s, t); + + if (owner_ == t) { + if (first) { + Thread* t = first; + first = first->next; + if (t == last) { + expect(s, first == 0); + last = 0; + } + + doNotify(t); + } + } else { + sysAbort(s); + } + } + + virtual void notifyAll(System::Thread* context) + { + Thread* t = static_cast(context); + assertT(s, t); + + if (owner_ == t) { + for (Thread* t = first; t; t = t->next) { + doNotify(t); + } + first = last = 0; + } else { + sysAbort(s); + } + } + + virtual System::Thread* owner() + { + return owner_; + } + + virtual void dispose() + { + assertT(s, owner_ == 0); + CloseHandle(mutex); + ::free(this); + } + + System* s; + HANDLE mutex; + Thread* owner_; + Thread* first; + Thread* last; + unsigned depth; + }; + + class Local : public System::Local { + public: + Local(System* s) : s(s) + { + key = TlsAlloc(); + assertT(s, key != TLS_OUT_OF_INDEXES); + } + + virtual void* get() + { + return TlsGetValue(key); + } + + virtual void set(void* p) + { + bool r UNUSED = TlsSetValue(key, p); + assertT(s, r); + } + + virtual void dispose() + { + bool r UNUSED = TlsFree(key); + assertT(s, r); + + ::free(this); + } + + System* s; + unsigned key; + }; + + class Region : public System::Region { + public: + Region(System* system, + uint8_t* start, + size_t length, + HANDLE mapping, + HANDLE file) + : system(system), + start_(start), + length_(length), + mapping(mapping), + file(file) + { + } + + virtual const uint8_t* start() + { + return start_; + } + + virtual size_t length() + { + return length_; + } + + virtual void dispose() + { + if (start_) { + if (start_) + UnmapViewOfFile(start_); + if (mapping) + CloseHandle(mapping); + if (file) + CloseHandle(file); + } + system->free(this); + } + + System* system; + uint8_t* start_; + size_t length_; + HANDLE mapping; + HANDLE file; + }; + + class Directory : public System::Directory { + public: + Directory(System* s) : s(s), handle(0), findNext(false) + { + } + + virtual const char* next() + { + if (handle and handle != INVALID_HANDLE_VALUE) { + if (findNext) { + if (FindNextFile(handle, &data)) { + return data.cFileName; + } + } else { + findNext = true; + return data.cFileName; + } + } + return 0; + } + + virtual void dispose() + { + if (handle and handle != INVALID_HANDLE_VALUE) { + FindClose(handle); + } + ::free(this); + } + + System* s; + HANDLE handle; + WIN32_FIND_DATA data; + bool findNext; + }; + + class Library : public System::Library { + public: + Library(System* s, HMODULE handle, const char* name) + : s(s), handle(handle), name_(name), next_(0) + { + } + + virtual void* resolve(const char* function) + { + void* address; + FARPROC p = GetProcAddress(handle, function); + memcpy(&address, &p, BytesPerWord); + return address; + } + + virtual const char* name() + { + return name_; + } + + virtual System::Library* next() + { + return next_; + } + + virtual void setNext(System::Library* lib) + { + next_ = lib; + } + + virtual void disposeAll() + { + if (Verbose) { + fprintf(stderr, "close %p\n", handle); + fflush(stderr); + } + + if (name_) { + FreeLibrary(handle); + } + + if (next_) { + next_->disposeAll(); + } + + if (name_) { + ::free(const_cast(name_)); + } + + ::free(this); + } + + System* s; + HMODULE handle; + const char* name_; + System::Library* next_; + }; + + MySystem(bool reentrant): reentrant(reentrant) + { + if (not reentrant) { + expect(this, globalSystem == 0); + globalSystem = this; + } + + mutex = CreateMutex(0, false, 0); + assertT(this, mutex); + } + + virtual void* tryAllocate(size_t sizeInBytes) + { + return malloc(sizeInBytes); + } + + virtual void free(const void* p) + { + if (p) + ::free(const_cast(p)); + } + + virtual bool success(Status s) { + return s == 0; + } + + virtual Status attach(Runnable* r) + { + Thread* t = new (allocate(this, sizeof(Thread))) Thread(this, r); + bool success UNUSED = DuplicateHandle(GetCurrentProcess(), + GetCurrentThread(), + GetCurrentProcess(), + &(t->thread), + 0, + false, + DUPLICATE_SAME_ACCESS); + assertT(this, success); + r->attach(t); + return 0; + } + + virtual Status start(Runnable* r) + { + Thread* t = new (allocate(this, sizeof(Thread))) Thread(this, r); + r->attach(t); + DWORD id; + t->thread = CreateThread(0, 0, run, r, 0, &id); + assertT(this, t->thread); + return 0; + } + + virtual Status make(System::Mutex** m) + { + *m = new (allocate(this, sizeof(Mutex))) Mutex(this); + return 0; + } + + virtual Status make(System::Monitor** m) + { + *m = new (allocate(this, sizeof(Monitor))) Monitor(this); + return 0; + } + + virtual Status make(System::Local** l) + { + *l = new (allocate(this, sizeof(Local))) Local(this); + return 0; + } + + virtual Status visit(System::Thread* st UNUSED, + System::Thread* sTarget, + ThreadVisitor* visitor) + { +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + assertT(this, st != sTarget); + + Thread* target = static_cast(sTarget); + + ACQUIRE(this, mutex); + + bool success = false; + int rv = SuspendThread(target->thread); + if (rv != -1) { + CONTEXT context; + memset(&context, 0, sizeof(CONTEXT)); + context.ContextFlags = CONTEXT_CONTROL; + rv = GetThreadContext(target->thread, &context); + + if (rv) { +#ifdef ARCH_x86_32 + visitor->visit(reinterpret_cast(context.Eip), + reinterpret_cast(context.Esp), + reinterpret_cast(context.Ebp)); +#elif defined ARCH_x86_64 + visitor->visit(reinterpret_cast(context.Rip), + reinterpret_cast(context.Rsp), + reinterpret_cast(context.Rbp)); +#endif + success = true; + } + + rv = ResumeThread(target->thread); + expect(this, rv != -1); + } + + return (success ? 0 : 1); +#else +#pragma message( \ + "TODO: http://msdn.microsoft.com/en-us/library/windowsphone/develop/system.windows.application.unhandledexception(v=vs.105).aspx") + return false; +#endif + } + + virtual Status map(System::Region** region, const char* name) + { + Status status = 1; + size_t nameLen = strlen(name) * 2; + RUNTIME_ARRAY(wchar_t, wideName, nameLen + 1); + MultiByteToWideChar( + CP_UTF8, 0, name, -1, RUNTIME_ARRAY_BODY(wideName), nameLen + 1); +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + HANDLE file = CreateFileW(RUNTIME_ARRAY_BODY(wideName), + FILE_READ_DATA, + FILE_SHARE_READ, + 0, + OPEN_EXISTING, + 0, + 0); +#else + HANDLE file = CreateFile2(RUNTIME_ARRAY_BODY(wideName), + GENERIC_READ, + FILE_SHARE_READ, + OPEN_EXISTING, + 0); +#endif + if (file != INVALID_HANDLE_VALUE) { +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + unsigned size = GetFileSize(file, 0); +#else + FILE_STANDARD_INFO info; + unsigned size = INVALID_FILE_SIZE; + if (GetFileInformationByHandleEx( + file, FileStandardInfo, &info, sizeof(info))) + size = info.EndOfFile.QuadPart; +#endif + if (size != INVALID_FILE_SIZE) { +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + HANDLE mapping = CreateFileMapping(file, 0, PAGE_READONLY, 0, size, 0); +#else + HANDLE mapping + = CreateFileMappingFromApp(file, 0, PAGE_READONLY, size, 0); +#endif + if (mapping) { +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + void* data = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0); +#else + void* data = MapViewOfFileFromApp(mapping, FILE_MAP_READ, 0, 0); +#endif + if (data) { + *region = new (allocate(this, sizeof(Region))) + Region(this, static_cast(data), size, file, mapping); + status = 0; + } + + if (status) { + CloseHandle(mapping); + } + } + } + + if (status) { + CloseHandle(file); + } + } + + return status; + } + + virtual Status open(System::Directory** directory, const char* name) + { + Status status = 1; + + unsigned length = strlen(name); + RUNTIME_ARRAY(char, buffer, length + 3); + memcpy(RUNTIME_ARRAY_BODY(buffer), name, length); + memcpy(RUNTIME_ARRAY_BODY(buffer) + length, "\\*", 3); + + Directory* d = new (allocate(this, sizeof(Directory))) Directory(this); + +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + d->handle = FindFirstFile(RUNTIME_ARRAY_BODY(buffer), &(d->data)); +#else + d->handle = FindFirstFileEx(RUNTIME_ARRAY_BODY(buffer), + FindExInfoStandard, + &(d->data), + FindExSearchNameMatch, + 0, + 0); +#endif + if (d->handle == INVALID_HANDLE_VALUE) { + d->dispose(); + } else { + *directory = d; + status = 0; + } + + return status; + } + + virtual FileType stat(const char* name, size_t* length) + { + size_t nameLen = strlen(name) * 2; + RUNTIME_ARRAY(wchar_t, wideName, nameLen + 1); + MultiByteToWideChar( + CP_UTF8, 0, name, -1, RUNTIME_ARRAY_BODY(wideName), nameLen + 1); + WIN32_FILE_ATTRIBUTE_DATA data; + if (GetFileAttributesExW( + RUNTIME_ARRAY_BODY(wideName), GetFileExInfoStandard, &data)) { + if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + return TypeDirectory; + } else { + *length = (data.nFileSizeHigh * static_cast(MAXDWORD + 1)) + + data.nFileSizeLow; + return TypeFile; + } + } else { + return TypeDoesNotExist; + } + } + + virtual const char* libraryPrefix() + { + return SO_PREFIX; + } + + virtual const char* librarySuffix() + { + return SO_SUFFIX; + } + + virtual const char* toAbsolutePath(avian::util::AllocOnly* allocator, + const char* name) + { +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + if (strncmp(name, "//", 2) == 0 or strncmp(name, "\\\\", 2) == 0 + or strncmp(name + 1, ":/", 2) == 0 + or strncmp(name + 1, ":\\", 2) == 0) { + return copy(allocator, name); + } else { + TCHAR buffer[MAX_PATH]; + GetCurrentDirectory(MAX_PATH, buffer); + return append(allocator, buffer, "\\", name); + } +#else +#pragma message( \ + "TODO:http://lunarfrog.com/blog/2012/05/21/winrt-folders-access/ Windows.ApplicationModel.Package.Current.InstalledLocation") + return copy(allocator, name); +#endif + } + + virtual Status load(System::Library** lib, const char* name) + { + HMODULE handle; + unsigned nameLength = (name ? strlen(name) : 0); + if (name) { + size_t nameLen = nameLength * 2; + RUNTIME_ARRAY(wchar_t, wideName, nameLen + 1); + MultiByteToWideChar( + CP_UTF8, 0, name, -1, RUNTIME_ARRAY_BODY(wideName), nameLen + 1); + +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + handle = LoadLibraryW(RUNTIME_ARRAY_BODY(wideName)); +#else + handle = LoadPackagedLibrary(RUNTIME_ARRAY_BODY(wideName), 0); +#endif + } else { +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + handle = GetModuleHandle(0); +#else + // Most of WinRT/WP8 applications can not host native object files inside + // main executable + assertT(this, false); +#endif + } + + if (handle) { + if (Verbose) { + fprintf(stderr, "open %s as %p\n", name, handle); + fflush(stderr); + } + + char* n; + if (name) { + n = static_cast(allocate(this, nameLength + 1)); + memcpy(n, name, nameLength + 1); + } else { + n = 0; + } + + *lib = new (allocate(this, sizeof(Library))) Library(this, handle, n); + + return 0; + } else { + if (Verbose) { + fprintf(stderr, "unable to open %s: %ld\n", name, GetLastError()); + fflush(stderr); + } + + return 1; + } + } + + virtual char pathSeparator() + { + return ';'; + } + + virtual char fileSeparator() + { + return '\\'; + } + + virtual int64_t now() + { + // We used to use _ftime here, but that only gives us 1-second + // resolution on Windows 7. _ftime_s might work better, but MinGW + // doesn't have it as of this writing. So we use this mess instead: + FILETIME time; + GetSystemTimeAsFileTime(&time); + return (((static_cast(time.dwHighDateTime) << 32) + | time.dwLowDateTime) / 10000) - 11644473600000LL; + } + + virtual void yield() + { +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + SwitchToThread(); +#else + YieldProcessor(); +#endif + } + + virtual void exit(int code) + { + ::exit(code); + } + + virtual void abort() + { + avian::system::crash(); + } + + virtual void dispose() + { + if (not reentrant) { + globalSystem = 0; + } + + CloseHandle(mutex); + ::free(this); + } + + HANDLE mutex; + bool reentrant; +}; + +} // namespace + +namespace vm { + +AVIAN_EXPORT System* makeSystem(bool reentrant) +{ + return new (malloc(sizeof(MySystem))) MySystem(reentrant); +} + +} // namespace vm diff --git a/sgx-jvm/avian/src/system/windows/crash.cpp b/sgx-jvm/avian/src/system/windows/crash.cpp new file mode 100644 index 0000000000..16ea717b72 --- /dev/null +++ b/sgx-jvm/avian/src/system/windows/crash.cpp @@ -0,0 +1,28 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include + +namespace avian { +namespace system { + +NO_RETURN void crash() +{ + // trigger an EXCEPTION_ACCESS_VIOLATION, which we will catch and + // generate a debug dump for + *static_cast(0) = 0; + + // Some (all?) compilers don't realize that we can't possibly continue past + // the above statement. + abort(); +} + +} // namespace system +} // namespace avian diff --git a/sgx-jvm/avian/src/system/windows/memory.cpp b/sgx-jvm/avian/src/system/windows/memory.cpp new file mode 100644 index 0000000000..c6d33053ca --- /dev/null +++ b/sgx-jvm/avian/src/system/windows/memory.cpp @@ -0,0 +1,55 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include + +#include + +#include + +namespace avian { +namespace system { + +const size_t Memory::PageSize = 1 << 12; + +util::Slice Memory::allocate(size_t sizeInBytes, + Permissions perms) +{ + unsigned prot; + switch(perms) { + case Read: + prot = PAGE_READONLY; + break; + case ReadWrite: + prot = PAGE_READWRITE; + break; + case ReadExecute: + prot = PAGE_EXECUTE_READ; + break; + case ReadWriteExecute: + prot = PAGE_EXECUTE_READWRITE; + break; + default: + UNREACHABLE_; + } + void* ret = VirtualAlloc( + 0, sizeInBytes, MEM_COMMIT | MEM_RESERVE, prot); + return util::Slice((uint8_t*)ret, sizeInBytes); +} + +void Memory::free(util::Slice pages) +{ + int r = VirtualFree(pages.begin(), 0, MEM_RELEASE); + (void) r; + ASSERT(r); +} + +} // namespace system +} // namespace avian diff --git a/sgx-jvm/avian/src/system/windows/signal.cpp b/sgx-jvm/avian/src/system/windows/signal.cpp new file mode 100644 index 0000000000..66e1cd1f8d --- /dev/null +++ b/sgx-jvm/avian/src/system/windows/signal.cpp @@ -0,0 +1,343 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "windows.h" +#include "sys/timeb.h" + +#ifdef _MSC_VER +#define FTIME _ftime_s +#else +#define FTIME _ftime +#endif + +#ifndef WINAPI_FAMILY + +#ifndef WINAPI_PARTITION_DESKTOP +#define WINAPI_PARTITION_DESKTOP 1 +#endif + +#ifndef WINAPI_FAMILY_PARTITION +#define WINAPI_FAMILY_PARTITION(x) (x) +#endif + +#endif + +#include +#include + +namespace avian { +namespace system { + +namespace windows { + +const unsigned HandlerCount = 2; + +} // namespace windows + +struct SignalRegistrar::Data { + Handler* handlers[windows::HandlerCount]; + const char* crashDumpDirectory; + +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + LPTOP_LEVEL_EXCEPTION_FILTER oldHandler; +#endif + + Data() + : crashDumpDirectory(0), +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + oldHandler(0) +#endif + { + if (instance) { + crash(); + } + instance = this; + memset(handlers, 0, sizeof(handlers)); + } + + ~Data() + { + instance = 0; + } + + bool registerHandler(Handler* handler, int index); + + bool findHandler() + { + for (unsigned i = 0; i < windows::HandlerCount; ++i) { + if (handlers[i]) + return true; + } + return false; + } + + static SignalRegistrar::Data* instance; +}; + +SignalRegistrar::Data* SignalRegistrar::Data::instance = 0; + +namespace windows { + +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + +#pragma pack(push, 4) +struct MINIDUMP_EXCEPTION_INFORMATION { + DWORD thread; + LPEXCEPTION_POINTERS exception; + BOOL exceptionInCurrentAddressSpace; +}; +#pragma pack(pop) + +struct MINIDUMP_USER_STREAM_INFORMATION; +struct MINIDUMP_CALLBACK_INFORMATION; + +enum MINIDUMP_TYPE { MiniDumpNormal = 0, MiniDumpWithFullMemory = 2 }; + +typedef BOOL (*MiniDumpWriteDumpType)( + HANDLE processHandle, + DWORD processId, + HANDLE file, + MINIDUMP_TYPE type, + const MINIDUMP_EXCEPTION_INFORMATION* exception, + const MINIDUMP_USER_STREAM_INFORMATION* userStream, + const MINIDUMP_CALLBACK_INFORMATION* callback); + +#endif + +void dump(LPEXCEPTION_POINTERS e, const char* directory) +{ + HINSTANCE dbghelp = LoadLibrary("dbghelp.dll"); + + if (dbghelp) { + MiniDumpWriteDumpType MiniDumpWriteDump + = reinterpret_cast( + GetProcAddress(dbghelp, "MiniDumpWriteDump")); + + if (MiniDumpWriteDump) { + char name[MAX_PATH]; + _timeb tb; + FTIME(&tb); + vm::snprintf(name, + MAX_PATH, + "%s\\crash-%" LLD ".mdmp", + directory, + (static_cast(tb.time) * 1000) + + static_cast(tb.millitm)); + + HANDLE file + = CreateFile(name, FILE_WRITE_DATA, 0, 0, CREATE_ALWAYS, 0, 0); + + if (file != INVALID_HANDLE_VALUE) { + MINIDUMP_EXCEPTION_INFORMATION exception + = {GetCurrentThreadId(), e, true}; + + MiniDumpWriteDump(GetCurrentProcess(), + GetCurrentProcessId(), + file, + MiniDumpWithFullMemory, + &exception, + 0, + 0); + + CloseHandle(file); + } + } + + FreeLibrary(dbghelp); + } +} + +void logException(LPEXCEPTION_POINTERS e, const char* directory) +{ + char name[MAX_PATH]; + _timeb tb; + FTIME(&tb); + vm::snprintf(name, MAX_PATH, "%s\\exceptions.txt", directory); + + FILE* log = vm::fopen(name, "ab"); + if (log) { +#ifdef ARCH_x86_32 + void* ip = reinterpret_cast(e->ContextRecord->Eip); + void* base = reinterpret_cast(e->ContextRecord->Ebp); + void* stack = reinterpret_cast(e->ContextRecord->Esp); + void* thread = reinterpret_cast(e->ContextRecord->Ebx); +#elif defined ARCH_x86_64 + void* ip = reinterpret_cast(e->ContextRecord->Rip); + void* base = reinterpret_cast(e->ContextRecord->Rbp); + void* stack = reinterpret_cast(e->ContextRecord->Rsp); + void* thread = reinterpret_cast(e->ContextRecord->Rbx); +#endif + + HMODULE module; + if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + static_cast(ip), + &module)) { + GetModuleFileName(module, name, MAX_PATH); + } else { + module = 0; + } + + fprintf(log, + "timestamp %" LLD + " code %ld ip %p base %p stack %p thread %p module %s\n", + (static_cast(tb.time) * 1000) + + static_cast(tb.millitm), + e->ExceptionRecord->ExceptionCode, + ip, + base, + stack, + thread, + module ? name : "(unknown)"); + + fflush(log); + fclose(log); + } +} + +LONG CALLBACK handleException(LPEXCEPTION_POINTERS e) +{ + SignalRegistrar::Handler* handler = 0; + if (e->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { + handler + = SignalRegistrar::Data::instance->handlers[SignalRegistrar::SegFault]; + } else if (e->ExceptionRecord->ExceptionCode + == EXCEPTION_INT_DIVIDE_BY_ZERO) { + handler = SignalRegistrar::Data::instance + ->handlers[SignalRegistrar::DivideByZero]; + } + + if (handler) { +#ifdef ARCH_x86_32 + void* ip = reinterpret_cast(e->ContextRecord->Eip); + void* base = reinterpret_cast(e->ContextRecord->Ebp); + void* stack = reinterpret_cast(e->ContextRecord->Esp); + void* thread = reinterpret_cast(e->ContextRecord->Ebx); +#elif defined ARCH_x86_64 + void* ip = reinterpret_cast(e->ContextRecord->Rip); + void* base = reinterpret_cast(e->ContextRecord->Rbp); + void* stack = reinterpret_cast(e->ContextRecord->Rsp); + void* thread = reinterpret_cast(e->ContextRecord->Rbx); +#endif + + bool jump = handler->handleSignal(&ip, &base, &stack, &thread); + + if (jump) { +#ifdef ARCH_x86_32 + e->ContextRecord->Eip = reinterpret_cast(ip); + e->ContextRecord->Ebp = reinterpret_cast(base); + e->ContextRecord->Esp = reinterpret_cast(stack); + e->ContextRecord->Ebx = reinterpret_cast(thread); +#elif defined ARCH_x86_64 + e->ContextRecord->Rip = reinterpret_cast(ip); + e->ContextRecord->Rbp = reinterpret_cast(base); + e->ContextRecord->Rsp = reinterpret_cast(stack); + e->ContextRecord->Rbx = reinterpret_cast(thread); +#endif + + return EXCEPTION_CONTINUE_EXECUTION; + } else if (SignalRegistrar::Data::instance->crashDumpDirectory) { + // We only generate a crash dump if exception occurred in code + // belonging to the current executable. If the exception + // occurred in a library, there may be a handler available to + // handle it, in which case it is premature to assume we're + // going to crash. Generating a full memory dump on each such + // event is time consuming and eats up disk space, so we'd + // prefer to avoid it unless we're really crashing. + HMODULE module; + if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + static_cast(ip), + &module) and module == GetModuleHandle(0)) { + dump(e, SignalRegistrar::Data::instance->crashDumpDirectory); + } else { + logException(e, SignalRegistrar::Data::instance->crashDumpDirectory); + } + } + } + + return EXCEPTION_CONTINUE_SEARCH; +} + +} // namespace windows + +SignalRegistrar::SignalRegistrar() +{ + data = new (malloc(sizeof(Data))) Data(); +} + +SignalRegistrar::~SignalRegistrar() +{ + data->~Data(); + free(data); +} + +bool SignalRegistrar::Data::registerHandler(Handler* handler, int index) +{ + if (index != SegFault && index != DivideByZero) { + crash(); + } + + if (handler) { + handlers[index] = handler; + +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + if (oldHandler == 0) { +#ifdef ARCH_x86_32 + oldHandler = SetUnhandledExceptionFilter(windows::handleException); +#elif defined ARCH_x86_64 + AddVectoredExceptionHandler(1, windows::handleException); + oldHandler = reinterpret_cast(1); +#endif + } +#else +#pragma message( \ + "TODO: http://msdn.microsoft.com/en-us/library/windowsphone/develop/system.windows.application.unhandledexception(v=vs.105).aspx") +#endif + + return true; + } else if (handlers[index]) { + handlers[index] = 0; + + if (not findHandler()) { +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#ifdef ARCH_x86_32 + SetUnhandledExceptionFilter(oldHandler); + oldHandler = 0; +#elif defined ARCH_x86_64 +// do nothing, handlers are never "unregistered" anyway +#endif +#else +#pragma message( \ + "TODO: http://msdn.microsoft.com/en-us/library/windowsphone/develop/system.windows.application.unhandledexception(v=vs.105).aspx") +#endif + } + + return true; + } else { + return false; + } +} + +bool SignalRegistrar::registerHandler(Signal signal, Handler* handler) +{ + return data->registerHandler(handler, signal); +} + +bool SignalRegistrar::unregisterHandler(Signal signal) +{ + return data->registerHandler(0, signal); +} + +void SignalRegistrar::setCrashDumpDirectory(const char* crashDumpDirectory) +{ + data->crashDumpDirectory = crashDumpDirectory; +} + +} // namespace system +} // namespace avian diff --git a/sgx-jvm/avian/src/thunks.cpp b/sgx-jvm/avian/src/thunks.cpp new file mode 100644 index 0000000000..43a2a0c45d --- /dev/null +++ b/sgx-jvm/avian/src/thunks.cpp @@ -0,0 +1,75 @@ +THUNK(tryInitClass) +THUNK(findInterfaceMethodFromInstance) +THUNK(findInterfaceMethodFromInstanceAndReference) +THUNK(findSpecialMethodFromReference) +THUNK(findStaticMethodFromReference) +THUNK(findVirtualMethodFromReference) +THUNK(getMethodAddress) +THUNK(compareDoublesG) +THUNK(compareDoublesL) +THUNK(compareFloatsG) +THUNK(compareFloatsL) +THUNK(compareLongs) +THUNK(addDouble) +THUNK(subtractDouble) +THUNK(multiplyDouble) +THUNK(divideDouble) +THUNK(moduloDouble) +THUNK(negateDouble) +THUNK(squareRootDouble) +THUNK(doubleToFloat) +THUNK(doubleToInt) +THUNK(doubleToLong) +THUNK(addFloat) +THUNK(subtractFloat) +THUNK(multiplyFloat) +THUNK(divideFloat) +THUNK(moduloFloat) +THUNK(negateFloat) +THUNK(absoluteFloat) +THUNK(absoluteLong) +THUNK(absoluteInt) +THUNK(divideLong) +THUNK(divideInt) +THUNK(moduloLong) +THUNK(moduloInt) +THUNK(floatToDouble) +THUNK(floatToInt) +THUNK(floatToLong) +THUNK(intToDouble) +THUNK(intToFloat) +THUNK(longToDouble) +THUNK(longToFloat) +THUNK(makeBlankObjectArray) +THUNK(makeBlankObjectArrayFromReference) +THUNK(makeBlankArray) +THUNK(lookUpAddress) +THUNK(setMaybeNull) +THUNK(acquireMonitorForObject) +THUNK(acquireMonitorForObjectOnEntrance) +THUNK(releaseMonitorForObject) +THUNK(acquireMonitorForClassOnEntrance) +THUNK(releaseMonitorForClass) +THUNK(makeMultidimensionalArray) +THUNK(makeMultidimensionalArrayFromReference) +THUNK(throw_) +THUNK(checkCast) +THUNK(checkCastFromReference) +THUNK(getStaticFieldValueFromReference) +THUNK(getFieldValueFromReference) +THUNK(setStaticFieldValueFromReference) +THUNK(setFieldValueFromReference) +THUNK(setStaticLongFieldValueFromReference) +THUNK(setLongFieldValueFromReference) +THUNK(setStaticObjectFieldValueFromReference) +THUNK(setObjectFieldValueFromReference) +THUNK(instanceOf64) +THUNK(instanceOfFromReference) +THUNK(makeNewGeneral64) +THUNK(makeNew64) +THUNK(makeNewFromReference) +THUNK(setObject) +THUNK(getJClass64) +THUNK(getJClassFromReference) +THUNK(gcIfNecessary) +THUNK(idleIfNecessary) diff --git a/sgx-jvm/avian/src/tools/CMakeLists.txt b/sgx-jvm/avian/src/tools/CMakeLists.txt new file mode 100644 index 0000000000..9825129a2c --- /dev/null +++ b/sgx-jvm/avian/src/tools/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory(binary-to-object) +add_subdirectory(object-writer) +add_subdirectory(type-generator) diff --git a/sgx-jvm/avian/src/tools/binary-to-object/CMakeLists.txt b/sgx-jvm/avian/src/tools/binary-to-object/CMakeLists.txt new file mode 100644 index 0000000000..1de4e5f9e4 --- /dev/null +++ b/sgx-jvm/avian/src/tools/binary-to-object/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(binary_to_object main.cpp) + +target_link_libraries(binary_to_object object_writer) diff --git a/sgx-jvm/avian/src/tools/binary-to-object/main.cpp b/sgx-jvm/avian/src/tools/binary-to-object/main.cpp new file mode 100644 index 0000000000..a6b25ae137 --- /dev/null +++ b/sgx-jvm/avian/src/tools/binary-to-object/main.cpp @@ -0,0 +1,173 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include +#include +#include +#include + +#include +#ifdef _WIN32 +#include +#include +#else +#include +#include +#endif +#include + +#include + +extern "C" void __cxa_pure_virtual() +{ + abort(); +} + +void* operator new(size_t size) +{ + return malloc(size); +} + +void operator delete(void*) throw() +{ + abort(); +} + +namespace { + +using namespace avian::tools; +using namespace avian::util; + +bool writeObject(uint8_t* data, + size_t size, + OutputStream* out, + const char* startName, + const char* endName, + Platform* platform, + unsigned alignment, + bool writable, + bool executable) +{ + + SymbolInfo symbols[] = {SymbolInfo(0, startName), SymbolInfo(size, endName)}; + + unsigned accessFlags = (writable ? Platform::Writable : 0) + | (executable ? Platform::Executable : 0); + + return platform->writeObject(out, + Slice(symbols, 2), + Slice(data, size), + accessFlags, + alignment); +} + +void usageAndExit(const char* name) +{ + fprintf(stderr, + "usage: %s " + " " + "[ [{writable|executable}...]]\n", + name); + exit(-1); +} + +} // namespace + +int main(int argc, const char** argv) +{ + if (argc < 7 || argc > 10) { + usageAndExit(argv[0]); + } + + unsigned alignment = 1; + if (argc > 7) { + alignment = atoi(argv[7]); + } + + bool writable = false; + bool executable = false; + + for (int i = 8; i < argc; ++i) { + if (strcmp("writable", argv[i]) == 0) { + writable = true; + } else if (strcmp("executable", argv[i]) == 0) { + executable = true; + } else { + usageAndExit(argv[0]); + } + } + + const char* format = argv[5]; + const char* architecture = argv[6]; + + Platform* platform = Platform::getPlatform( + PlatformInfo(PlatformInfo::formatFromString(format), + PlatformInfo::archFromString(architecture))); + + if (!platform) { + fprintf(stderr, "unsupported platform: %s/%s\n", format, architecture); + return 1; + } + + + uint8_t* data = 0; + unsigned size; + int fd = open(argv[1], O_RDONLY); + if (fd != -1) { + struct stat s; + int r = fstat(fd, &s); + if (r != -1) { +#ifdef _WIN32 + HANDLE fm; + HANDLE h = (HANDLE)_get_osfhandle(fd); + + fm = CreateFileMapping(h, NULL, PAGE_READONLY, 0, 0, NULL); + data = static_cast( + MapViewOfFile(fm, FILE_MAP_READ, 0, 0, s.st_size)); + + CloseHandle(fm); +#else + data = static_cast( + mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0)); +#endif + size = s.st_size; + } + close(fd); + } + + bool success = false; + + if (data) { + FileOutputStream out(argv[2]); + if (out.isValid()) { + success = writeObject(data, + size, + &out, + argv[3], + argv[4], + platform, + alignment, + writable, + executable); + } else { + fprintf(stderr, "unable to open %s\n", argv[2]); + } + +#ifdef _WIN32 + UnmapViewOfFile(data); +#else + munmap(data, size); +#endif + } else { + perror(argv[0]); + } + + return (success ? 0 : -1); +} diff --git a/sgx-jvm/avian/src/tools/bootimage-generator/main.cpp b/sgx-jvm/avian/src/tools/bootimage-generator/main.cpp new file mode 100644 index 0000000000..1b271a22dd --- /dev/null +++ b/sgx-jvm/avian/src/tools/bootimage-generator/main.cpp @@ -0,0 +1,2324 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include +#include "avian/heapwalk.h" +#include "avian/common.h" +#include "avian/machine.h" +#include "avian/util.h" +#include +#include +#include +#include "avian/target.h" +#include +#include +#include "avian/lzma.h" + +#include +#include + +// since we aren't linking against libstdc++, we must implement this +// ourselves: +extern "C" void __cxa_pure_virtual(void) +{ + abort(); +} + +using namespace vm; +using namespace avian::tools; +using namespace avian::util; +using namespace avian::codegen; + +namespace { + +const unsigned HeapCapacity = 512 * 1024 * 1024; + +const unsigned TargetFixieSizeInBytes = 8 + (TargetBytesPerWord * 2); +const unsigned TargetFixieSizeInWords + = ceilingDivide(TargetFixieSizeInBytes, TargetBytesPerWord); +const unsigned TargetFixieAge = 0; +const unsigned TargetFixieFlags = 2; +const unsigned TargetFixieSize = 4; + +const bool DebugNativeTarget = false; + +enum Type { + Type_none, + Type_object, + Type_object_nogc, + Type_int8_t, + Type_uint8_t, + Type_int16_t, + Type_uint16_t, + Type_int32_t, + Type_uint32_t, + Type_intptr_t, + Type_uintptr_t, + Type_int64_t, + Type_int64_t_pad, + Type_uint64_t, + Type_float, + Type_double, + Type_double_pad, + Type_word, + Type_array +}; + +class Field { + public: + Type type; + unsigned buildOffset; + unsigned buildSize; + unsigned targetOffset; + unsigned targetSize; +}; + +void init(Field* f, + Type type, + unsigned buildOffset, + unsigned buildSize, + unsigned targetOffset, + unsigned targetSize) +{ + f->type = type; + f->buildOffset = buildOffset; + f->buildSize = buildSize; + f->targetOffset = targetOffset; + f->targetSize = targetSize; +} + +class TypeMap { + public: + enum Kind { NormalKind, SingletonKind, PoolKind }; + + TypeMap(unsigned buildFixedSizeInWords, + unsigned targetFixedSizeInWords, + unsigned fixedFieldCount, + Kind kind = NormalKind, + unsigned buildArrayElementSizeInBytes = 0, + unsigned targetArrayElementSizeInBytes = 0, + Type arrayElementType = Type_none) + : buildFixedSizeInWords(buildFixedSizeInWords), + targetFixedSizeInWords(targetFixedSizeInWords), + fixedFieldCount(fixedFieldCount), + buildArrayElementSizeInBytes(buildArrayElementSizeInBytes), + targetArrayElementSizeInBytes(targetArrayElementSizeInBytes), + arrayElementType(arrayElementType), + kind(kind) + { + } + + uintptr_t* targetFixedOffsets() + { + return reinterpret_cast(this + 1); + } + + Field* fixedFields() + { + return reinterpret_cast(targetFixedOffsets() + + (buildFixedSizeInWords * BytesPerWord)); + } + + static unsigned sizeInBytes(unsigned buildFixedSizeInWords, + unsigned fixedFieldCount) + { + return sizeof(TypeMap) + + (buildFixedSizeInWords * BytesPerWord * BytesPerWord) + + (sizeof(Field) * fixedFieldCount); + } + + unsigned buildFixedSizeInWords; + unsigned targetFixedSizeInWords; + unsigned fixedFieldCount; + unsigned buildArrayElementSizeInBytes; + unsigned targetArrayElementSizeInBytes; + Type arrayElementType; + Kind kind; +}; + +// Notes on immutable references in the heap image: +// +// One of the advantages of a bootimage-based build is that reduces +// the overhead of major GCs at runtime since we can avoid scanning +// the pre-built heap image entirely. However, this only works if we +// can ensure that no part of the heap image (with exceptions noted +// below) ever points to runtime-allocated objects. Therefore (most) +// references in the heap image are considered immutable, and any +// attempt to update them at runtime will cause the process to abort. +// +// However, some references in the heap image really must be updated +// at runtime: e.g. the static field table for each class. Therefore, +// we allocate these as "fixed" objects, subject to mark-and-sweep +// collection, instead of as "copyable" objects subject to copying +// collection. This strategy avoids the necessity of maintaining +// "dirty reference" bitsets at runtime for the entire heap image; +// each fixed object has its own bitset specific to that object. +// +// In addition to the "fixed" object solution, there are other +// strategies available to avoid attempts to update immutable +// references at runtime: +// +// * Table-based: use a lazily-updated array or vector to associate +// runtime data with heap image objects (see +// e.g. getClassRuntimeData in machine.cpp). +// +// * Update references at build time: for example, we set the names +// of primitive classes before generating the heap image so that we +// need not populate them lazily at runtime. + +bool endsWith(const char* suffix, const char* s, unsigned length) +{ + unsigned suffixLength = strlen(suffix); + return length >= suffixLength + and memcmp(suffix, s + (length - suffixLength), suffixLength) == 0; +} + +GcVector* getNonStaticFields(Thread* t, + GcHashMap* typeMaps, + GcClass* c, + GcVector* fields, + unsigned* count, + GcByteArray** array) +{ + PROTECT(t, typeMaps); + PROTECT(t, c); + PROTECT(t, fields); + + *array = cast( + t, + hashMapFind( + t, typeMaps, reinterpret_cast(c), objectHash, objectEqual)); + + if (*array) { + *count += reinterpret_cast((*array)->body().begin()) + ->fixedFieldCount; + } else { + if (c->super()) { + fields + = getNonStaticFields(t, typeMaps, c->super(), fields, count, array); + } + + if (GcArray* ftable = cast(t, c->fieldTable())) { + PROTECT(t, ftable); + for (unsigned i = 0; i < ftable->length(); ++i) { + GcField* field = cast(t, ftable->body()[i]); + + if ((field->flags() & ACC_STATIC) == 0) { + ++(*count); + fields = vectorAppend(t, fields, reinterpret_cast(field)); + } + } + } + } + + return vectorAppend(t, fields, 0); +} + +GcVector* allFields(Thread* t, + GcHashMap* typeMaps, + GcClass* c, + unsigned* count, + GcByteArray** array) +{ + PROTECT(t, typeMaps); + PROTECT(t, c); + + GcVector* fields = makeVector(t, 0, 0); + PROTECT(t, fields); + + *array = cast( + t, + hashMapFind( + t, typeMaps, reinterpret_cast(c), objectHash, objectEqual)); + + bool includeMembers; + if (*array) { + includeMembers = false; + *count += reinterpret_cast((*array)->body().begin()) + ->fixedFieldCount; + } else { + includeMembers = true; + if (c->super()) { + fields + = getNonStaticFields(t, typeMaps, c->super(), fields, count, array); + } + } + + if (GcArray* ftable = cast(t, c->fieldTable())) { + PROTECT(t, ftable); + for (unsigned i = 0; i < ftable->length(); ++i) { + GcField* field = cast(t, ftable->body()[i]); + + if (includeMembers or (field->flags() & ACC_STATIC)) { + ++(*count); + fields = vectorAppend(t, fields, reinterpret_cast(field)); + } + } + } + + return fields; +} + +TypeMap* classTypeMap(Thread* t, GcHashMap* typeMaps, object p) +{ + return reinterpret_cast( + cast(t, hashMapFind(t, typeMaps, p, objectHash, objectEqual)) + ->body() + .begin()); +} + +TypeMap* typeMap(Thread* t, GcHashMap* typeMaps, object p) +{ + return reinterpret_cast( + cast( + t, + objectClass(t, p) == type(t, GcSingleton::Type) + ? hashMapFind(t, typeMaps, p, objectHash, objectEqual) + : hashMapFind(t, + typeMaps, + reinterpret_cast(objectClass(t, p)), + objectHash, + objectEqual)) + ->body() + .begin()); +} + +unsigned targetFieldOffset(Thread* t, GcHashMap* typeMaps, GcField* field) +{ + unsigned offset + = ((field->flags() & ACC_STATIC) + ? typeMap(t, + typeMaps, + reinterpret_cast(field->class_()->staticTable())) + : classTypeMap( + t, typeMaps, reinterpret_cast(field->class_()))) + ->targetFixedOffsets()[field->offset()]; + + assertT(t, not((field->offset() == 0) xor (offset == 0))); + + 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, + GcHashMap* typeMaps) +{ + PROTECT(t, typeMaps); + + t->m->classpath->interceptMethods(t); + + GcPair* classes = 0; + PROTECT(t, classes); + + class MyOffsetResolver : public OffsetResolver { + public: + MyOffsetResolver(GcHashMap** typeMaps, GcPair** classes) + : typeMaps(typeMaps), classes(classes) + { + } + + virtual unsigned fieldOffset(Thread* t, GcField* field) + { + 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; + GcPair** classes; + } resolver(&typeMaps, &classes); + + Finder* finder = static_cast( + roots(t)->bootLoader()->as(t)->finder()); + + for (Finder::Iterator it(finder); it.hasMore();) { + size_t nameSize = 0; + const char* name = it.next(&nameSize); + + if (endsWith(".class", name, nameSize) + and (className == 0 or strncmp(name, className, nameSize - 6) == 0)) { + 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); + + System::Region* region = finder->find(name); + + THREAD_RESOURCE(t, System::Region*, region, region->dispose()); + + 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); + + if (endsWith(".class", name, nameSize) + and (className == 0 or strncmp(name, className, nameSize - 6) == 0)) { + if (false) { + fprintf(stderr, "pass 2 %.*s\n", (int)nameSize - 6, name); + } + + GcClass* c + = resolveSystemClass(t, + roots(t)->bootLoader(), + makeByteArray(t, "%.*s", nameSize - 6, name), + true); + + classes = makePair( + t, reinterpret_cast(c), reinterpret_cast(classes)); + } + } + + // 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); + + classes = 0; + + for (; myClasses; myClasses = cast(t, myClasses->second())) { + compileMethods(t, + cast(t, myClasses->first()), + zone, + &constants, + &calls, + &methods, + &addresses, + &resolver, + hostVM, + methodName, + methodSpec); + } + } + + for (; calls; calls = cast(t, calls->third())) { + GcMethod* method = cast(t, calls->first()); + uintptr_t address; + if (method->flags() & ACC_NATIVE) { + address = reinterpret_cast(code + image->thunks.native.start); + } else { + address = method->code()->compiled(); + } + + static_cast(cast(t, calls->second())->value()) + ->listener->resolve(address, 0); + } + + for (; addresses; addresses = addresses->next) { + uint8_t* value = reinterpret_cast(addresses->basis->value()); + expect(t, value >= code); + + addresses->listener->resolve(static_cast(value - code), 0); + } + + for (; methods; methods = cast(t, methods->second())) { + cast(t, methods->first())->code()->compiled() + -= reinterpret_cast(code); + } + + t->m->processor->normalizeVirtualThunks(t); + + return constants; +} + +void visitRoots(Thread* t, BootImage* image, HeapWalker* w, GcTriple* constants) +{ + Machine* m = t->m; + + for (HashMapIterator it(t, cast(t, roots(t)->bootLoader()->map())); + it.hasMore();) { + w->visitRoot(it.next()->second()); + } + + image->bootLoader + = w->visitRoot(reinterpret_cast(roots(t)->bootLoader())); + image->appLoader + = w->visitRoot(reinterpret_cast(roots(t)->appLoader())); + image->types = w->visitRoot(reinterpret_cast(m->types)); + + m->processor->visitRoots(t, w); + + for (; constants; constants = cast(t, constants->third())) { + w->visitRoot(constants->first()); + } +} + +unsigned targetOffset(Thread* t, GcHashMap* typeMaps, object p, unsigned offset) +{ + TypeMap* map = typeMap(t, typeMaps, p); + + if (map->targetArrayElementSizeInBytes + and offset >= map->buildFixedSizeInWords * BytesPerWord) { + return (map->targetFixedSizeInWords * TargetBytesPerWord) + + (((offset - (map->buildFixedSizeInWords * BytesPerWord)) + / map->buildArrayElementSizeInBytes) + * map->targetArrayElementSizeInBytes); + } else { + return map->targetFixedOffsets()[offset]; + } +} + +unsigned targetSize(Thread* t, GcHashMap* typeMaps, object p) +{ + TypeMap* map = typeMap(t, typeMaps, p); + + if (map->targetArrayElementSizeInBytes) { + return map->targetFixedSizeInWords + + ceilingDivide( + map->targetArrayElementSizeInBytes + * fieldAtOffset( + p, (map->buildFixedSizeInWords - 1) * BytesPerWord), + TargetBytesPerWord); + } else { + switch (map->kind) { + case TypeMap::NormalKind: + return map->targetFixedSizeInWords; + + case TypeMap::SingletonKind: + return map->targetFixedSizeInWords + + singletonMaskSize(map->targetFixedSizeInWords - 2, + TargetBitsPerWord); + + case TypeMap::PoolKind: { + unsigned maskSize + = poolMaskSize(map->targetFixedSizeInWords - 2, TargetBitsPerWord); + + return map->targetFixedSizeInWords + maskSize + + singletonMaskSize(map->targetFixedSizeInWords - 2 + maskSize, + TargetBitsPerWord); + } + + default: + abort(t); + } + } +} + +unsigned objectMaskCount(TypeMap* map) +{ + unsigned count = map->targetFixedSizeInWords; + + if (map->targetArrayElementSizeInBytes) { + ++count; + } + + return count; +} + +unsigned targetSize(Thread* t, + GcHashMap* typeMaps, + object referer, + unsigned refererOffset, + object p) +{ + if (referer and objectClass(t, referer) == type(t, GcClass::Type) + and (refererOffset * BytesPerWord) == ClassObjectMask) { + return (TargetBytesPerWord * 2) + + pad(ceilingDivide( + objectMaskCount(classTypeMap(t, typeMaps, referer)), 32) + * 4, + TargetBytesPerWord); + } else { + return targetSize(t, typeMaps, p); + } +} + +void copy(Thread* t, uint8_t* src, uint8_t* dst, Type type) +{ + switch (type) { + case Type_int8_t: + memcpy(dst, src, 1); + break; + + case Type_int16_t: { + int16_t s; + memcpy(&s, src, 2); + int16_t d = targetV2(s); + memcpy(dst, &d, 2); + } break; + + case Type_int32_t: + case Type_float: { + int32_t s; + memcpy(&s, src, 4); + int32_t d = targetV4(s); + memcpy(dst, &d, 4); + } break; + + case Type_int64_t: + case Type_double: { + int64_t s; + memcpy(&s, src, 8); + int64_t d = targetV8(s); + memcpy(dst, &d, 8); + } break; + + case Type_int64_t_pad: + case Type_double_pad: + break; + + case Type_intptr_t: { + intptr_t s; + memcpy(&s, src, BytesPerWord); + target_intptr_t d = targetVW(s); + memcpy(dst, &d, TargetBytesPerWord); + } break; + + case Type_object: { + memset(dst, 0, TargetBytesPerWord); + } break; + + default: + abort(t); + } +} + +bool nonObjectsEqual(uint8_t* src, uint8_t* dst, Type type) +{ + switch (type) { + case Type_int8_t: + return memcmp(dst, src, 1) == 0; + + case Type_int16_t: + return memcmp(dst, src, 2) == 0; + + case Type_int32_t: + case Type_float: + return memcmp(dst, src, 4) == 0; + + case Type_int64_t: + case Type_double: + return memcmp(dst, src, 8) == 0; + + case Type_int64_t_pad: + case Type_double_pad: + return true; + + case Type_intptr_t: + return memcmp(dst, src, BytesPerWord) == 0; + + case Type_object: + case Type_object_nogc: + return true; + + default: + abort(); + } +} + +bool nonObjectsEqual(TypeMap* map, uint8_t* src, uint8_t* dst) +{ + for (unsigned i = 0; i < map->fixedFieldCount; ++i) { + Field* field = map->fixedFields() + i; + if (not nonObjectsEqual( + src + field->buildOffset, dst + field->targetOffset, field->type)) { + return false; + } + } + + if (map->targetArrayElementSizeInBytes) { + unsigned fixedSize = map->buildFixedSizeInWords * BytesPerWord; + unsigned count = fieldAtOffset(src, fixedSize - BytesPerWord); + + for (unsigned i = 0; i < count; ++i) { + if (not nonObjectsEqual( + src + fixedSize + (i * map->buildArrayElementSizeInBytes), + dst + (map->targetFixedSizeInWords * TargetBytesPerWord) + + (i * map->targetArrayElementSizeInBytes), + map->arrayElementType)) { + return false; + } + } + } + + return true; +} + +void copy(Thread* t, GcHashMap* typeMaps, object p, uint8_t* dst) +{ + TypeMap* map = typeMap(t, typeMaps, p); + + uint8_t* src = reinterpret_cast(p); + + for (unsigned i = 0; i < map->fixedFieldCount; ++i) { + Field* field = map->fixedFields() + i; + if (field->type > Type_array) + abort(t); + copy(t, src + field->buildOffset, dst + field->targetOffset, field->type); + } + + if (map->targetArrayElementSizeInBytes) { + unsigned fixedSize = map->buildFixedSizeInWords * BytesPerWord; + unsigned count = fieldAtOffset(p, fixedSize - BytesPerWord); + + for (unsigned i = 0; i < count; ++i) { + copy(t, + src + fixedSize + (i * map->buildArrayElementSizeInBytes), + dst + (map->targetFixedSizeInWords * TargetBytesPerWord) + + (i * map->targetArrayElementSizeInBytes), + map->arrayElementType); + } + + if (objectClass(t, p) == type(t, GcClass::Type)) { + uint16_t fixedSize; + uint8_t arrayElementSize; + GcByteArray* array = cast( + t, hashMapFind(t, typeMaps, p, objectHash, objectEqual)); + PROTECT(t, array); + + GcClass* c = cast(t, p); + PROTECT(t, c); + + if (array) { + TypeMap* classMap = reinterpret_cast(array->body().begin()); + + fixedSize + = targetV2(classMap->targetFixedSizeInWords * TargetBytesPerWord); + + arrayElementSize = classMap->targetArrayElementSizeInBytes; + } else if (c->fixedSize() == BytesPerWord * 2 + and c->arrayElementSize() == BytesPerWord) { + fixedSize = targetV2(TargetBytesPerWord * 2); + + arrayElementSize = TargetBytesPerWord; + } else { + fixedSize = 0; + arrayElementSize = 0; + } + + if (fixedSize) { + memcpy(dst + TargetClassFixedSize, &fixedSize, 2); + + memcpy(dst + TargetClassArrayElementSize, &arrayElementSize, 1); + } + + // if (strcmp("vm::lineNumberTable", + // reinterpret_cast(&byteArrayBody(t, className(t, + // p), 0))) == 0) trap(); + } + } else { + switch (map->kind) { + case TypeMap::NormalKind: + if (objectClass(t, p) == type(t, GcField::Type)) { + uint16_t offset + = targetV2(targetFieldOffset(t, typeMaps, cast(t, p))); + memcpy(dst + TargetFieldOffset, &offset, 2); + } + break; + + case TypeMap::SingletonKind: { + unsigned maskSize = singletonMaskSize(map->targetFixedSizeInWords - 2, + TargetBitsPerWord); + + target_uintptr_t targetLength + = targetVW(map->targetFixedSizeInWords - 2 + maskSize); + memcpy(dst + TargetBytesPerWord, &targetLength, TargetBytesPerWord); + + uint8_t* mask = dst + (map->targetFixedSizeInWords * TargetBytesPerWord); + memset(mask, 0, maskSize * TargetBytesPerWord); + + for (unsigned i = 0; i < map->fixedFieldCount; ++i) { + Field* field = map->fixedFields() + i; + if (field->type == Type_object) { + unsigned offset = field->targetOffset / TargetBytesPerWord; + reinterpret_cast(mask)[offset / 32] + |= targetV4(static_cast(1) << (offset % 32)); + } + } + + if (DebugNativeTarget) { + expect(t, + memcmp(src + (map->targetFixedSizeInWords * TargetBytesPerWord), + mask, + singletonMaskSize(map->targetFixedSizeInWords - 2, + TargetBitsPerWord) * TargetBytesPerWord) + == 0); + } + } break; + + case TypeMap::PoolKind: { + unsigned poolMaskSize = vm::poolMaskSize(map->targetFixedSizeInWords - 2, + TargetBitsPerWord); + + unsigned objectMaskSize = singletonMaskSize( + map->targetFixedSizeInWords - 2 + poolMaskSize, TargetBitsPerWord); + + target_uintptr_t targetLength = targetVW(map->targetFixedSizeInWords - 2 + + poolMaskSize + objectMaskSize); + memcpy(dst + TargetBytesPerWord, &targetLength, TargetBytesPerWord); + + uint8_t* poolMask = dst + + (map->targetFixedSizeInWords * TargetBytesPerWord); + + memset(poolMask, 0, poolMaskSize * TargetBytesPerWord); + + uint8_t* objectMask = dst + ((map->targetFixedSizeInWords + poolMaskSize) + * TargetBytesPerWord); + + memset(objectMask, 0, objectMaskSize * TargetBytesPerWord); + + for (unsigned i = 0; i < map->fixedFieldCount; ++i) { + Field* field = map->fixedFields() + i; + switch (field->type) { + case Type_object: + reinterpret_cast(objectMask)[i / 32] + |= targetV4(static_cast(1) << (i % 32)); + break; + + case Type_float: + case Type_double: + reinterpret_cast(poolMask)[i / TargetBitsPerWord] + |= targetVW(static_cast(1) + << (i % TargetBitsPerWord)); + break; + + default: + break; + } + } + + if (DebugNativeTarget) { + expect(t, + memcmp(src + (map->targetFixedSizeInWords * TargetBytesPerWord), + poolMask, + (poolMaskSize + + singletonMaskSize( + map->targetFixedSizeInWords - 2 + poolMaskSize, + TargetBitsPerWord)) * TargetBytesPerWord) == 0); + } + } break; + + default: + abort(t); + } + } +} + +void copy(Thread* t, + GcHashMap* typeMaps, + object referer, + unsigned refererOffset, + object p, + uint8_t* dst) +{ + if (referer and objectClass(t, referer) == type(t, GcClass::Type) + and (refererOffset * BytesPerWord) == ClassObjectMask) { + TypeMap* map = classTypeMap(t, typeMaps, referer); + + memset(dst, 0, TargetBytesPerWord); + + unsigned length = ceilingDivide(objectMaskCount(map), 32); + + target_uintptr_t targetLength = targetVW(length); + + memcpy(dst + TargetBytesPerWord, &targetLength, TargetBytesPerWord); + + memset(dst + (TargetBytesPerWord * 2), 0, length * 4); + + for (unsigned i = 0; i < map->fixedFieldCount; ++i) { + Field* field = map->fixedFields() + i; + if (field->type == Type_object) { + unsigned offset = field->targetOffset / TargetBytesPerWord; + reinterpret_cast(dst + (TargetBytesPerWord * 2))[offset / 32] + |= targetV4(static_cast(1) << (offset % 32)); + } + } + + if (map->targetArrayElementSizeInBytes + and map->arrayElementType == Type_object) { + unsigned offset = map->targetFixedSizeInWords; + reinterpret_cast(dst + (TargetBytesPerWord * 2))[offset / 32] + |= targetV4(static_cast(1) << (offset % 32)); + } + } else { + copy(t, typeMaps, p, dst); + } + + if (DebugNativeTarget) { + expect(t, targetSize(t, typeMaps, p) == baseSize(t, p, objectClass(t, p))); + expect(t, + nonObjectsEqual( + typeMap(t, typeMaps, p), reinterpret_cast(p), dst)); + } +} + +HeapWalker* makeHeapImage(Thread* t, + BootImage* image, + target_uintptr_t* heap, + target_uintptr_t* map, + unsigned capacity, + GcTriple* constants, + GcHashMap* typeMaps) +{ + class Visitor : public HeapVisitor { + public: + Visitor(Thread* t, + GcHashMap* typeMaps, + target_uintptr_t* heap, + target_uintptr_t* map, + unsigned capacity) + : t(t), + typeMaps(typeMaps), + currentObject(0), + currentNumber(0), + currentOffset(0), + heap(heap), + map(map), + position(0), + capacity(capacity) + { + } + + void visit(unsigned number) + { + if (currentObject) { + if (DebugNativeTarget) { + expect(t, + targetOffset( + t, typeMaps, currentObject, currentOffset * BytesPerWord) + == currentOffset * BytesPerWord); + } + + unsigned offset + = currentNumber - 1 + + (targetOffset( + t, typeMaps, currentObject, currentOffset * BytesPerWord) + / TargetBytesPerWord); + + unsigned mark = heap[offset] & (~TargetPointerMask); + unsigned value = number | (mark << TargetBootShift); + + if (value) + targetMarkBit(map, offset); + + heap[offset] = targetVW(value); + } + } + + virtual void root() + { + currentObject = 0; + } + + virtual unsigned visitNew(object p) + { + if (p) { + unsigned size + = targetSize(t, typeMaps, currentObject, currentOffset, p); + + unsigned number; + if ((currentObject + and objectClass(t, currentObject) == type(t, GcClass::Type) + and (currentOffset * BytesPerWord) == ClassStaticTable) + or instanceOf(t, type(t, GcSystemClassLoader::Type), p) + or instanceOf(t, type(t, GcAddendum::Type), p)) { + // Static tables, system classloaders, and addendums must be + // allocated as fixed objects in the heap image so that they + // can be marked as dirty and visited during GC. Otherwise, + // attempts to update references in these objects to point + // to runtime-allocated memory would fail because we don't + // scan non-fixed objects in the heap image during GC. + + target_uintptr_t* dst = heap + position + TargetFixieSizeInWords; + + unsigned maskSize = ceilingDivide(size, TargetBitsPerWord); + + unsigned total = TargetFixieSizeInWords + size + maskSize; + + expect(t, position + total < capacity); + + memset(heap + position, 0, TargetFixieSizeInBytes); + + uint16_t age = targetV2(FixieTenureThreshold + 1); + memcpy(reinterpret_cast(heap + position) + TargetFixieAge, + &age, + 2); + + uint16_t flags = targetV2(1); + memcpy(reinterpret_cast(heap + position) + TargetFixieFlags, + &flags, + 2); + + uint32_t targetSize = targetV4(size); + memcpy(reinterpret_cast(heap + position) + TargetFixieSize, + &targetSize, + 4); + + copy(t, + typeMaps, + currentObject, + currentOffset, + p, + reinterpret_cast(dst)); + + dst[0] |= FixedMark; + + memset(heap + position + TargetFixieSizeInWords + size, + 0, + maskSize * TargetBytesPerWord); + + number = (dst - heap) + 1; + position += total; + } else { + expect(t, position + size < capacity); + + copy(t, + typeMaps, + currentObject, + currentOffset, + p, + reinterpret_cast(heap + position)); + + number = position + 1; + position += size; + } + + visit(number); + + return number; + } else { + return 0; + } + } + + virtual void visitOld(object, unsigned number) + { + visit(number); + } + + virtual void push(object object, unsigned number, unsigned offset) + { + currentObject = object; + currentNumber = number; + currentOffset = offset; + } + + virtual void pop() + { + currentObject = 0; + } + + Thread* t; + GcHashMap* typeMaps; + object currentObject; + unsigned currentNumber; + unsigned currentOffset; + target_uintptr_t* heap; + target_uintptr_t* map; + unsigned position; + unsigned capacity; + } visitor(t, typeMaps, heap, map, capacity / TargetBytesPerWord); + + HeapWalker* w = makeHeapWalker(t, &visitor); + visitRoots(t, image, w, constants); + + image->heapSize = visitor.position * TargetBytesPerWord; + + return w; +} + +void updateConstants(Thread* t, GcTriple* constants, HeapMap* heapTable) +{ + for (; constants; constants = cast(t, constants->third())) { + unsigned target = heapTable->find(constants->first()); + expect(t, target > 0); + + for (Promise::Listener* pl + = static_cast( + cast(t, constants->second())->value())->listener; + pl; + pl = pl->next) { + pl->resolve((target - 1) * TargetBytesPerWord, 0); + } + } +} + +BootImage::Thunk targetThunk(BootImage::Thunk t) +{ + return BootImage::Thunk( + targetV4(t.start), targetV4(t.frameSavedOffset), targetV4(t.length)); +} + +void writeBootImage2(Thread* t, + OutputStream* bootimageOutput, + OutputStream* codeOutput, + BootImage* image, + uint8_t* code, + JavaVM* hostVM, + const char* className, + const char* methodName, + const char* methodSpec, + const char* bootimageStart, + const char* bootimageEnd, + const char* codeimageStart, + const char* codeimageEnd, + bool useLZMA) +{ + GcThrowable* throwable + = cast(t, make(t, type(t, GcOutOfMemoryError::Type))); + // sequence point, for gc (don't recombine statements) + roots(t)->setOutOfMemoryError(t, throwable); + + Zone zone(t->m->heap, 64 * 1024); + + class MyCompilationHandler : public Processor::CompilationHandler { + public: + String heapDup(const char* name) + { + String ret(name); + char* n = (char*)heap->allocate(ret.length + 1); + memcpy(n, ret.text, ret.length + 1); + ret.text = n; + return ret; + } + + virtual void compiled(const void* code, + unsigned size UNUSED, + unsigned frameSize UNUSED, + const char* name) + { + uint64_t offset = reinterpret_cast(code) - codeOffset; + symbols.add(SymbolInfo(offset, heapDup(name))); + // printf("%ld %ld %s.%s%s\n", offset, offset + size, class_, name, spec); + } + + virtual void dispose() + { + } + + DynamicArray symbols; + uint64_t codeOffset; + Heap* heap; + + MyCompilationHandler(uint64_t codeOffset, Heap* heap) + : codeOffset(codeOffset), heap(heap) + { + } + + } compilationHandler(reinterpret_cast(code), t->m->heap); + + t->m->processor->addCompilationHandler(&compilationHandler); + + GcHashMap* classPoolMap; + GcHashMap* typeMaps; + GcTriple* constants; + + { + classPoolMap = makeHashMap(t, 0, 0); + PROTECT(t, classPoolMap); + + roots(t)->setPoolMap(t, classPoolMap); + + typeMaps = makeHashMap(t, 0, 0); + PROTECT(t, typeMaps); + +#include "type-maps.cpp" + + for (unsigned i = 0; i < t->m->types->length(); ++i) { + Type* source = types[i]; + unsigned typeCount = 0; + unsigned fieldCount = 1; + while (source[typeCount] != Type_none) { + ++typeCount; + ++fieldCount; + } + + THREAD_RUNTIME_ARRAY(t, Field, fields, fieldCount); + + init(new (RUNTIME_ARRAY_BODY(fields)) Field, + Type_object, + 0, + BytesPerWord, + 0, + TargetBytesPerWord); + + unsigned buildOffset = BytesPerWord; + unsigned targetOffset = TargetBytesPerWord; + bool sawArray = false; + Type type = Type_none; + unsigned buildSize = 0; + unsigned targetSize = 0; + unsigned fieldOffset = 1; + for (unsigned j = 0; j < typeCount; ++j) { + switch (source[j]) { + case Type_object: + type = Type_object; + buildSize = BytesPerWord; + targetSize = TargetBytesPerWord; + break; + + case Type_object_nogc: + type = Type_object_nogc; + buildSize = BytesPerWord; + targetSize = TargetBytesPerWord; + break; + + case Type_word: + case Type_intptr_t: + case Type_uintptr_t: + type = Type_intptr_t; + buildSize = BytesPerWord; + targetSize = TargetBytesPerWord; + break; + + case Type_int8_t: + case Type_uint8_t: + type = Type_int8_t; + buildSize = targetSize = 1; + break; + + case Type_int16_t: + case Type_uint16_t: + type = Type_int16_t; + buildSize = targetSize = 2; + break; + + case Type_int32_t: + case Type_uint32_t: + case Type_float: + type = Type_int32_t; + buildSize = targetSize = 4; + break; + + case Type_int64_t: + case Type_uint64_t: + case Type_double: + type = Type_int64_t; + buildSize = targetSize = 8; + break; + + case Type_array: + type = Type_none; + buildSize = targetSize = 0; + break; + + default: + abort(t); + } + + if (source[j] == Type_array) { + sawArray = true; + } + + if (not sawArray) { + buildOffset = pad(buildOffset, buildSize); + + targetOffset = pad(targetOffset, targetSize); + + init(new (RUNTIME_ARRAY_BODY(fields) + (fieldOffset++)) Field, + type, + buildOffset, + buildSize, + targetOffset, + targetSize); + + buildOffset += buildSize; + targetOffset += targetSize; + } + } + + unsigned fixedFieldCount; + Type arrayElementType; + unsigned buildArrayElementSize; + unsigned targetArrayElementSize; + if (sawArray) { + fixedFieldCount = fieldCount - 2; + arrayElementType = type; + buildArrayElementSize = buildSize; + targetArrayElementSize = targetSize; + } else { + fixedFieldCount = fieldCount; + arrayElementType = Type_none; + buildArrayElementSize = 0; + targetArrayElementSize = 0; + } + + GcByteArray* array = makeByteArray( + t, + TypeMap::sizeInBytes(ceilingDivide(buildOffset, BytesPerWord), + fixedFieldCount)); + + TypeMap* map = new (array->body().begin()) + TypeMap(ceilingDivide(buildOffset, BytesPerWord), + ceilingDivide(targetOffset, TargetBytesPerWord), + fixedFieldCount, + TypeMap::NormalKind, + buildArrayElementSize, + targetArrayElementSize, + arrayElementType); + + for (unsigned j = 0; j < fixedFieldCount; ++j) { + Field* f = RUNTIME_ARRAY_BODY(fields) + j; + + expect(t, f->buildOffset < map->buildFixedSizeInWords * BytesPerWord); + + map->targetFixedOffsets()[f->buildOffset] = f->targetOffset; + + map->fixedFields()[j] = *f; + } + + hashMapInsert( + t, + typeMaps, + reinterpret_cast(vm::type(t, static_cast(i))), + reinterpret_cast(array), + objectHash); + } + + constants = makeCodeImage(t, + &zone, + image, + code, + hostVM, + className, + methodName, + methodSpec, + typeMaps); + + PROTECT(t, constants); + + // these roots will not be used when the bootimage is loaded, so + // there's no need to preserve them: + roots(t)->setPoolMap(t, 0); + + GcWeakHashMap* map = makeWeakHashMap(t, 0, 0); + // sequence point, for gc (don't recombine statements) + roots(t)->setByteArrayMap(t, map->as(t)); + + // name all primitive classes so we don't try to update immutable + // references at runtime: + { + GcByteArray* name = makeByteArray(t, "void"); + // sequence point, for gc (don't recombine statements) + type(t, GcJvoid::Type)->setName(t, name); + + name = makeByteArray(t, "boolean"); + // sequence point, for gc (don't recombine statements) + type(t, GcJboolean::Type)->setName(t, name); + + name = makeByteArray(t, "byte"); + // sequence point, for gc (don't recombine statements) + type(t, GcJbyte::Type)->setName(t, name); + + name = makeByteArray(t, "short"); + // sequence point, for gc (don't recombine statements) + type(t, GcJshort::Type)->setName(t, name); + + name = makeByteArray(t, "char"); + // sequence point, for gc (don't recombine statements) + type(t, GcJchar::Type)->setName(t, name); + + name = makeByteArray(t, "int"); + // sequence point, for gc (don't recombine statements) + type(t, GcJint::Type)->setName(t, name); + + name = makeByteArray(t, "float"); + // sequence point, for gc (don't recombine statements) + type(t, GcJfloat::Type)->setName(t, name); + + name = makeByteArray(t, "long"); + // sequence point, for gc (don't recombine statements) + type(t, GcJlong::Type)->setName(t, name); + + name = makeByteArray(t, "double"); + // sequence point, for gc (don't recombine statements) + type(t, GcJdouble::Type)->setName(t, name); + } + + // resolve primitive array classes in case they are needed at + // runtime: + { + GcByteArray* name = makeByteArray(t, "[B"); + resolveSystemClass(t, roots(t)->bootLoader(), name, true); + + name = makeByteArray(t, "[Z"); + resolveSystemClass(t, roots(t)->bootLoader(), name, true); + + name = makeByteArray(t, "[S"); + resolveSystemClass(t, roots(t)->bootLoader(), name, true); + + name = makeByteArray(t, "[C"); + resolveSystemClass(t, roots(t)->bootLoader(), name, true); + + name = makeByteArray(t, "[I"); + resolveSystemClass(t, roots(t)->bootLoader(), name, true); + + name = makeByteArray(t, "[J"); + resolveSystemClass(t, roots(t)->bootLoader(), name, true); + + name = makeByteArray(t, "[F"); + resolveSystemClass(t, roots(t)->bootLoader(), name, true); + + name = makeByteArray(t, "[D"); + resolveSystemClass(t, roots(t)->bootLoader(), name, true); + } + } + + target_uintptr_t* heap + = static_cast(t->m->heap->allocate(HeapCapacity)); + + target_uintptr_t* heapMap = static_cast( + t->m->heap->allocate(heapMapSize(HeapCapacity))); + memset(heapMap, 0, heapMapSize(HeapCapacity)); + + HeapWalker* heapWalker = makeHeapImage( + t, image, heap, heapMap, HeapCapacity, constants, typeMaps); + + updateConstants(t, constants, heapWalker->map()); + + image->bootClassCount + = cast(t, roots(t)->bootLoader()->map())->size(); + + unsigned* bootClassTable = static_cast( + t->m->heap->allocate(image->bootClassCount * sizeof(unsigned))); + + { + unsigned i = 0; + for (HashMapIterator it(t, + cast(t, roots(t)->bootLoader()->map())); + it.hasMore();) { + bootClassTable[i++] + = targetVW(heapWalker->map()->find(it.next()->second())); + } + } + + image->appClassCount + = cast(t, roots(t)->appLoader()->map())->size(); + + unsigned* appClassTable = static_cast( + t->m->heap->allocate(image->appClassCount * sizeof(unsigned))); + + { + unsigned i = 0; + for ( + HashMapIterator it(t, cast(t, roots(t)->appLoader()->map())); + it.hasMore();) { + appClassTable[i++] + = targetVW(heapWalker->map()->find(it.next()->second())); + } + } + + image->stringCount = roots(t)->stringMap()->size(); + unsigned* stringTable = static_cast( + t->m->heap->allocate(image->stringCount * sizeof(unsigned))); + + { + unsigned i = 0; + for (HashMapIterator it(t, roots(t)->stringMap()); it.hasMore();) { + stringTable[i++] + = targetVW(heapWalker->map()->find(reinterpret_cast( + cast(t, it.next()->first())->target()))); + } + } + + unsigned* callTable = t->m->processor->makeCallTable(t, heapWalker); + + heapWalker->dispose(); + + image->magic = BootImage::Magic; + image->initialized = 0; + + fprintf(stderr, + "class count %d string count %d call count %d\n" + "heap size %d code size %d\n", + image->bootClassCount, + image->stringCount, + image->callCount, + image->heapSize, + image->codeSize); + + Buffer bootimageData; + + if (true) { + { + BootImage targetImage; + +#ifdef FIELD +#undef FIELD +#endif + +#define FIELD(name) targetImage.name = targetV4(image->name); +#include "bootimage-fields.cpp" +#undef FIELD + +#define THUNK_FIELD(name) \ + targetImage.thunks.name = targetThunk(image->thunks.name); +#include "bootimage-fields.cpp" +#undef THUNK_FIELD + + bootimageData.write(&targetImage, sizeof(BootImage)); + } + + bootimageData.write(bootClassTable, + image->bootClassCount * sizeof(unsigned)); + bootimageData.write(appClassTable, image->appClassCount * sizeof(unsigned)); + bootimageData.write(stringTable, image->stringCount * sizeof(unsigned)); + bootimageData.write(callTable, image->callCount * sizeof(unsigned) * 2); + + unsigned offset = sizeof(BootImage) + + (image->bootClassCount * sizeof(unsigned)) + + (image->appClassCount * sizeof(unsigned)) + + (image->stringCount * sizeof(unsigned)) + + (image->callCount * sizeof(unsigned) * 2); + + while (offset % TargetBytesPerWord) { + uint8_t c = 0; + bootimageData.write(&c, 1); + ++offset; + } + + bootimageData.write(heapMap, + pad(heapMapSize(image->heapSize), TargetBytesPerWord)); + + bootimageData.write(heap, pad(image->heapSize, TargetBytesPerWord)); + + // fwrite(code, pad(image->codeSize, TargetBytesPerWord), 1, codeOutput); + + Platform* platform = Platform::getPlatform( + PlatformInfo((PlatformInfo::Format)AVIAN_TARGET_FORMAT, + (PlatformInfo::Architecture)AVIAN_TARGET_ARCH)); + + if (!platform) { + fprintf(stderr, + "unsupported platform: target-format = %d / target-arch = %d\n", + AVIAN_TARGET_FORMAT, + AVIAN_TARGET_ARCH); + abort(); + } + + uint8_t* bootimage; + size_t bootimageLength; + if (useLZMA) { +#ifdef AVIAN_USE_LZMA + bootimage = encodeLZMA(t->m->system, + t->m->heap, + bootimageData.data, + bootimageData.length, + &bootimageLength); + + fprintf(stderr, "compressed heap size %zu\n", bootimageLength); +#else + abort(t); +#endif + } else { + bootimage = bootimageData.data; + bootimageLength = bootimageData.length; + } + + SymbolInfo bootimageSymbols[] + = {SymbolInfo(0, bootimageStart), + SymbolInfo(bootimageLength, bootimageEnd)}; + + platform->writeObject(bootimageOutput, + Slice(bootimageSymbols, 2), + Slice(bootimage, bootimageLength), + Platform::Writable, + TargetBytesPerWord); + + if (useLZMA) { + t->m->heap->free(bootimage, bootimageLength); + } + + compilationHandler.symbols.add(SymbolInfo(0, codeimageStart)); + compilationHandler.symbols.add(SymbolInfo(image->codeSize, codeimageEnd)); + + platform->writeObject(codeOutput, + Slice(compilationHandler.symbols), + Slice(code, image->codeSize), + Platform::Executable, + TargetBytesPerWord); + + for (SymbolInfo* sym = compilationHandler.symbols.begin(); + sym != compilationHandler.symbols.end() - 2; + sym++) { + t->m->heap->free(const_cast((const void*)sym->name.text), + sym->name.length + 1); + } + } +} + +uint64_t writeBootImage(Thread* t, uintptr_t* arguments) +{ + OutputStream* bootimageOutput = reinterpret_cast(arguments[0]); + OutputStream* codeOutput = reinterpret_cast(arguments[1]); + BootImage* image = reinterpret_cast(arguments[2]); + uint8_t* code = reinterpret_cast(arguments[3]); + 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[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, + bootimageStart, + bootimageEnd, + codeimageStart, + codeimageEnd, + useLZMA); + + return 1; +} + +char* myStrndup(const char* src, unsigned length) +{ + char* s = static_cast(malloc(length + 1)); + memcpy(s, src, length); + s[length] = 0; + return s; +} + +class Arguments { + public: + const char* classpath; + + const char* bootimage; + const char* codeimage; + + const char* hostvm; + + char* entryClass; + char* entryMethod; + char* entrySpec; + + char* bootimageStart; + char* bootimageEnd; + + char* codeimageStart; + char* codeimageEnd; + + bool useLZMA; + + bool maybeSplit(const char* src, char*& destA, char*& destB) + { + if (src) { + const char* split = strchr(src, ':'); + if (!split) { + return false; + } + + destA = myStrndup(src, split - src); + destB = strdup(split + 1); + } + return true; + } + + Arguments(int ac, const char** av) + : entryClass(0), + entryMethod(0), + entrySpec(0), + bootimageStart(0), + bootimageEnd(0), + codeimageStart(0), + codeimageEnd(0) + { + ArgParser parser; + 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, + false, + "bootimage-symbols", + ":"); + Arg codeimageSymbols(parser, + false, + "codeimage-symbols", + ":"); + Arg useLZMA(parser, false, "use-lzma", 0); + + if (!parser.parse(ac, av)) { + parser.printUsage(av[0]); + exit(1); + } + + this->classpath = classpath.value; + this->bootimage = bootimage.value; + this->codeimage = codeimage.value; + this->hostvm = hostvm.value; + this->useLZMA = useLZMA.value != 0; + + if (entry.value) { + if (const char* entryClassEnd = strchr(entry.value, '.')) { + entryClass = myStrndup(entry.value, entryClassEnd - entry.value); + if (const char* entryMethodEnd = strchr(entryClassEnd, '(')) { + entryMethod = myStrndup(entryClassEnd + 1, + entryMethodEnd - entryClassEnd - 1); + entrySpec = strdup(entryMethodEnd); + } else { + entryMethod = strdup(entryClassEnd + 1); + } + } else { + entryClass = strdup(entry.value); + } + } + + if (!maybeSplit(bootimageSymbols.value, bootimageStart, bootimageEnd) + || !maybeSplit(codeimageSymbols.value, codeimageStart, codeimageEnd)) { + fprintf(stderr, "wrong format for symbols\n"); + parser.printUsage(av[0]); + exit(1); + } + + if (!bootimageStart) { + bootimageStart = strdup("_binary_bootimage_bin_start"); + } + + if (!bootimageEnd) { + bootimageEnd = strdup("_binary_bootimage_bin_end"); + } + + if (!codeimageStart) { + codeimageStart = strdup("_binary_codeimage_bin_start"); + } + + if (!codeimageEnd) { + codeimageEnd = strdup("_binary_codeimage_bin_end"); + } + } + + ~Arguments() + { + if (entryClass) { + free(entryClass); + } + if (entryMethod) { + free(entryMethod); + } + if (entrySpec) { + free(entrySpec); + } + if (bootimageStart) { + free(bootimageStart); + } + if (bootimageEnd) { + free(bootimageEnd); + } + if (codeimageStart) { + free(codeimageStart); + } + if (codeimageEnd) { + free(codeimageEnd); + } + } + + void dump() + { + printf( + "classpath = %s\n" + "bootimage = %s\n" + "codeimage = %s\n" + "hostvm = %s\n" + "entryClass = %s\n" + "entryMethod = %s\n" + "entrySpec = %s\n" + "bootimageStart = %s\n" + "bootimageEnd = %s\n" + "codeimageStart = %s\n" + "codeimageEnd = %s\n", + classpath, + bootimage, + codeimage, + hostvm, + entryClass, + entryMethod, + entrySpec, + bootimageStart, + bootimageEnd, + codeimageStart, + codeimageEnd); + } +}; + +} // namespace + +int main(int ac, const char** av) +{ + Arguments args(ac, av); + // args.dump(); + + System* s = makeSystem(); + Heap* h = makeHeap(s, HeapCapacity * 2); + Classpath* c = makeClasspath(s, h, AVIAN_JAVA_HOME, AVIAN_EMBED_PREFIX); + Finder* f = makeFinder(s, h, args.classpath, 0); + Processor* p = makeProcessor(s, h, 0, false); + +// todo: currently, the compiler cannot compile code with jumps or +// calls spanning more than the maximum size of an immediate value +// in a branch instruction for the target architecture (~32MB on ARM). +// When that limitation is removed, we'll be able to specify a +// capacity as large as we like here: +#if (AVIAN_TARGET_ARCH == AVIAN_ARCH_X86_64) \ + || (AVIAN_TARGET_ARCH == AVIAN_ARCH_X86) + const unsigned CodeCapacity = 128 * 1024 * 1024; +#else + const unsigned CodeCapacity = 30 * 1024 * 1024; +#endif + + Slice code = Slice::alloc(h, CodeCapacity); + BootImage image; + p->initialize(&image, code); + + Machine* m = new (h->allocate(sizeof(Machine))) + Machine(s, h, f, 0, p, c, 0, 0, 0, 0, 128 * 1024); + Thread* t = p->makeThread(m, 0, 0); + + enter(t, Thread::ActiveState); + enter(t, Thread::IdleState); + + FileOutputStream bootimageOutput(args.bootimage); + if (!bootimageOutput.isValid()) { + fprintf(stderr, "unable to open %s\n", args.bootimage); + return -1; + } + + FileOutputStream codeOutput(args.codeimage); + if (!codeOutput.isValid()) { + fprintf(stderr, "unable to open %s\n", args.codeimage); + 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), + reinterpret_cast(args.bootimageStart), + reinterpret_cast(args.bootimageEnd), + reinterpret_cast(args.codeimageStart), + reinterpret_cast(args.codeimageEnd), + static_cast(args.useLZMA)}; + + run(t, writeBootImage, arguments); + + if (hostVM) { + hostVM->vtable->DestroyJavaVM(hostVM); + hostVMLibrary->disposeAll(); + } + + if (t->exception) { + printTrace(t, t->exception); + return -1; + } else { + return 0; + } +} diff --git a/sgx-jvm/avian/src/tools/object-writer/CMakeLists.txt b/sgx-jvm/avian/src/tools/object-writer/CMakeLists.txt new file mode 100644 index 0000000000..1437bf1d67 --- /dev/null +++ b/sgx-jvm/avian/src/tools/object-writer/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(object_writer + elf.cpp + mach-o.cpp + pe.cpp + tools.cpp +) \ No newline at end of file diff --git a/sgx-jvm/avian/src/tools/object-writer/elf.cpp b/sgx-jvm/avian/src/tools/object-writer/elf.cpp new file mode 100644 index 0000000000..a54690d10c --- /dev/null +++ b/sgx-jvm/avian/src/tools/object-writer/elf.cpp @@ -0,0 +1,407 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include +#include +#include +#include + +#include "endianness.h" + +#include + +#define EI_NIDENT 16 + +#define EI_MAG0 0 +#define EI_MAG1 1 +#define EI_MAG2 2 +#define EI_MAG3 3 +#define EI_CLASS 4 +#define EI_DATA 5 +#define EI_VERSION 6 +#define EI_OSABI 7 +#define EI_ABIVERSION 8 + +#define ELFMAG0 0x7f +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' + +#define ELFCLASS64 2 +#define ELFCLASS32 1 + +#define EV_CURRENT 1 + +#define ELFDATA2LSB 1 +#define ELFDATA2MSB 2 + +#define ELFOSABI_SYSV 0 + +#define ET_REL 1 + +#define EM_386 3 +#define EM_X86_64 62 +#define EM_ARM 40 +#define EM_AARCH64 183 + +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 + +#define SHF_WRITE (1 << 0) +#define SHF_ALLOC (1 << 1) +#define SHF_EXECINSTR (1 << 2) + +#define STB_GLOBAL 1 + +#define STT_NOTYPE 0 + +#define STV_DEFAULT 0 + +#define SYMBOL_INFO(bind, type) (((bind) << 4) + ((type)&0xf)) + +#define OSABI ELFOSABI_SYSV + +namespace { + +using namespace avian::tools; +using namespace avian::util; + +template +struct ElfTypes { + typedef uint16_t Half; + typedef uint32_t Word; + typedef AddrTy Addr; + typedef uint64_t Xword; + typedef uint16_t Section; + typedef AddrTy Off; + typedef AddrTy XFlags; + static const unsigned BytesPerWord = sizeof(AddrTy); +}; + +template +struct Symbol_Ty; + +template <> +struct Symbol_Ty { + typedef ElfTypes Elf; + + Elf::Word st_name; + unsigned char st_info; + unsigned char st_other; + Elf::Section st_shndx; + Elf::Addr st_value; + Elf::Xword st_size; +}; + +template <> +struct Symbol_Ty { + typedef ElfTypes Elf; + + Elf::Word st_name; + Elf::Addr st_value; + Elf::Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf::Section st_shndx; +}; + +using avian::endian::Endianness; + +#define V1 Endianness::v1 +#define V2 Endianness::v2 +#define V3 Endianness::v3 +#define V4 Endianness::v4 +#define VANY Endianness::vAny + +unsigned getElfPlatform(PlatformInfo::Architecture arch) +{ + switch (arch) { + case PlatformInfo::x86_64: + return EM_X86_64; + case PlatformInfo::x86: + return EM_386; + case PlatformInfo::Arm: + return EM_ARM; + case PlatformInfo::Arm64: + return EM_AARCH64; + default: + return ~0; + } +} + +const char* getSectionName(unsigned accessFlags, unsigned& sectionFlags) +{ + sectionFlags = SHF_ALLOC; + if (accessFlags & Platform::Writable) { + if (accessFlags & Platform::Executable) { + sectionFlags |= SHF_WRITE | SHF_EXECINSTR; + return ".rwx"; + } else { + sectionFlags |= SHF_WRITE; + return ".data"; + } + } else if (accessFlags & Platform::Executable) { + sectionFlags |= SHF_EXECINSTR; + return ".text"; + } else { + return ".rodata"; + } +} + +template +class ElfPlatform : public Platform { + public: + typedef ElfTypes Elf; + static const unsigned Class = Elf::BytesPerWord / 4; + + struct FileHeader { + unsigned char e_ident[EI_NIDENT]; + typename Elf::Half e_type; + typename Elf::Half e_machine; + typename Elf::Word e_version; + typename Elf::Addr e_entry; + typename Elf::Off e_phoff; + typename Elf::Off e_shoff; + typename Elf::Word e_flags; + typename Elf::Half e_ehsize; + typename Elf::Half e_phentsize; + typename Elf::Half e_phnum; + typename Elf::Half e_shentsize; + typename Elf::Half e_shnum; + typename Elf::Half e_shstrndx; + }; + + struct SectionHeader { + typename Elf::Word sh_name; + typename Elf::Word sh_type; + typename Elf::XFlags sh_flags; + typename Elf::Addr sh_addr; + typename Elf::Off sh_offset; + typename Elf::Off sh_size; + typename Elf::Word sh_link; + typename Elf::Word sh_info; + typename Elf::Addr sh_addralign; + typename Elf::Off sh_entsize; + }; + + typedef Symbol_Ty Symbol; + + static const unsigned Encoding = TargetLittleEndian ? ELFDATA2LSB + : ELFDATA2MSB; + + const unsigned machine; + + ElfPlatform(PlatformInfo::Architecture arch) + : Platform(PlatformInfo(PlatformInfo::Elf, arch)), + machine(getElfPlatform(arch)) + { + } + + class FileWriter { + public: + unsigned sectionCount; + unsigned sectionStringTableSectionNumber; + + AddrTy dataOffset; + + FileHeader header; + StringTable strings; + + FileWriter(unsigned machine) + : sectionCount(0), dataOffset(sizeof(FileHeader)) + { + memset(&header, 0, sizeof(FileHeader)); + header.e_ident[EI_MAG0] = V1(ELFMAG0); + header.e_ident[EI_MAG1] = V1(ELFMAG1); + header.e_ident[EI_MAG2] = V1(ELFMAG2); + header.e_ident[EI_MAG3] = V1(ELFMAG3); + header.e_ident[EI_CLASS] = V1(Class); + header.e_ident[EI_DATA] = V1(Encoding); + header.e_ident[EI_VERSION] = V1(EV_CURRENT); + header.e_ident[EI_OSABI] = V1(OSABI); + header.e_ident[EI_ABIVERSION] = V1(0); + header.e_type = V2(ET_REL); + header.e_machine = V2(machine); + header.e_version = V4(EV_CURRENT); + header.e_entry = VANY(static_cast(0)); + header.e_phoff = VANY(static_cast(0)); + header.e_shoff = VANY(static_cast(sizeof(FileHeader))); + header.e_flags = V4(machine == EM_ARM ? 0x04000000 : 0); + header.e_ehsize = V2(sizeof(FileHeader)); + header.e_phentsize = V2(0); + header.e_phnum = V2(0); + header.e_shentsize = V2(sizeof(SectionHeader)); + } + + void writeHeader(OutputStream* out) + { + header.e_shnum = V2(sectionCount); + header.e_shstrndx = V2(sectionStringTableSectionNumber); + out->writeChunk(&header, sizeof(FileHeader)); + } + }; + + class SectionWriter { + public: + FileWriter& file; + String name; + SectionHeader header; + const size_t* dataSize; + const uint8_t* const* data; + + SectionWriter(FileWriter& file) : file(file), name(""), dataSize(0), data(0) + { + memset(&header, 0, sizeof(SectionHeader)); + file.sectionCount++; + file.dataOffset += sizeof(SectionHeader); + size_t nameOffset = file.strings.add(name); + header.sh_name = V4(nameOffset); + } + + SectionWriter(FileWriter& file, + const char* chname, + unsigned type, + AddrTy flags, + unsigned alignment, + AddrTy addr, + const uint8_t* const* data, + size_t* dataSize, + size_t entsize = 0, + unsigned link = 0) + : file(file), name(chname), dataSize(dataSize), data(data) + { + if (strcmp(chname, ".shstrtab") == 0) { + file.sectionStringTableSectionNumber = file.sectionCount; + } + file.sectionCount++; + file.dataOffset += sizeof(SectionHeader); + size_t nameOffset = file.strings.add(name); + + header.sh_name = V4(nameOffset); + header.sh_type = V4(type); + header.sh_flags = VANY(flags); + header.sh_addr = VANY(addr); + // header.sh_offset = VANY(static_cast(bodySectionOffset)); + // header.sh_size = VANY(static_cast(*dataSize)); + header.sh_link = V4(link); + header.sh_info = V4(0); + header.sh_addralign = VANY(static_cast(alignment)); + header.sh_entsize = VANY(static_cast(entsize)); + } + + void writeHeader(OutputStream* out) + { + if (dataSize) { + header.sh_offset = VANY(file.dataOffset); + header.sh_size = VANY(static_cast(*dataSize)); + file.dataOffset += *dataSize; + } + + out->writeChunk(&header, sizeof(SectionHeader)); + } + + void writeData(OutputStream* out) + { + if (data) { + out->writeChunk(*data, *dataSize); + } + } + }; + + virtual bool writeObject(OutputStream* out, + Slice symbols, + Slice data, + unsigned accessFlags, + unsigned alignment) + { + unsigned sectionFlags; + const char* sectionName = getSectionName(accessFlags, sectionFlags); + + StringTable symbolStringTable; + Buffer symbolTable; + + FileWriter file(machine); + + const int bodySectionNumber = 1; + const int stringTableSectionNumber = 3; + + SectionWriter sections[] = {SectionWriter(file), // null section + SectionWriter(file, + sectionName, + SHT_PROGBITS, + sectionFlags, + alignment, + 0, + &data.items, + &data.count), // body section + SectionWriter(file, + ".shstrtab", + SHT_STRTAB, + 0, + 1, + 0, + &file.strings.data, + &file.strings.length), + SectionWriter(file, + ".strtab", + SHT_STRTAB, + 0, + 1, + 0, + &symbolStringTable.data, + &symbolStringTable.length), + SectionWriter(file, + ".symtab", + SHT_SYMTAB, + 0, + 8, + 0, + &symbolTable.data, + &symbolTable.length, + sizeof(Symbol), + stringTableSectionNumber)}; + + // for some reason, string tables require a null first element... + symbolStringTable.add(""); + + for (SymbolInfo* sym = symbols.begin(); sym != symbols.end(); sym++) { + size_t nameOffset = symbolStringTable.add(sym->name); + + Symbol symbolStruct; + symbolStruct.st_name = V4(nameOffset); + symbolStruct.st_value = VANY(static_cast(sym->addr)); + symbolStruct.st_size = VANY(static_cast(0)); + symbolStruct.st_info = V1(SYMBOL_INFO(STB_GLOBAL, STT_NOTYPE)); + symbolStruct.st_other = V1(STV_DEFAULT); + symbolStruct.st_shndx = V2(bodySectionNumber); + symbolTable.write(&symbolStruct, sizeof(Symbol)); + } + + file.writeHeader(out); + + for (unsigned i = 0; i < file.sectionCount; i++) { + sections[i].writeHeader(out); + } + + for (unsigned i = 0; i < file.sectionCount; i++) { + sections[i].writeData(out); + } + + return true; + } +}; + +ElfPlatform elfX86Platform(PlatformInfo::x86); +ElfPlatform elfArmPlatform(PlatformInfo::Arm); +ElfPlatform elfArm64Platform(PlatformInfo::Arm64); +ElfPlatform elfX86_64Platform(PlatformInfo::x86_64); + +} // namespace diff --git a/sgx-jvm/avian/src/tools/object-writer/endianness.h b/sgx-jvm/avian/src/tools/object-writer/endianness.h new file mode 100644 index 0000000000..f29c90bf24 --- /dev/null +++ b/sgx-jvm/avian/src/tools/object-writer/endianness.h @@ -0,0 +1,90 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_ENDIANNESS_H +#define AVIAN_ENDIANNESS_H + +namespace avian { + +namespace endian { + +static union { + uint32_t i; + char c[4]; +} _DetectEndianness = {1}; + +const bool LittleEndian = _DetectEndianness.c[0] == 1; + +template +class Endianness { + public: + static inline uint8_t v1(uint8_t v) + { + return v; + } + + static inline uint16_t v2(uint16_t v) + { + if (LittleEndian == TargetLittleEndian) { + return v; + } else { + return ((v >> 8) & 0xFF) | (v << 8); + } + } + + static inline uint32_t v4(uint32_t v) + { + if (LittleEndian == TargetLittleEndian) { + return v; + } else { + return ((v >> 24) & 0x000000FF) | ((v >> 8) & 0x0000FF00) + | ((v << 8) & 0x00FF0000) | ((v << 24)); + } + } + + static inline uint32_t vAny(uint32_t v) + { + return v4(v); + } + + static inline uint64_t v8(uint64_t v) + { + if (LittleEndian == TargetLittleEndian) { + return v; + } else { + return ((static_cast(v) >> 56) + & (static_cast(0xff) << 0)) + | ((static_cast(v) >> 40) + & (static_cast(0xff) << 8)) + | ((static_cast(v) >> 24) + & (static_cast(0xff) << 16)) + | ((static_cast(v) >> 8) + & (static_cast(0xff) << 24)) + | ((static_cast(v) << 8) + & (static_cast(0xff) << 32)) + | ((static_cast(v) << 24) + & (static_cast(0xff) << 40)) + | ((static_cast(v) << 40) + & (static_cast(0xff) << 48)) + | ((static_cast(v) << 56)); + } + } + + static inline uint64_t vAny(uint64_t v) + { + return v8(v); + } +}; + +} // namespace endian + +} // namespace avian + +#endif // AVIAN_ENDIANNESS_H diff --git a/sgx-jvm/avian/src/tools/object-writer/mach-o.cpp b/sgx-jvm/avian/src/tools/object-writer/mach-o.cpp new file mode 100644 index 0000000000..85f713ba23 --- /dev/null +++ b/sgx-jvm/avian/src/tools/object-writer/mach-o.cpp @@ -0,0 +1,292 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include +#include +#include + +#include "endianness.h" + +#include + +#define MH_MAGIC_64 0xfeedfacf +#define MH_MAGIC 0xfeedface + +#define MH_OBJECT 1 + +#define LC_SYMTAB 2 + +#define S_REGULAR 0 + +#define N_SECT 0xe +#define N_EXT 0x1 + +#define CPU_ARCH_ABI64 0x01000000 + +#define CPU_TYPE_I386 7 +#define CPU_TYPE_X86_64 (CPU_TYPE_I386 | CPU_ARCH_ABI64) +#define CPU_TYPE_ARM 12 +#define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64) + +#define CPU_SUBTYPE_I386_ALL 3 +#define CPU_SUBTYPE_X86_64_ALL CPU_SUBTYPE_I386_ALL +#define CPU_SUBTYPE_ARM_V7 9 +#define CPU_SUBTYPE_ARM_V8 13 + +namespace { + +using namespace avian::tools; +using namespace avian::util; + +typedef int cpu_type_t; +typedef int cpu_subtype_t; +typedef int vm_prot_t; + +using avian::endian::Endianness; + +#define V1 Endianness::v1 +#define V2 Endianness::v2 +#define V3 Endianness::v3 +#define V4 Endianness::v4 +#define VANY Endianness::vAny + +inline unsigned log(unsigned n) +{ + unsigned r = 0; + for (unsigned i = 1; i < n; ++r) + i <<= 1; + return r; +} + +template +class MachOPlatform : public Platform { + public: + struct FileHeader { + uint32_t magic; + cpu_type_t cputype; + cpu_subtype_t cpusubtype; + uint32_t filetype; + uint32_t ncmds; + uint32_t sizeofcmds; + + union { + uint32_t flags; + AddrTy flagsAndMaybeReserved; + }; + }; + + struct SegmentCommand { + uint32_t cmd; + uint32_t cmdsize; + char segname[16]; + AddrTy vmaddr; + AddrTy vmsize; + AddrTy fileoff; + AddrTy filesize; + vm_prot_t maxprot; + vm_prot_t initprot; + uint32_t nsects; + uint32_t flags; + }; + + struct Section { + char sectname[16]; + char segname[16]; + AddrTy addr; + AddrTy size; + uint32_t offset; + uint32_t align; + uint32_t reloff; + uint32_t nreloc; + uint32_t flags; + uint32_t reserved1; + AddrTy reserved2AndMaybe3; + }; + + struct NList { + union { + uint32_t n_strx; + } n_un; + uint8_t n_type; + uint8_t n_sect; + uint16_t n_desc; + AddrTy n_value; + }; + + struct SymtabCommand { + uint32_t cmd; + uint32_t cmdsize; + uint32_t symoff; + uint32_t nsyms; + uint32_t stroff; + uint32_t strsize; + }; + + static const unsigned BytesPerWord = sizeof(AddrTy); + static const unsigned Segment = BytesPerWord == 8 ? 0x19 : 1; + static const unsigned Magic = BytesPerWord == 8 ? 0xfeedfacf : 0xfeedface; + + static inline unsigned pad(unsigned n) + { + return (n + (BytesPerWord - 1)) & ~(BytesPerWord - 1); + } + + virtual bool writeObject(OutputStream* out, + Slice symbols, + Slice data, + unsigned accessFlags, + unsigned alignment) + { + cpu_type_t cpuType; + cpu_subtype_t cpuSubType; + switch (info.arch) { + case PlatformInfo::x86_64: + cpuType = CPU_TYPE_X86_64; + cpuSubType = CPU_SUBTYPE_X86_64_ALL; + break; + case PlatformInfo::x86: + cpuType = CPU_TYPE_I386; + cpuSubType = CPU_SUBTYPE_I386_ALL; + break; + case PlatformInfo::Arm: + cpuType = CPU_TYPE_ARM; + cpuSubType = CPU_SUBTYPE_ARM_V7; + break; + case PlatformInfo::Arm64: + cpuType = CPU_TYPE_ARM64; + cpuSubType = CPU_SUBTYPE_ARM_V8; + break; + default: + // should never happen (see MachOPlatform declarations at bottom) + fprintf(stderr, "unsupported architecture: %d\n", info.arch); + return false; + } + + const char* segmentName; + const char* sectionName; + if (accessFlags & Writable) { + if (accessFlags & Executable) { + segmentName = "__RWX"; + sectionName = "__rwx"; + } else { + segmentName = "__DATA"; + sectionName = "__data"; + } + } else { + segmentName = "__TEXT"; + sectionName = "__text"; + } + + FileHeader header = { + V4(Magic), // magic + static_cast(V4(cpuType)), + static_cast(V4(cpuSubType)), + V4(MH_OBJECT), // filetype, + V4(2), // ncmds + V4(sizeof(SegmentCommand) + sizeof(Section) + + sizeof(SymtabCommand)), // sizeofcmds + {V4(0)} // flags + }; + + AddrTy finalSize = pad(data.count); + + SegmentCommand segment = { + V4(Segment), // cmd + V4(sizeof(SegmentCommand) + sizeof(Section)), // cmdsize + "", // segname + VANY(static_cast(0)), // vmaddr + VANY(static_cast(finalSize)), // vmsize + VANY(static_cast(sizeof(FileHeader) + sizeof(SegmentCommand) + + sizeof(Section) + + sizeof(SymtabCommand))), // fileoff + VANY(static_cast(finalSize)), // filesize + static_cast(V4(7)), // maxprot + static_cast(V4(7)), // initprot + V4(1), // nsects + V4(0) // flags + }; + + strncpy(segment.segname, segmentName, sizeof(segment.segname)); + + Section sect = { + "", // sectname + "", // segname + VANY(static_cast(0)), // addr + VANY(static_cast(finalSize)), // size + V4(sizeof(FileHeader) + sizeof(SegmentCommand) + sizeof(Section) + + sizeof(SymtabCommand)), // offset + V4(log(alignment)), // align + V4(0), // reloff + V4(0), // nreloc + V4(S_REGULAR), // flags + V4(0), // reserved1 + V4(0), // reserved2 + }; + + strncpy(sect.segname, segmentName, sizeof(sect.segname)); + strncpy(sect.sectname, sectionName, sizeof(sect.sectname)); + + StringTable strings; + strings.add(""); + Buffer symbolList; + + for (SymbolInfo* sym = symbols.begin(); sym != symbols.end(); sym++) { + unsigned offset = strings.length; + strings.write("_", 1); + strings.add(sym->name); + NList symbol = { + {V4(offset)}, // n_un + V1(N_SECT | N_EXT), // n_type + V1(1), // n_sect + V2(0), // n_desc + VANY(static_cast(sym->addr)) // n_value + }; + symbolList.write(&symbol, sizeof(NList)); + } + + SymtabCommand symbolTable = { + V4(LC_SYMTAB), // cmd + V4(sizeof(SymtabCommand)), // cmdsize + V4(sizeof(FileHeader) + sizeof(SegmentCommand) + sizeof(Section) + + sizeof(SymtabCommand) + finalSize), // symoff + V4(symbols.count), // nsyms + V4(sizeof(FileHeader) + sizeof(SegmentCommand) + sizeof(Section) + + sizeof(SymtabCommand) + finalSize + + (sizeof(NList) * symbols.count)), // stroff + V4(strings.length), // strsize + }; + + out->writeChunk(&header, sizeof(header)); + out->writeChunk(&segment, sizeof(segment)); + out->writeChunk(§, sizeof(sect)); + out->writeChunk(&symbolTable, sizeof(symbolTable)); + + out->writeChunk(data.items, data.count); + out->writeRepeat(0, finalSize - data.count); + + out->writeChunk(symbolList.data, symbolList.length); + + out->writeChunk(strings.data, strings.length); + + return true; + } + + MachOPlatform(PlatformInfo::Architecture arch) + : Platform(PlatformInfo(PlatformInfo::MachO, arch)) + { + } +}; + +MachOPlatform darwinx86Platform(PlatformInfo::x86); +MachOPlatform darwinArmPlatform(PlatformInfo::Arm); +MachOPlatform darwinArm64Platform(PlatformInfo::Arm64); +MachOPlatform darwinx86_64Platform(PlatformInfo::x86_64); + +} // namespace diff --git a/sgx-jvm/avian/src/tools/object-writer/pe.cpp b/sgx-jvm/avian/src/tools/object-writer/pe.cpp new file mode 100644 index 0000000000..22888f1ecb --- /dev/null +++ b/sgx-jvm/avian/src/tools/object-writer/pe.cpp @@ -0,0 +1,299 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include +#include +#include +#include + +#include + +namespace { + +// --- winnt.h ---- +#define IMAGE_SIZEOF_SHORT_NAME 8 + +#define IMAGE_FILE_RELOCS_STRIPPED \ + 0x0001 // Relocation info stripped from file. +#define IMAGE_FILE_LINE_NUMS_STRIPPED \ + 0x0004 // Line nunbers stripped from file. +#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8) +#define IMAGE_FILE_MACHINE_I386 0x014c // Intel 386. +#define IMAGE_FILE_MACHINE_ARM 0x01c0 // ARM Little-Endian +#define IMAGE_FILE_MACHINE_THUMB 0x01c2 // ARM Thumb/Thumb-2 Little-Endian +#define IMAGE_FILE_MACHINE_ARMNT 0x01c4 // ARM Thumb-2 Little-Endian +#define IMAGE_FILE_32BIT_MACHINE 0x0100 // 32 bit word machine. + +#define IMAGE_SCN_ALIGN_1BYTES 0x100000 +#define IMAGE_SCN_ALIGN_2BYTES 0x200000 +#define IMAGE_SCN_ALIGN_4BYTES 0x300000 +#define IMAGE_SCN_ALIGN_8BYTES 0x400000 +#define IMAGE_SCN_MEM_EXECUTE 0x20000000 +#define IMAGE_SCN_MEM_READ 0x40000000 +#define IMAGE_SCN_MEM_WRITE 0x80000000 +#define IMAGE_SCN_CNT_CODE 32 + +#ifdef _MSC_VER +#define PACKED_STRUCT _declspec(align(1)) +#else +#define PACKED_STRUCT __attribute__((packed)) +#endif + +struct IMAGE_FILE_HEADER { + uint16_t Machine; + uint16_t NumberOfSections; + uint32_t TimeDateStamp; + uint32_t PointerToSymbolTable; + uint32_t NumberOfSymbols; + uint16_t SizeOfOptionalHeader; + uint16_t Characteristics; +} PACKED_STRUCT; + +struct IMAGE_SECTION_HEADER { + uint8_t Name[IMAGE_SIZEOF_SHORT_NAME]; + union { + uint32_t PhysicalAddress; + uint32_t VirtualSize; + } Misc; + uint32_t VirtualAddress; + uint32_t SizeOfRawData; + uint32_t PointerToRawData; + uint32_t PointerToRelocations; + uint32_t PointerToLinenumbers; + uint16_t NumberOfRelocations; + uint16_t NumberOfLinenumbers; + uint32_t Characteristics; +} PACKED_STRUCT; + +struct IMAGE_SYMBOL { + union { + struct { + uint32_t Short; + uint32_t Long; + } Name; + } N; + uint32_t Value; + int16_t SectionNumber; + uint16_t Type; + uint8_t StorageClass; + uint8_t NumberOfAuxSymbols; +} PACKED_STRUCT; +// --- winnt.h ---- + +inline unsigned pad(unsigned n) +{ + return (n + (4 - 1)) & ~(4 - 1); +} + +using namespace avian::tools; +using namespace avian::util; + +template +class WindowsPlatform : public Platform { + public: + class FileWriter { + public: + unsigned sectionCount; + unsigned symbolCount; + unsigned dataStart; + unsigned dataOffset; + + IMAGE_FILE_HEADER header; + + StringTable strings; + Buffer symbols; + + FileWriter(unsigned machine, unsigned machineMask, unsigned symbolCount) + : sectionCount(0), + symbolCount(symbolCount), + dataStart(sizeof(IMAGE_FILE_HEADER)), + dataOffset(0) + { + header.Machine = machine; + // header.NumberOfSections = sectionCount; + header.TimeDateStamp = 0; + // header.PointerToSymbolTable = sizeof(IMAGE_FILE_HEADER) + // + sizeof(IMAGE_SECTION_HEADER) + // + pad(size); + // header.NumberOfSymbols = symbolCount; + header.SizeOfOptionalHeader = 0; + header.Characteristics = IMAGE_FILE_RELOCS_STRIPPED + | IMAGE_FILE_LINE_NUMS_STRIPPED | machineMask; + } + + void writeHeader(OutputStream* out) + { + header.NumberOfSections = sectionCount; + header.PointerToSymbolTable = dataStart + dataOffset; + dataOffset = pad(dataOffset + symbolCount * sizeof(IMAGE_SYMBOL)); + header.NumberOfSymbols = symbolCount; + out->writeChunk(&header, sizeof(IMAGE_FILE_HEADER)); + } + + void addSymbol(String name, + unsigned addr, + unsigned sectionNumber, + unsigned type, + unsigned storageClass) + { + unsigned nameOffset = strings.add(name); + IMAGE_SYMBOL symbol = { + {{0, 0}}, // Name + addr, // Value + static_cast(sectionNumber), // SectionNumber + static_cast(type), // Type + static_cast(storageClass), // StorageClass + 0, // NumberOfAuxSymbols + }; + symbol.N.Name.Long = nameOffset + 4; + symbols.write(&symbol, sizeof(IMAGE_SYMBOL)); + } + + void writeData(OutputStream* out) + { + out->writeChunk(symbols.data, symbols.length); + uint32_t size = strings.length + 4; + out->writeChunk(&size, 4); + out->writeChunk(strings.data, strings.length); + } + }; + + class SectionWriter { + public: + FileWriter& file; + IMAGE_SECTION_HEADER header; + size_t dataSize; + size_t finalSize; + const uint8_t* data; + unsigned dataOffset; + + SectionWriter(FileWriter& file, + const char* name, + unsigned sectionMask, + const uint8_t* data, + size_t dataSize) + : file(file), dataSize(dataSize), finalSize(pad(dataSize)), data(data) + { + file.sectionCount++; + file.dataStart += sizeof(IMAGE_SECTION_HEADER); + strcpy(reinterpret_cast(header.Name), name); + header.Misc.VirtualSize = 0; + header.SizeOfRawData = finalSize; + // header.PointerToRawData = file.dataOffset; + dataOffset = file.dataOffset; + file.dataOffset += finalSize; + header.PointerToRelocations = 0; + header.PointerToLinenumbers = 0; + header.NumberOfRelocations = 0; + header.NumberOfLinenumbers = 0; + header.Characteristics = sectionMask; + } + + void writeHeader(OutputStream* out) + { + header.PointerToRawData = dataOffset + file.dataStart; + out->writeChunk(&header, sizeof(IMAGE_SECTION_HEADER)); + } + + void writeData(OutputStream* out) + { + out->writeChunk(data, dataSize); + out->writeRepeat(0, finalSize - dataSize); + } + }; + + virtual bool writeObject(OutputStream* out, + Slice symbols, + Slice data, + unsigned accessFlags, + unsigned alignment) + { + int machine; + int machineMask; + + if (Architecture == PlatformInfo::x86_64) { + machine = IMAGE_FILE_MACHINE_AMD64; + machineMask = 0; + } else if (Architecture == PlatformInfo::x86) { + machine = IMAGE_FILE_MACHINE_I386; + machineMask = IMAGE_FILE_32BIT_MACHINE; + } else if (Architecture == PlatformInfo::Arm) { + machine = IMAGE_FILE_MACHINE_ARMNT; + machineMask = IMAGE_FILE_32BIT_MACHINE; + } + + int sectionMask; + switch (alignment) { + case 0: + case 1: + sectionMask = IMAGE_SCN_ALIGN_1BYTES; + break; + case 2: + sectionMask = IMAGE_SCN_ALIGN_2BYTES; + break; + case 4: + sectionMask = IMAGE_SCN_ALIGN_4BYTES; + break; + case 8: + sectionMask = IMAGE_SCN_ALIGN_8BYTES; + break; + default: + fprintf(stderr, "unsupported alignment: %d\n", alignment); + return false; + } + + sectionMask |= IMAGE_SCN_MEM_READ; + + const char* sectionName; + if (accessFlags & Platform::Writable) { + if (accessFlags & Platform::Executable) { + sectionName = ".rwx"; + sectionMask |= IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE + | IMAGE_SCN_CNT_CODE; + } else { + sectionName = ".data"; + sectionMask |= IMAGE_SCN_MEM_WRITE; + } + } else { + sectionName = ".text"; + sectionMask |= IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_CNT_CODE; + } + + FileWriter file(machine, machineMask, symbols.count); + + SectionWriter section( + file, sectionName, sectionMask, data.items, data.count); + + file.writeHeader(out); + + for (SymbolInfo* sym = symbols.begin(); sym != symbols.end(); sym++) { + file.addSymbol(sym->name, sym->addr, 1, 0, 2); + } + + section.writeHeader(out); + + section.writeData(out); + + file.writeData(out); + + return true; + } + + WindowsPlatform() : Platform(PlatformInfo(PlatformInfo::Pe, Architecture)) + { + } +}; + +WindowsPlatform<4, PlatformInfo::x86> windows32Platform; +WindowsPlatform<8, PlatformInfo::x86_64> windows64Platform; +WindowsPlatform<4, PlatformInfo::Arm> + windowsRtPlatform; // Windows Phone 8 and Windows RT + +} // namespace diff --git a/sgx-jvm/avian/src/tools/object-writer/tools.cpp b/sgx-jvm/avian/src/tools/object-writer/tools.cpp new file mode 100644 index 0000000000..138cc5e122 --- /dev/null +++ b/sgx-jvm/avian/src/tools/object-writer/tools.cpp @@ -0,0 +1,137 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include +#include +#include +#include + +#include + +using namespace avian::util; + +namespace avian { + +namespace tools { + +Buffer::Buffer() : capacity(100), length(0), data((uint8_t*)malloc(capacity)) +{ +} + +Buffer::~Buffer() +{ + free(data); +} + +void Buffer::ensure(size_t more) +{ + if (length + more > capacity) { + capacity = capacity * 2 + more; + data = (uint8_t*)realloc(data, capacity); + } +} + +void Buffer::write(const void* d, size_t size) +{ + ensure(size); + memcpy(data + length, d, size); + length += size; +} + +unsigned StringTable::add(String str) +{ + unsigned offset = Buffer::length; + Buffer::write(str.text, str.length + 1); + return offset; +} + +void OutputStream::write(uint8_t byte) +{ + writeChunk(&byte, 1); +} + +void OutputStream::writeRepeat(uint8_t byte, size_t size) +{ + for (size_t i = 0; i < size; i++) { + write(byte); + } +} + +FileOutputStream::FileOutputStream(const char* name) : file(fopen(name, "wb")) +{ +} + +FileOutputStream::~FileOutputStream() +{ + if (file) { + fclose(file); + } +} + +bool FileOutputStream::isValid() +{ + return file; +} + +void FileOutputStream::writeChunk(const void* data, size_t size) +{ + fwrite(data, size, 1, file); +} + +void FileOutputStream::write(uint8_t byte) +{ + fputc(byte, file); +} + +Platform* Platform::first = 0; + +PlatformInfo::Format PlatformInfo::formatFromString(const char* format) +{ + if (strcmp(format, "elf") == 0 || strcmp(format, "linux") == 0 + || strcmp(format, "freebsd") == 0 || strcmp(format, "qnx") == 0) { + return Elf; + } else if (strcmp(format, "pe") == 0 || strcmp(format, "windows") == 0) { + return Pe; + } else if (strcmp(format, "macho") == 0 || strcmp(format, "darwin") == 0 + || strcmp(format, "ios") == 0 || strcmp(format, "macosx") == 0) { + return MachO; + } else { + return UnknownFormat; + } +} + +PlatformInfo::Architecture PlatformInfo::archFromString(const char* arch) +{ + if (strcmp(arch, "i386") == 0) { + return Architecture::x86; + } else if (strcmp(arch, "x86_64") == 0) { + return Architecture::x86_64; + } else if (strcmp(arch, "arm") == 0) { + return Architecture::Arm; + } else if (strcmp(arch, "arm64") == 0) { + return Architecture::Arm64; + } else { + return Architecture::UnknownArch; + } +} + +Platform* Platform::getPlatform(PlatformInfo info) +{ + for (Platform* p = first; p; p = p->next) { + if (p->info == info) { + return p; + } + } + return 0; +} + +} // namespace tools + +} // namespace avian diff --git a/sgx-jvm/avian/src/tools/type-generator/CMakeLists.txt b/sgx-jvm/avian/src/tools/type-generator/CMakeLists.txt new file mode 100644 index 0000000000..53bb7cabb1 --- /dev/null +++ b/sgx-jvm/avian/src/tools/type-generator/CMakeLists.txt @@ -0,0 +1,9 @@ +add_executable(type_generator main.cpp) + +target_link_libraries(type_generator + avian_jvm_finder + avian_system + avian_util + ${ZLIB_LIBRARIES} + ${PLATFORM_LIBS} +) diff --git a/sgx-jvm/avian/src/tools/type-generator/io.h b/sgx-jvm/avian/src/tools/type-generator/io.h new file mode 100644 index 0000000000..2dcbaac4c1 --- /dev/null +++ b/sgx-jvm/avian/src/tools/type-generator/io.h @@ -0,0 +1,181 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include + +#include "assert.h" + +#ifndef AVIAN_TOOLS_TYPE_GENERATOR_IO_H +#define AVIAN_TOOLS_TYPE_GENERATOR_IO_H + +namespace avian { +namespace tools { +namespace typegenerator { + +class Input { + public: + virtual ~Input() + { + } + + virtual void dispose() = 0; + + virtual int peek() = 0; + + virtual int read() = 0; + + virtual unsigned line() = 0; + + virtual unsigned column() = 0; + + void skipSpace() + { + bool quit = false; + while (not quit) { + int c = peek(); + switch (c) { + case ' ': + case '\t': + case '\n': + read(); + break; + + default: + quit = true; + } + } + } +}; + +class FileInput : public Input { + public: + const char* file; + FILE* stream; + unsigned line_; + unsigned column_; + bool close; + + FileInput(const char* file, FILE* stream = 0, bool close = true) + : file(file), stream(stream), line_(1), column_(1), close(close) + { + } + + virtual ~FileInput() + { + dispose(); + } + + virtual void dispose() + { + if (stream and close) { + fclose(stream); + stream = 0; + } + } + + virtual int peek() + { + int c = getc(stream); + ungetc(c, stream); + return c; + } + + virtual int read() + { + int c = getc(stream); + if (c == '\n') { + ++line_; + column_ = 1; + } else { + ++column_; + } + return c; + } + + virtual unsigned line() + { + return line_; + } + + virtual unsigned column() + { + return column_; + } +}; + +class Output { + public: + virtual ~Output() + { + } + + virtual void dispose() = 0; + + virtual void write(const std::string& s) = 0; + + void write(int32_t i) + { + static const int Size = 32; + char s[Size]; + int c UNUSED = vm::snprintf(s, Size, "%d", i); + assert(c > 0 and c < Size); + write(s); + } + + void writeUnsigned(uint32_t i) + { + static const int Size = 32; + char s[Size]; + int c UNUSED = vm::snprintf(s, Size, "%u", i); + assert(c > 0 and c < Size); + write(s); + } +}; + +class FileOutput : public Output { + public: + const char* file; + FILE* stream; + bool close; + + FileOutput(const char* file, FILE* stream = 0, bool close = true) + : file(file), stream(stream), close(close) + { + } + + virtual ~FileOutput() + { + dispose(); + } + + virtual void dispose() + { + if (stream and close) { + fclose(stream); + stream = 0; + } + } + + virtual void write(const std::string& s) + { + fputs(s.c_str(), stream); + } + + const char* filename() + { + return file; + } +}; + +} // namespace typegenerator +} // namespace tools +} // namespace avian + +#endif // AVIAN_TOOLS_TYPE_GENERATOR_IO_H diff --git a/sgx-jvm/avian/src/tools/type-generator/main.cpp b/sgx-jvm/avian/src/tools/type-generator/main.cpp new file mode 100644 index 0000000000..40735927b2 --- /dev/null +++ b/sgx-jvm/avian/src/tools/type-generator/main.cpp @@ -0,0 +1,1707 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "stdlib.h" +#include "stdio.h" +#include "stdint.h" +#include "string.h" +#include "errno.h" + +#include +#include +#include +#include +#include +#include + +#include "avian/constants.h" +#include "avian/finder.h" + +#include +#include +#include + +#include "io.h" +#include "sexpr.h" + +#include "assert.h" + +using namespace avian::util; + +#define UNREACHABLE abort() + +#define UNUSED __attribute__((unused)) + +using namespace vm; +using namespace avian::tools::typegenerator; + +namespace avian { +namespace tools { +namespace typegenerator { + +class Class; + +class Field { + public: + std::string name; + size_t elementSize; + size_t offset; + uintptr_t ownerId; + bool noassert; + bool nogc; + bool polyfill; + bool threadParam; + + std::string javaSpec; + std::string typeName; + + Field(Class* ownerId, + const std::string& typeName, + const std::string& javaSpec, + const std::string& name) + : name(name), + elementSize(-1), + offset(0), + ownerId(reinterpret_cast(ownerId)), + noassert(false), + nogc(false), + polyfill(false), + threadParam(false), + javaSpec(javaSpec), + typeName(typeName) + { + } + + std::string dump() const + { + std::ostringstream ss; + ss << "field " << name << ":" << typeName << ":" << javaSpec + << ", size=" << elementSize << ", offset=" << offset; + if (noassert) { + ss << " noassert"; + } + if (nogc) { + ss << " nogc"; + } + if (polyfill) { + ss << " polyfill"; + } + return ss.str(); + } +}; + +class Method { + public: + std::string javaName; + std::string javaSpec; + + Method(const std::string& javaName, const std::string& javaSpec) + : javaName(javaName), javaSpec(javaSpec) + { + } + + bool operator==(const Method& o) const + { + return javaName == o.javaName && javaSpec == o.javaSpec; + } + + bool operator<(const Method& o) const + { + return javaName < o.javaName + || (javaName == o.javaName && javaSpec < o.javaSpec); + } + std::string dump() const + { + return "method " + javaName + javaSpec; + } +}; + +class Class { + public: + // "simple" name, used for generated code, defined in types.def + std::string name; + + // Name of the backing Java class, empty if there isn't one + std::string javaName; + + Class* super; + + std::vector fields; + std::set methods; + + Field* arrayField; + + bool overridesMethods; + + int fixedSize; + + Class(const std::string& name) + : name(name), + super(0), + arrayField(0), + overridesMethods(false), + fixedSize(-1) + { + } + + std::string dump() const + { + std::ostringstream ss; + ss << "class " << name; + if (javaName.size() > 0) { + ss << "(" << javaName << ")"; + } + if (super) { + ss << " : " << super->name << "(" << super->javaName << ")"; + } + ss << " {\n"; + + for (const auto f : fields) { + ss << " " << f->dump() << "\n"; + } + + for (const auto m : methods) { + ss << " " << m.dump() << "\n"; + } + ss << "}"; + return ss.str(); + } + + void dumpToStdout() const + { + printf("%s\n", dump().c_str()); + } +}; + +class Module { + public: + // Map from java-level name to Class + std::map javaClasses; + + std::map classes; + + void add(Class* cl) + { + assert(classes.find(cl->name) == classes.end()); + classes[cl->name] = cl; + if (cl->javaName != "") { + assert(javaClasses.find(cl->javaName) == javaClasses.end()); + javaClasses[cl->javaName] = cl; + } + } +}; +} +} +} + +namespace { + +namespace local { + +#ifndef POINTER_SIZE +#define POINTER_SIZE sizeof(void*) +#endif + +const unsigned BytesPerWord = POINTER_SIZE; + +inline bool equal(const char* a, const char* b) +{ + return strcmp(a, b) == 0; +} + +inline bool endsWith(const std::string& b, const std::string& a) +{ + if (b.size() > a.size()) { + return false; + } + return std::equal(a.begin() + a.size() - b.size(), a.end(), b.begin()); +} + +std::string enumName(Module& module, Field* f) +{ + std::string& type = f->typeName; + if (type == "void*") { + return "word"; + } else if (type == "maybe_object") { + return "uintptr_t"; + } else if (f->javaSpec.size() != 0 + && (f->javaSpec[0] == 'L' || f->javaSpec[0] == '[')) { + return "object"; + } + const auto it = module.classes.find(f->typeName); + assert(f->typeName.size() > 0); + if (it != module.classes.end()) { + return "object"; + } else { + return f->typeName; + } +} + +class Character : public Object { + public: + char value; + + static Character* make(char value) + { + Character* o = allocate(); + o->type = Object::Character; + o->value = value; + return o; + } +}; + +char character(Object* o) +{ + assert(o->type == Object::Character); + return static_cast(o)->value; +} + +class String : public Object { + public: + const char* value; + + static String* make(Object* s) + { + assert(s); + + String* o = allocate(); + o->type = Object::String; + + unsigned length = 0; + for (Object* p = s; p; p = cdr(p)) + ++length; + + char* value = static_cast(malloc(length + 1)); + assert(value); + unsigned i = 0; + for (Object* p = s; p; p = cdr(p)) + value[i++] = character(car(p)); + value[i] = 0; + + o->value = value; + return o; + } +}; + +const char* string(Object* o) +{ + assert(o->type == Object::String); + return static_cast(o)->value; +} + +class Singleton : public Object { + public: + static Singleton* make(Object::ObjectType type) + { + Singleton* o = allocate(); + o->type = type; + return o; + } +}; + +std::string capitalize(const std::string& s) +{ + if (s[0] >= 'a' && s[0] <= 'z') { + return (char)(s[0] + 'A' - 'a') + s.substr(1, s.size() - 1); + } + return s; +} + +Object* read(Input* in, Object* eos, int level) +{ + List s; + + int c; + while ((c = in->peek()) >= 0) { + switch (c) { + case '(': { + if (s.first) { + return String::make(s.first); + } else { + List list; + Object* o; + in->read(); + while ((o = read(in, eos, level + 1)) != eos) { + list.append(o); + } + return list.first; + } + } break; + + case ')': { + if (s.first) { + return String::make(s.first); + } else { + if (level == 0) { + fprintf(stderr, "unexpected ')'\n"); + abort(); + } + in->read(); + return eos; + } + } break; + + case ' ': + case '\t': + case '\n': + case '\r': { + if (s.first) { + return String::make(s.first); + } + } break; + + default: { + s.append(Character::make(c)); + } break; + } + + in->read(); + } + + if (level == 0) { + if (s.first) { + return String::make(s.first); + } else { + return eos; + } + } else { + fprintf(stderr, "unexpected end of stream\n"); + abort(); + } +} + +bool namesPointer(const std::string& s) +{ + return s == "Collector" or s == "Disposer" or endsWith("*", s); +} + +unsigned sizeOf(Module& module, const std::string& type) +{ + if (type == "object" or type == "intptr_t" or type == "uintptr_t" + or type == "maybe_object") { + return BytesPerWord; + } else if (type == "unsigned" or type == "int") { + return sizeof(int); + } else if (type == "bool") { + return sizeof(bool); + } else if (type == "int8_t" or type == "uint8_t") { + return sizeof(uint8_t); + } else if (type == "int16_t" or type == "uint16_t") { + return sizeof(uint16_t); + } else if (type == "int32_t" or type == "uint32_t") { + return sizeof(uint32_t); + } else if (type == "int64_t" or type == "uint64_t") { + return sizeof(uint64_t); + } else if (type == "char") { + return sizeof(char); + } else if (endsWith("[0]", type)) { + return 0; + } else if (namesPointer(type)) { + return BytesPerWord; + } else { + const auto it = module.classes.find(type); + if (it != module.classes.end()) { + return BytesPerWord; + } else { + fprintf(stderr, "unexpected type: %s\n", type.c_str()); + abort(); + } + } +} + +struct FieldSpec { + bool isArray; + std::string aliasName; + bool require; + Field* field; + + FieldSpec() + { + } + + FieldSpec(bool isArray, Field* field) + : isArray(isArray), require(false), field(field) + { + } +}; + +class ClassParser { + public: + Class* cl; + std::map fields; + + ClassParser(Class* cl) : cl(cl) + { + } + + void add(FieldSpec f) + { + if (f.field->polyfill) { + if (fields.find(f.field->name) == fields.end()) { + fields[f.field->name] = f.field; + cl->fields.push_back(f.field); + } else { + fields[f.field->name]->threadParam = true; + } + return; + } + if (f.aliasName.size() > 0) { + if (fields.find(f.aliasName) == fields.end()) { + if (fields.find(f.field->name) != fields.end()) { + // printf("alias %s.%s -> %s.%s\n", cl->name.c_str(), + // f.field->name.c_str(), cl->name.c_str(), f.aliasName.c_str()); + const auto it = fields.find(f.field->name); + assert(it != fields.end()); + Field* renamed = it->second; + fields.erase(it); + fields[f.aliasName] = renamed; + + renamed->name = f.aliasName; + + // TODO: this currently works around how avian uses an object (either + // a char[] or byte[]) for String.data + renamed->typeName = f.field->typeName; + renamed->javaSpec = f.field->javaSpec; + } else { + // printf("ignoring absent alias %s.%s -> %s.%s\n", cl->name.c_str(), + // f.field->name.c_str(), cl->name.c_str(), f-> aliasName.c_str()); + } + } else { + // printf("ignoring already defined alias %s.%s -> %s.%s\n", + // cl->name.c_str(), f.field->name.c_str(), cl->name.c_str(), f-> + // aliasName.c_str()); + } + } else { + if (fields.find(f.field->name) == fields.end()) { + // printf("add %s.%s\n", cl->name.c_str(), f.field->name.c_str()); + fields[f.field->name] = f.field; + if (f.isArray) { + add(FieldSpec(false, new Field(cl, "uintptr_t", "", "length"))); + assert(!cl->arrayField); + cl->arrayField = f.field; + } else { + cl->fields.push_back(f.field); + } + } else { + // printf("required check %s.%s\n", cl->name.c_str(), + // f.field->name.c_str()); + assert(f.aliasName.size() > 0 || f.require); + fields[f.field->name]->nogc |= f.field->nogc; + fields[f.field->name]->noassert |= f.field->noassert; + } + } + } + + void setSuper(Class* super) + { + assert(!cl->super); + cl->super = super; + assert(!super->arrayField); + assert(fields.size() == 0); + for (const auto f : super->fields) { + add(FieldSpec(false, f)); + } + } +}; + +FieldSpec parseArray(Module&, ClassParser& clparser, Object* p) +{ + const char* typeName = string(car(p)); + + p = cdr(p); + const char* name = string(car(p)); + + assert(!clparser.cl->arrayField); + return FieldSpec(true, new Field(clparser.cl, typeName, "", name)); +} + +FieldSpec parseVerbatimField(Module&, ClassParser& clparser, Object* p) +{ + const char* spec = string(car(p)); + const char* name = string(car(cdr(p))); + return FieldSpec(false, new Field(clparser.cl, spec, "", name)); +} + +FieldSpec parseField(Module& module, ClassParser& clparser, Object* p) +{ + FieldSpec f; + const char* spec = string(car(p)); + if (equal(spec, "field")) { + return parseVerbatimField(module, clparser, cdr(p)); + } else if (equal(spec, "array")) { + return parseArray(module, clparser, cdr(p)); + } else if (equal(spec, "noassert")) { + f = parseField(module, clparser, cdr(p)); + f.field->noassert = true; + f.require = true; + } else if (equal(spec, "nogc")) { + f = parseField(module, clparser, cdr(p)); + f.field->nogc = true; + f.require = true; + } else if (equal(spec, "require")) { + f = parseField(module, clparser, cdr(p)); + f.require = true; + } else if (equal(spec, "alias")) { + const char* name = string(car(cdr(p))); + f = parseField(module, clparser, cdr(cdr(p))); + f.aliasName = name; + } else if (equal(spec, "polyfill")) { + f = parseField(module, clparser, cdr(p)); + f.field->polyfill = true; + } else { + return parseVerbatimField(module, clparser, p); + } + return f; +} + +void parseSubdeclaration(Module& module, ClassParser& clparser, Object* p) +{ + const char* front = string(car(p)); + if (equal(front, "extends")) { + Class* super = module.classes[string(car(cdr(p)))]; + clparser.setSuper(super); + } else { + clparser.add(parseField(module, clparser, p)); + } +} + +const char* append(const char* a, const char* b, const char* c, const char* d) +{ + unsigned al = strlen(a); + unsigned bl = strlen(b); + unsigned cl = strlen(c); + unsigned dl = strlen(d); + char* p = static_cast(malloc(al + bl + cl + dl + 1)); + memcpy(p, a, al); + memcpy(p + al, b, bl); + memcpy(p + al + bl, c, cl); + memcpy(p + al + bl + cl, d, dl + 1); + return p; +} + +const char* append(const char* a, const char* b) +{ + return append(a, b, "", ""); +} + +const char* fieldType(const char* spec) +{ + switch (*spec) { + case 'B': + case 'Z': + return "uint8_t"; + case 'C': + case 'S': + return "uint16_t"; + case 'D': + case 'J': + return "uint64_t"; + case 'F': + case 'I': + return "uint32_t"; + case 'L': + case '[': + return "object"; + + default: + abort(); + } +} + +void parseJavaClass(Module& module, ClassParser& clparser, Stream* s) +{ + uint32_t magic = s->read4(); + assert(magic == 0xCAFEBABE); + (void)magic; + s->read2(); // minor version + s->read2(); // major version + + unsigned poolCount = s->read2() - 1; + std::vector pool(poolCount, -1); + for (unsigned i = 0; i < poolCount; ++i) { + unsigned c = s->read1(); + + switch (c) { + case CONSTANT_Integer: + case CONSTANT_Float: + pool[i] = s->read4(); + break; + + case CONSTANT_Long: + case CONSTANT_Double: + pool[i++] = s->read4(); + pool[i] = s->read4(); + break; + + case CONSTANT_Utf8: { + unsigned length = s->read2(); + uint8_t* p = static_cast(malloc(length + 1)); + s->read(p, length); + p[length] = 0; + pool[i] = reinterpret_cast(p); + } break; + + case CONSTANT_Class: + case CONSTANT_String: + pool[i] = s->read2(); + break; + + case CONSTANT_NameAndType: + pool[i] = s->read4(); + break; + + case CONSTANT_Fieldref: + case CONSTANT_Methodref: + case CONSTANT_InterfaceMethodref: + pool[i] = s->read4(); + break; + + default: + abort(); + } + } + + s->read2(); // flags + s->read2(); // name + + unsigned superIndex = s->read2(); + if (superIndex) { + const char* name + = reinterpret_cast(pool[pool[superIndex - 1] - 1]); + Class* super = module.javaClasses[name]; + clparser.setSuper(super); + } + + unsigned interfaceCount = s->read2(); + s->skip(interfaceCount * 2); + // for (unsigned i = 0; i < interfaceCount; ++i) { + // const char* name = reinterpret_cast + // (pool[pool[s->read2() - 1] - 1]); + // } + + unsigned fieldCount = s->read2(); + for (unsigned i = 0; i < fieldCount; ++i) { + unsigned flags = s->read2(); + unsigned nameIndex = s->read2(); + unsigned specIndex = s->read2(); + + unsigned attributeCount = s->read2(); + for (unsigned j = 0; j < attributeCount; ++j) { + s->read2(); + s->skip(s->read4()); + } + + if ((flags & ACC_STATIC) == 0) { + char* name = reinterpret_cast(pool[nameIndex - 1]); + unsigned nameLength = strlen(name); + if (nameLength > 0 and name[nameLength - 1] == '_') { + name[nameLength - 1] = 0; + } + + const char* spec = reinterpret_cast(pool[specIndex - 1]); + const char* memberType = fieldType(spec); + + clparser.add( + FieldSpec(false, new Field(clparser.cl, memberType, spec, name))); + } + } + + if (clparser.cl->super) { + clparser.cl->methods.insert(clparser.cl->super->methods.begin(), + clparser.cl->super->methods.end()); + } + + unsigned methodCount = s->read2(); + for (unsigned i = 0; i < methodCount; ++i) { + unsigned flags = s->read2(); + unsigned nameIndex = s->read2(); + unsigned specIndex = s->read2(); + + unsigned attributeCount = s->read2(); + for (unsigned j = 0; j < attributeCount; ++j) { + s->read2(); + s->skip(s->read4()); + } + + const char* name = reinterpret_cast(pool[nameIndex - 1]); + const char* spec = reinterpret_cast(pool[specIndex - 1]); + + if ((flags & (ACC_STATIC | ACC_PRIVATE)) == 0 and *name != '<') { + clparser.cl->methods.insert(Method(name, spec)); + clparser.cl->overridesMethods = true; + } + } +} + +void parseType(Finder* finder, Module& module, Object* p) +{ + const char* name = string(car(p)); + + Class* cl = new Class(name); + + ClassParser clparser(cl); + + const char* javaName = 0; + if (cdr(p) and car(cdr(p))->type == Object::String) { + p = cdr(p); + javaName = string(car(p)); + cl->javaName = javaName; + } + + bool isJavaType = javaName and *javaName != '['; + + if (isJavaType) { + class Client : public Stream::Client { + public: + virtual void NO_RETURN handleError() + { + abort(); + } + } client; + System::Region* region = finder->find(append(javaName, ".class")); + if (region == 0) { + return; + } + Stream s(&client, region->start(), region->length()); + parseJavaClass(module, clparser, &s); + region->dispose(); + } + + module.add(cl); + + for (p = cdr(p); p; p = cdr(p)) { + parseSubdeclaration(module, clparser, car(p)); + } + + if (not isJavaType) { + if (cl->super) { + cl->methods.insert(cl->super->methods.begin(), cl->super->methods.end()); + } + } +} + +void parseDeclaration(Finder* finder, Module& module, Object* p) +{ + const char* spec = string(car(p)); + if (equal(spec, "type")) { + parseType(finder, module, cdr(p)); + } else { + fprintf(stderr, "unexpected declaration spec: %s\n", spec); + abort(); + } +} + +void parse(Finder* finder, Input* in, Module& module) +{ + Object* eos = Singleton::make(Object::Eos); + List declarations; + + Object* o; + while ((o = read(in, eos, 0)) != eos) { + parseDeclaration(finder, module, o); + } +} + +void layoutClass(Module& module, Class* cl) +{ + if (cl->fixedSize >= 0) { + return; + } + + unsigned offset = BytesPerWord; + + unsigned size = 0; + unsigned alignment = BytesPerWord; + + alignment = BytesPerWord; + + for (const auto f : cl->fields) { + f->elementSize = sizeOf(module, f->typeName); + + if (!f->polyfill) { // polyfills contribute no size + alignment = f->elementSize; + offset = (offset + alignment - 1) & ~(alignment - 1); + f->offset = offset; + + size = f->elementSize; + + offset += size; + } + } + if (cl->arrayField) { + Field* f = cl->arrayField; + + f->elementSize = sizeOf(module, f->typeName); + + alignment = f->elementSize; + offset = (offset + alignment - 1) & ~(alignment - 1); + f->offset = offset; + } + // offset = (offset + BytesPerWord - 1) & ~(BytesPerWord - 1); + cl->fixedSize = offset; +} + +void layoutClasses(Module& module) +{ + for (const auto p : module.classes) { + Class* cl = p.second; + layoutClass(module, cl); + } +} + +void writeOffset(Output* out, size_t offset) +{ + out->write(offset); +} + +void writeOffset(Output* out, Class* cl) +{ + out->write(cl->fixedSize); + if (cl->arrayField) { + out->write(" + pad(length * "); + out->write(cl->arrayField->elementSize); + out->write(")"); + } +} + +std::string cppClassName(Class* cl) +{ + if (cl->name == "jobject") { + return "object"; + } else { + return "Gc" + capitalize(cl->name) + "*"; + } +} + +std::string cppFieldType(Module& module, Field* f) +{ + if (f->javaSpec.size() != 0) { + if (f->javaSpec[0] == 'L') { + std::string className = f->javaSpec.substr(1, f->javaSpec.size() - 2); + const auto it = module.javaClasses.find(className); + if (it != module.javaClasses.end()) { + return cppClassName(it->second); + } + } else if (f->javaSpec[0] == '[') { + const auto it = module.javaClasses.find(f->javaSpec); + if (it != module.javaClasses.end()) { + return cppClassName(it->second); + } + } + } + const auto it = module.classes.find(f->typeName); + assert(f->typeName.size() > 0); + if (it != module.classes.end()) { + return cppClassName(it->second); + } else if (f->typeName == "maybe_object") { + return "uintptr_t"; + } else { + return f->typeName; + } +} + +void writeAccessor(Output* out, Class* cl, Field* f) +{ + out->write("const unsigned "); + out->write(capitalize(cl->name)); + out->write(capitalize(f->name)); + out->write(" = "); + writeOffset(out, f->offset); + out->write(";\n\n"); + + out->write("#define HAVE_"); + out->write(capitalize(cl->name)); + out->write(capitalize(f->name)); + out->write(" 1\n"); + + if (! f->javaSpec.empty()) { + std::string s = f->javaSpec; + std::replace(s.begin(), s.end(), '/', '_'); + std::replace(s.begin(), s.end(), '$', '_'); + std::replace(s.begin(), s.end(), ';', '_'); + std::replace(s.begin(), s.end(), '[', '_'); + + out->write("#define HAVE_"); + out->write(capitalize(cl->name)); + out->write(capitalize(f->name)); + out->write("_"); + out->write(s); + out->write(" 1\n\n"); + } +} + +void writeAccessors(Output* out, Module& module) +{ + for (const auto p : module.classes) { + Class* cl = p.second; + for (const auto f : cl->fields) { + if (!f->polyfill) { + writeAccessor(out, cl, f); + } + } + if (cl->arrayField) { + writeAccessor(out, cl, cl->arrayField); + } + } +} + +void writeSizes(Output* out, Module& module) +{ + for (const auto p : module.classes) { + Class* cl = p.second; + + out->write("const unsigned FixedSizeOf"); + out->write(capitalize(cl->name)); + out->write(" = "); + out->write(cl->fixedSize); + out->write(";\n\n"); + + if (cl->arrayField) { + out->write("const unsigned ArrayElementSizeOf"); + out->write(capitalize(cl->name)); + out->write(" = "); + out->write(cl->arrayField->elementSize); + out->write(";\n\n"); + } + } +} + +std::string obfuscate(const std::string& s) +{ + if (s == "default" || s == "template" || s == "class" || s == "register" + || s == "this") { + return s + "_"; + } else { + return s; + } +} + +void writeConstructorParameters(Output* out, Module& module, Class* cl) +{ + for (const auto f : cl->fields) { + if (!f->polyfill) { + out->write(", "); + out->write(cppFieldType(module, f)); + out->write(" "); + out->write(obfuscate(f->name)); + } + } +} + +void writeConstructorArguments(Output* out, Class* cl) +{ + for (const auto f : cl->fields) { + if (!f->polyfill) { + out->write(", "); + out->write(obfuscate(f->name)); + } + } +} + +void writeConstructorInitializations(Output* out, Class* cl) +{ + for (const auto f : cl->fields) { + if (!f->polyfill) { + out->write(" o->set"); + out->write(capitalize(f->name)); + out->write("(t, "); + out->write(obfuscate(f->name)); + out->write(");\n"); + } + } +} + +void writeClassDeclarations(Output* out, Module& module) +{ + for (const auto p : module.classes) { + Class* cl = p.second; + + out->write("class Gc"); + out->write(capitalize(cl->name)); + out->write(";\n"); + } + out->write("\n"); +} + +bool isFieldGcVisible(Module& module, Field* f) +{ + return enumName(module, f) == "object" && !f->nogc; +} + +bool isFieldGcMarkable(Module& module, Field* f) +{ + return (f->typeName == "maybe_object" || enumName(module, f) == "object") + && !f->nogc; +} + +void writeClassAccessors(Output* out, Module& module, Class* cl) +{ + for (const auto f : cl->fields) { + if (!f->polyfill) { + out->write(" void set"); + out->write(capitalize(f->name)); + out->write("(Thread* t UNUSED, "); + out->write(cppFieldType(module, f)); + out->write(" value) { "); + if (isFieldGcMarkable(module, f)) { + out->write("setField(t, this , "); + out->write(capitalize(cl->name)); + out->write(capitalize(f->name)); + out->write(", reinterpret_cast(value));"); + } else { + out->write("field_at<"); + out->write(cppFieldType(module, f)); + out->write(">("); + out->write(capitalize(cl->name)); + out->write(capitalize(f->name)); + out->write(") = value;"); + } + out->write(" }\n"); + + out->write(" "); + out->write(cppFieldType(module, f)); + out->write("* "); + out->write(obfuscate(f->name)); + out->write("Ptr() { return &field_at<"); + out->write(cppFieldType(module, f)); + out->write(">("); + out->write(capitalize(cl->name)); + out->write(capitalize(f->name)); + out->write("); }\n"); + } + + out->write(" "); + out->write(cppFieldType(module, f)); + if (!f->polyfill && !isFieldGcMarkable(module, f)) { + out->write("&"); + } + out->write(" "); + out->write(obfuscate(f->name)); + if (f->threadParam || f->polyfill) { + out->write("(Thread*"); + } else { + out->write("("); + } + if (f->polyfill) { + out->write("); // polyfill, assumed to be implemented elsewhere\n"); + } else { + out->write(") { return field_at<"); + out->write(cppFieldType(module, f)); + out->write(">("); + out->write(capitalize(cl->name)); + out->write(capitalize(f->name)); + out->write("); }\n"); + } + } + if (cl->arrayField) { + Field* f = cl->arrayField; + out->write(" avian::util::Slice<"); + if (isFieldGcVisible(module, f)) { + out->write("const "); + } + out->write(cppFieldType(module, f)); + out->write("> "); + out->write(obfuscate(f->name)); + out->write("() { return avian::util::Slice<"); + if (isFieldGcVisible(module, f)) { + out->write("const "); + } + out->write(cppFieldType(module, f)); + out->write("> (&field_at<"); + if (isFieldGcVisible(module, f)) { + out->write("const "); + } + out->write(cppFieldType(module, f)); + out->write(">("); + out->write(capitalize(cl->name)); + out->write(capitalize(f->name)); + out->write("), field_at("); + out->write(capitalize(cl->name)); + out->write("Length)); }\n"); + + out->write(" void set"); + out->write(capitalize(f->name)); + out->write("Element(Thread* t UNUSED, size_t index, "); + out->write(cppFieldType(module, f)); + out->write(" value) { "); + if (isFieldGcMarkable(module, f)) { + out->write("setField(t, this , "); + out->write(capitalize(cl->name)); + out->write(capitalize(f->name)); + out->write(" + index * ("); + out->write(sizeOf(module, f->typeName)); + out->write("), reinterpret_cast(value));"); + } else { + out->write("field_at<"); + out->write(cppFieldType(module, f)); + out->write(">("); + out->write(capitalize(cl->name)); + out->write(capitalize(f->name)); + out->write(" + index * ("); + out->write(sizeOf(module, f->typeName)); + out->write(")) = value;"); + } + out->write(" }\n"); + } +} + +void writeClasses(Output* out, Module& module) +{ + for (const auto p : module.classes) { + Class* cl = p.second; + + out->write("class Gc"); + out->write(capitalize(cl->name)); + out->write(": public GcObject {\n"); + out->write(" public:\n"); + out->write(" static const Gc::Type Type = Gc::"); + out->write(capitalize(cl->name)); + out->write("Type;\n"); + out->write(" static const size_t FixedSize = FixedSizeOf"); + out->write(capitalize(cl->name)); + out->write(";\n\n"); + + out->write(" static Gc" + capitalize(cl->name) + "* makeZeroed(Thread* t"); + if (cl->arrayField) { + out->write(", uintptr_t length"); + } + out->write(");\n"); + + writeClassAccessors(out, module, cl); + + out->write("};\n\n"); + } +} + +void writeInitializerDeclarations(Output* out, Module& module) +{ + for (const auto p : module.classes) { + Class* cl = p.second; + out->write("void init"); + out->write(capitalize(cl->name)); + out->write("(Thread* t, Gc"); + out->write(capitalize(cl->name)); + out->write("* o"); + + writeConstructorParameters(out, module, cl); + + out->write(");\n\n"); + } +} + +void writeConstructorDeclarations(Output* out, Module& module) +{ + for (const auto p : module.classes) { + Class* cl = p.second; + out->write("Gc"); + out->write(capitalize(cl->name)); + out->write("* make"); + out->write(capitalize(cl->name)); + out->write("(Thread* t"); + + writeConstructorParameters(out, module, cl); + + out->write(");\n\n"); + } +} + +void writeInitializers(Output* out, Module& module) +{ + for (const auto p : module.classes) { + Class* cl = p.second; + out->write("void init"); + out->write(capitalize(cl->name)); + out->write("(Thread* t, Gc"); + out->write(capitalize(cl->name)); + out->write("* o"); + + writeConstructorParameters(out, module, cl); + + out->write(")\n{\n"); + + out->write(" setObjectClass(t, reinterpret_cast(o), "); + out->write( + "reinterpret_cast(reinterpret_cast(t->m->types)->" + "body()[Gc::"); + out->write(capitalize(cl->name)); + out->write("Type]));\n"); + + writeConstructorInitializations(out, cl); + + out->write("}\n\n"); + } +} + +void writeConstructors(Output* out, Module& module) +{ + for (const auto p : module.classes) { + Class* cl = p.second; + + bool hasObjectMask = cl->name == "singleton"; + + std::string name = "Gc" + capitalize(cl->name); + + out->write(name + "* " + name + "::makeZeroed(Thread* t"); + if (cl->arrayField) { + out->write(", uintptr_t length"); + } + out->write(")\n{\n"); + out->write(" " + name + "* o = reinterpret_cast<" + name + + "*>(allocate(t, "); + writeOffset(out, cl); + if (hasObjectMask) { + out->write(", true"); + } else { + out->write(", false"); + } + out->write("));\n"); + out->write(" setObjectClass(t, reinterpret_cast(o), "); + out->write( + "reinterpret_cast(reinterpret_cast(t->m->types)->" + "body()[Gc::"); + out->write(capitalize(cl->name)); + out->write("Type]));\n"); + out->write(" return o;\n"); + out->write("}\n\n"); + + out->write(name + "* make" + capitalize(cl->name)); + out->write("(Thread* t"); + + writeConstructorParameters(out, module, cl); + + out->write(")\n{\n"); + for (const auto f : cl->fields) { + if (enumName(module, f) == "object" and not f->nogc) { + out->write(" PROTECT(t, "); + out->write(obfuscate(f->name)); + out->write(");\n"); + + hasObjectMask = true; + } + } + if (cl->arrayField) { + Field* f = cl->arrayField; + if (f->typeName == "object" and not f->nogc) { + hasObjectMask = true; + } + } + + out->write(" " + name + "* o = reinterpret_cast<" + name + + "*>(allocate(t, "); + writeOffset(out, cl); + if (hasObjectMask) { + out->write(", true"); + } else { + out->write(", false"); + } + out->write("));\n"); + + out->write(" init"); + out->write(capitalize(cl->name)); + out->write("(t, o"); + writeConstructorArguments(out, cl); + out->write(");\n"); + + out->write(" return o;\n}\n\n"); + } +} + +void writeEnums(Output* out, Module& module) +{ + bool wrote = false; + for (const auto p : module.classes) { + Class* cl = p.second; + if (wrote) { + out->write(",\n"); + } else { + wrote = true; + } + out->write(capitalize(cl->name)); + out->write("Type"); + } + + if (wrote) { + out->write("\n"); + } +} + +void set(uint32_t* mask, unsigned index) +{ + if (index < 32) { + *mask |= 1 << index; + } else { + UNREACHABLE; + } +} + +void set(std::vector& mask, unsigned index) +{ + set(&(mask[index / 32]), index % 32); +} + +std::vector typeObjectMask(Module& module, Class* cl) +{ + std::vector mask(ceilingDivide( + cl->fixedSize + (cl->arrayField ? cl->arrayField->elementSize : 0), + 32 * BytesPerWord)); + + set(mask, 0); + + for (const auto f : cl->fields) { + unsigned offset = f->offset / BytesPerWord; + if (isFieldGcVisible(module, f)) { + set(mask, offset); + } + } + + if (cl->arrayField) { + Field* f = cl->arrayField; + unsigned offset = f->offset / BytesPerWord; + if (isFieldGcVisible(module, f)) { + set(mask, offset); + } + } + + return mask; +} + +bool trivialMask(const std::vector& mask) +{ + if (mask[0] != 1) { + return false; + } + + for (size_t i = 1; i < mask.size(); ++i) { + if (mask[i]) { + return false; + } + } + + return true; +} + +void writeInitialization(Output* out, + Module& module, + std::set& alreadyInited, + Class* cl) +{ + if (alreadyInited.find(cl) != alreadyInited.end()) { + return; + } + alreadyInited.insert(cl); + if (cl->super && cl->name != "intArray" && cl->name != "class") { + writeInitialization(out, module, alreadyInited, cl->super); + } + + std::vector mask = typeObjectMask(module, cl); + bool trivialMask = local::trivialMask(mask); + if (trivialMask) { + out->write("{ "); + } else { + out->write("{ uint32_t mask["); + out->write(mask.size()); + out->write("] = { "); + for (size_t i = 0; i < mask.size(); ++i) { + out->writeUnsigned(mask[i]); + if (i < mask.size() - 1) { + out->write(", "); + } + } + out->write(" };\n"); + } + + out->write("bootClass(t, Gc::"); + out->write(capitalize(cl->name)); + out->write("Type, "); + + if (cl->super) { + out->write("Gc::"); + out->write(capitalize(cl->super->name)); + out->write("Type"); + } else { + out->write("-1"); + } + out->write(", "); + + if (trivialMask) { + out->write("0"); + } else { + out->write("mask"); + } + out->write(", "); + + out->write(cl->fixedSize); + out->write(", "); + + out->write(cl->arrayField ? cl->arrayField->elementSize : 0); + out->write(", "); + + out->write(cl->methods.size()); + out->write("); }\n"); +} + +void writeInitializations(Output* out, Module& module) +{ + std::set alreadyInited; + + writeInitialization(out, module, alreadyInited, module.classes["intArray"]); + writeInitialization(out, module, alreadyInited, module.classes["class"]); + + for (const auto p : module.classes) { + Class* cl = p.second; + if (cl->name != "intArray" && cl->name != "class") { + writeInitialization(out, module, alreadyInited, cl); + } + } +} + +void writeJavaInitialization(Output* out, + Class* cl, + std::set& alreadyInited) +{ + if (alreadyInited.find(cl) != alreadyInited.end()) { + return; + } + + alreadyInited.insert(cl); + + if (cl->super) { + writeJavaInitialization(out, cl->super, alreadyInited); + } + + out->write("bootJavaClass(t, Gc::"); + out->write(capitalize(cl->name)); + out->write("Type, "); + + if (cl->super) { + out->write("Gc::"); + out->write(capitalize(cl->super->name)); + out->write("Type"); + } else { + out->write("-1"); + } + out->write(", \""); + + out->write(cl->javaName); + out->write("\", "); + + if (cl->overridesMethods) { + out->write(cl->methods.size()); + } else { + out->write("-1"); + } + out->write(", bootMethod);\n"); +} + +void writeJavaInitializations(Output* out, Module& module) +{ + std::set alreadyInited; + for (const auto p : module.classes) { + Class* cl = p.second; + if (cl->javaName.size()) { + writeJavaInitialization(out, cl, alreadyInited); + } + } +} + +void writeNameInitialization(Output* out, Class* cl) +{ + out->write("nameClass(t, Gc::"); + out->write(capitalize(cl->name)); + out->write("Type, \""); + if (cl->name == "jbyte" or cl->name == "jboolean" or cl->name == "jshort" + or cl->name == "jchar" or cl->name == "jint" or cl->name == "jlong" + or cl->name == "jfloat" or cl->name == "jdouble" or cl->name == "jvoid") { + out->write(cl->name.substr(1, cl->name.size() - 1)); + } else { + out->write("vm::"); + out->write(cl->name); + } + out->write("\");\n"); +} + +void writeNameInitializations(Output* out, Module& module) +{ + for (const auto p : module.classes) { + Class* cl = p.second; + if (!cl->javaName.size()) { + writeNameInitialization(out, cl); + } + } +} + +void writeMap(Output* out, Module& module, Class* cl) +{ + std::ostringstream ss; + for (const auto f : cl->fields) { + ss << "Type_"; + ss << enumName(module, f); + if (f->nogc) { + ss << "_nogc"; + } + + ss << ", "; + } + + if (cl->arrayField) { + Field* f = cl->arrayField; + ss << "Type_array, "; + ss << "Type_"; + ss << enumName(module, f); + ss << ", "; + } + + ss << "Type_none"; + + out->write(ss.str()); +} + +void writeMaps(Output* out, Module& module) +{ + out->write("Type types[]["); + out->write(module.classes.size()); + out->write("] = {\n"); + bool wrote = false; + for (const auto p : module.classes) { + Class* cl = p.second; + if (wrote) { + out->write(",\n"); + } else { + wrote = true; + } + + out->write("// "); + out->write(cl->name); + out->write("\n{ "); + writeMap(out, module, cl); + out->write(" }"); + } + out->write("\n};"); +} + +} // namespace local + +} // namespace + +extern "C" uint64_t vmNativeCall(void*, void*, unsigned, unsigned) +{ + abort(); +} + +extern "C" void vmJump(void*, void*, void*, void*, uintptr_t, uintptr_t) +{ + abort(); +} + +int main(int ac, char** av) +{ + ArgParser parser; + Arg classpath(parser, true, "cp", ""); + Arg input(parser, true, "i", ""); + Arg output(parser, true, "o", ""); + Arg outputType(parser, + true, + "t", + ""); + + if (!parser.parse(ac, av)) { + parser.printUsage(av[0]); + exit(1); + } + + if (!(local::equal(outputType.value, "enums") + || local::equal(outputType.value, "declarations") + || local::equal(outputType.value, "constructors") + || local::equal(outputType.value, "initializations") + || local::equal(outputType.value, "java-initializations") + || local::equal(outputType.value, "name-initializations") + || local::equal(outputType.value, "maps"))) { + parser.printUsage(av[0]); + exit(1); + } + + System* system = makeSystem(); + + class MyAllocator : public avian::util::Alloc { + public: + MyAllocator(System* s) : s(s) + { + } + + virtual void* allocate(size_t size) + { + void* p = s->tryAllocate(size); + if (p == 0) { + abort(s); + } + return p; + } + + virtual void free(const void* p, size_t) + { + s->free(p); + } + + System* s; + } allocator(system); + + Finder* finder = makeFinder(system, &allocator, classpath.value, 0); + + FILE* inStream = ::fopen(input.value, "rb"); + if (inStream == 0) { + fprintf(stderr, "unable to open %s: %s\n", input.value, strerror(errno)); + exit(1); + } + + FileInput in(0, inStream, false); + + Module module; + local::parse(finder, &in, module); + local::layoutClasses(module); + + finder->dispose(); + system->dispose(); + + FILE* outStream = ::fopen(output.value, "wb"); + if (outStream == 0) { + fprintf(stderr, "unable to open %s: %s\n", output.value, strerror(errno)); + exit(1); + } + FileOutput out(0, outStream, false); + + if (local::equal(outputType.value, "enums")) { + local::writeEnums(&out, module); + } else if (local::equal(outputType.value, "declarations")) { + out.write("const unsigned TypeCount = "); + out.Output::write(module.classes.size()); + out.write(";\n\n"); + + local::writeClassDeclarations(&out, module); + local::writeAccessors(&out, module); + local::writeSizes(&out, module); + local::writeClasses(&out, module); + local::writeInitializerDeclarations(&out, module); + local::writeConstructorDeclarations(&out, module); + } else if (local::equal(outputType.value, "constructors")) { + local::writeInitializers(&out, module); + local::writeConstructors(&out, module); + } else if (local::equal(outputType.value, "initializations")) { + local::writeInitializations(&out, module); + } else if (local::equal(outputType.value, "java-initializations")) { + local::writeJavaInitializations(&out, module); + } else if (local::equal(outputType.value, "name-initializations")) { + local::writeNameInitializations(&out, module); + } else if (local::equal(outputType.value, "maps")) { + local::writeMaps(&out, module); + } + + out.write("\n"); +} diff --git a/sgx-jvm/avian/src/tools/type-generator/sexpr.h b/sgx-jvm/avian/src/tools/type-generator/sexpr.h new file mode 100644 index 0000000000..07059d74ec --- /dev/null +++ b/sgx-jvm/avian/src/tools/type-generator/sexpr.h @@ -0,0 +1,111 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef AVIAN_TOOLS_TYPE_GENERATOR_SEXPR_H +#define AVIAN_TOOLS_TYPE_GENERATOR_SEXPR_H + +namespace avian { +namespace tools { +namespace typegenerator { + +template +inline T* allocate() +{ + T* t = static_cast(malloc(sizeof(T))); + assert(t); + return t; +} + +class Object { + public: + typedef enum { + Scalar, + Array, + Method, + Type, + Pair, + Character, + String, + Eos + } ObjectType; + + ObjectType type; +}; + +class Pair : public Object { + public: + Object* car; + Object* cdr; + + static Pair* make(Object* car, Object* cdr) + { + Pair* o = allocate(); + o->type = Object::Pair; + o->car = car; + o->cdr = cdr; + return o; + } +}; + +inline Object* cons(Object* car, Object* cdr) +{ + return Pair::make(car, cdr); +} + +inline Object*& car(Object* o) +{ + assert(o->type == Object::Pair); + return static_cast(o)->car; +} + +inline void setCar(Object* o, Object* v) +{ + assert(o->type == Object::Pair); + static_cast(o)->car = v; +} + +inline Object*& cdr(Object* o) +{ + assert(o->type == Object::Pair); + return static_cast(o)->cdr; +} + +inline void setCdr(Object* o, Object* v) +{ + assert(o->type == Object::Pair); + static_cast(o)->cdr = v; +} + +class List { + public: + Object* first; + Object* last; + + List() : first(0), last(0) + { + } + + void append(Object* o) + { + Object* p = cons(o, 0); + if (last) { + setCdr(last, p); + last = p; + } else { + first = last = p; + } + } +}; + +} // namespace typegenerator +} // namespace tools +} // namespace avian + +#endif // AVIAN_TOOLS_TYPE_GENERATOR_SEXPR_H diff --git a/sgx-jvm/avian/src/types.def b/sgx-jvm/avian/src/types.def new file mode 100644 index 0000000000..da247c9d26 --- /dev/null +++ b/sgx-jvm/avian/src/types.def @@ -0,0 +1,432 @@ +(type jobject java/lang/Object) + +(type class avian/VMClass + (array void* vtable)) + +(type jclass java/lang/Class + (require class vmClass)) + +(type jaccessibleObject java/lang/reflect/AccessibleObject) + +(type jexecutable java/lang/reflect/Executable) + +(type jfield java/lang/reflect/Field) + +(type jmethod java/lang/reflect/Method) + +(type jconstructor java/lang/reflect/Constructor) + +(type constantPool sun/reflect/ConstantPool) + +(type serializable java/io/Serializable) + +(type cloneable java/lang/Cloneable) + +(type callSite java/lang/invoke/CallSite + (require invocation invocation)) + +(type methodHandle java/lang/invoke/MethodHandle + (alias method method vmtarget)) + +(type methodType java/lang/invoke/MethodType) + +(type lookup java/lang/invoke/MethodHandles$Lookup) + +(type singleton avian/Singleton + (array maybe_object body)) + +(type classLoader java/lang/ClassLoader + (object map)) + +(type systemClassLoader avian/SystemClassLoader + (void* finder)) + +(type field avian/VMField) + +(type method avian/VMMethod) + +(type addendum avian/Addendum) + +(type classAddendum avian/ClassAddendum) + +(type methodAddendum avian/MethodAddendum) + +(type fieldAddendum avian/FieldAddendum) + +(type classRuntimeData + (object arrayClass) + (object jclass) + (object pool) + (object signers)) + +(type native + (void* function) + (uint8_t fast)) + +(type methodRuntimeData + (native native)) + +(type pointer + (void* value)) + +(type nativeIntercept + (extends native) + (object original)) + +(type region + (void* region) + (uint32_t position)) + +(type exceptionHandlerTable + (array uint64_t body)) + +(type lineNumberTable + (array uint64_t body)) + +(type invocation + (uint16_t bootstrap) + (int32_t index) + (class class) + (singleton pool) + (method template) + (callSite site)) + +(type triple + (object first) + (object second) + (object third)) + +(type finalizer + (nogc object target) + (void* finalize) + (nogc object next) + (object queueTarget) + (finalizer queueNext)) + +(type list + (uint32_t size) + (object front) + (object rear)) + +(type vector + (uint32_t size) + (array object body)) + +(type traceElement + (object method) + (int32_t ip)) + +(type treeNode + (object value) + (treeNode left) + (treeNode right)) + +(type callNode + (intptr_t address) + (method target) + (uintptr_t flags) + (callNode next)) + +(type wordArray + (array uintptr_t body)) + +(type array + (noassert array object body)) + +(type hashMap + (uint32_t size) + (field array array)) + +(type weakHashMap + (extends hashMap)) + +(type pair avian/Pair + (object first) + (object second)) + +(type monitor + (void* owner) + (void* waitHead) + (void* waitTail) + (object acquireHead) + (object acquireTail) + (uint32_t depth)) + +(type monitorNode + (void* value) + (object next)) + +(type innerClassReference avian/InnerClassReference) + +(type continuationContext + (continuationContext next) + (object before) + (object after) + (object continuation) + (method method)) + +(type continuation avian/Continuations$Continuation + (continuation next) + (continuationContext context) + (method method) + (void* address) + (uintptr_t returnAddressOffset) + (uintptr_t framePointerOffset) + (array uintptr_t body)) + +(type unwindResult avian/Continuations$UnwindResult) + +(type string java/lang/String + (alias data object value) + (alias length uint32_t count) + (alias hashCode uint32_t hash) + (polyfill uint32_t offset) + (polyfill uint32_t length)) + +(type thread java/lang/Thread + (require object sleepLock) + (require object interruptLock) + (require uint8_t interrupted) + (require uint8_t unparked) + (alias peer uint64_t eetop) + (alias peer uint64_t nativePeer) + (require uint64_t peer)) + +(type threadGroup java/lang/ThreadGroup) + +(type stackTraceElement java/lang/StackTraceElement) + +(type throwable java/lang/Throwable + (alias message string detailMessage) + (alias trace object backtrace) + (alias trace object stackState)) + +(type exception java/lang/Exception) + +(type runtimeException java/lang/RuntimeException) + +(type nullPointerException java/lang/NullPointerException) + +(type arithmeticException java/lang/ArithmeticException) + +(type illegalStateException java/lang/IllegalStateException) + +(type illegalArgumentException java/lang/IllegalArgumentException) + +(type illegalMonitorStateException java/lang/IllegalMonitorStateException) + +(type indexOutOfBoundsException java/lang/IndexOutOfBoundsException) + +(type arrayIndexOutOfBoundsException java/lang/ArrayIndexOutOfBoundsException) + +(type arrayStoreException java/lang/ArrayStoreException) + +(type negativeArraySizeException java/lang/NegativeArraySizeException) + +(type cloneNotSupportedException java/lang/CloneNotSupportedException) + +(type reflectiveOperationException java/lang/ReflectiveOperationException) + +(type classCastException java/lang/ClassCastException) + +(type classNotFoundException java/lang/ClassNotFoundException) + +(type invocationTargetException java/lang/reflect/InvocationTargetException) + +(type interruptedException java/lang/InterruptedException) + +(type error java/lang/Error) + +(type virtualMachineError java/lang/VirtualMachineError) + +(type outOfMemoryError java/lang/OutOfMemoryError) + +(type stackOverflowError java/lang/StackOverflowError) + +(type linkageError java/lang/LinkageError) + +(type incompatibleClassChangeError java/lang/IncompatibleClassChangeError) + +(type abstractMethodError java/lang/AbstractMethodError) + +(type noSuchFieldError java/lang/NoSuchFieldError) + +(type noSuchMethodError java/lang/NoSuchMethodError) + +(type noClassDefFoundError java/lang/NoClassDefFoundError) + +(type unsatisfiedLinkError java/lang/UnsatisfiedLinkError) + +(type exceptionInInitializerError java/lang/ExceptionInInitializerError) + +(type ioException java/io/IOException) + +(type fileNotFoundException java/io/FileNotFoundException) + +(type incompatibleContinuationException + avian/IncompatibleContinuationException) + +(type number java/lang/Number) + +(type byte java/lang/Byte) + +(type boolean java/lang/Boolean) + +(type short java/lang/Short) + +(type char java/lang/Character) + +(type int java/lang/Integer) + +(type long java/lang/Long) + +(type float java/lang/Float) + +(type double java/lang/Double) + +(type referenceQueue java/lang/ref/ReferenceQueue + (alias front jreference head) + (require object jnext)) + +(type jreference java/lang/ref/Reference + (alias target object referent) + (alias queue referenceQueue queue) + (alias jNext jreference next) + (alias jNext jreference queueNext) + (alias vmNext object discovered) + (alias vmNext object pendingNext) + (nogc object target) + (nogc object queue) + (nogc object vmNext)) + +(type weakReference java/lang/ref/WeakReference) + +(type softReference java/lang/ref/SoftReference) + +(type phantomReference java/lang/ref/PhantomReference) + +(type cleaner sun/misc/Cleaner + (cleaner queueNext)) + +(type byteArray [B + (extends jobject) + (array int8_t body)) + +(type reference + (uint8_t kind) + (byteArray class) + (byteArray name) + (byteArray spec)) + +(type finder + (void* finder) + (byteArray name) + (finder next)) + +(type booleanArray [Z + (extends jobject) + (array uint8_t body)) + +(type shortArray [S + (extends jobject) + (array int16_t body)) + +(type charArray [C + (extends jobject) + (array uint16_t body)) + +(type intArray [I + (extends jobject) + (array int32_t body)) + +(type code avian/Code + (singleton pool) + (intArray stackMap) + (object exceptionHandlerTable) + (lineNumberTable lineNumberTable) + (intptr_t compiled) + (uint32_t compiledSize) + (uint16_t maxStack) + (uint16_t maxLocals) + (array uint8_t body)) + +(type longArray [J + (extends jobject) + (array int64_t body)) + +(type floatArray [F + (extends jobject) + (array uint32_t body)) + +(type doubleArray [D + (extends jobject) + (array uint64_t body)) + +(type jbyte + (extends jobject)) + +(type jboolean + (extends jobject)) + +(type jshort + (extends jobject)) + +(type jchar + (extends jobject)) + +(type jint + (extends jobject)) + +(type jlong + (extends jobject)) + +(type jfloat + (extends jobject)) + +(type jdouble + (extends jobject)) + +(type jvoid + (extends jobject)) + +(type roots + (classLoader bootLoader) + (classLoader appLoader) + (hashMap bootstrapClassMap) + (hashMap packageMap) + (method findLoadedClassMethod) + (method loadClassMethod) + (hashMap monitorMap) + (hashMap stringMap) + (hashMap byteArrayMap) + (hashMap poolMap) + (vector classRuntimeDataTable) + (vector methodRuntimeDataTable) + (vector jNIMethodTable) + (vector jNIFieldTable) + (pair shutdownHooks) + (thread finalizerThread) + (finalizer objectsToFinalize) + (cleaner objectsToClean) + (throwable nullPointerException) + (throwable arithmeticException) + (throwable arrayIndexOutOfBoundsException) + (throwable outOfMemoryError) + (throwable shutdownInProgress) + (finder virtualFileFinders) + (field array virtualFiles) + (field array arrayInterfaceTable) + (object threadTerminated) + (field array invocations)) + +(type compileRoots + (field array callTable) + (treeNode methodTree) + (treeNode methodTreeSentinal) + (object objectPools) + (object staticTableArray) + (wordArray virtualThunks) + (wordArray dynamicThunks) + (method receiveMethod) + (method windMethod) + (method rewindMethod)) diff --git a/sgx-jvm/avian/src/util.cpp b/sgx-jvm/avian/src/util.cpp new file mode 100644 index 0000000000..9b2e31d3ff --- /dev/null +++ b/sgx-jvm/avian/src/util.cpp @@ -0,0 +1,604 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/util.h" +#include + +using namespace vm; + +namespace { + +class TreeContext { + public: + class MyProtector : public Thread::Protector { + public: + MyProtector(Thread* thread, TreeContext* context) + : Protector(thread), context(context) + { + } + + virtual void visit(Heap::Visitor* v) + { + v->visit(&(context->root)); + v->visit(&(context->node)); + + for (List* p = context->ancestors; p; p = p->next) { + v->visit(&(p->item)); + } + } + + TreeContext* context; + }; + + TreeContext(Thread* thread, Zone* zone) + : zone(zone), + root(0), + node(0), + ancestors(0), + protector(thread, this), + fresh(false) + { + } + + Zone* zone; + GcTreeNode* root; + GcTreeNode* node; + List* ancestors; + MyProtector protector; + bool fresh; +}; + +List* path(TreeContext* c, + GcTreeNode* node, + List* next) +{ + return new (c->zone) List(node, next); +} + +inline object getTreeNodeValue(Thread*, GcTreeNode* n) +{ + return reinterpret_cast(alias(n, TreeNodeValue) & PointerMask); +} + +inline void setTreeNodeValue(Thread* t, GcTreeNode* n, object value) +{ + intptr_t red = alias(n, TreeNodeValue) & (~PointerMask); + + n->setValue(t, value); + + alias(n, TreeNodeValue) |= red; +} + +inline bool treeNodeRed(Thread*, GcTreeNode* n) +{ + return (alias(n, TreeNodeValue) & (~PointerMask)) == 1; +} + +inline void setTreeNodeRed(Thread*, GcTreeNode* n, bool red) +{ + if (red) { + alias(n, TreeNodeValue) |= 1; + } else { + alias(n, TreeNodeValue) &= PointerMask; + } +} + +inline GcTreeNode* cloneTreeNode(Thread* t, GcTreeNode* n) +{ + PROTECT(t, n); + + GcTreeNode* newNode + = makeTreeNode(t, getTreeNodeValue(t, n), n->left(), n->right()); + setTreeNodeRed(t, newNode, treeNodeRed(t, n)); + return newNode; +} + +GcTreeNode* treeFind(Thread* t, + GcTreeNode* tree, + intptr_t key, + GcTreeNode* sentinal, + intptr_t (*compare)(Thread* t, intptr_t key, object b)) +{ + GcTreeNode* node = tree; + while (node != sentinal) { + intptr_t difference = compare(t, key, getTreeNodeValue(t, node)); + if (difference < 0) { + node = node->left(); + } else if (difference > 0) { + node = node->right(); + } else { + return node; + } + } + + return 0; +} + +void treeFind(Thread* t, + TreeContext* c, + GcTreeNode* old, + intptr_t key, + GcTreeNode* node, + GcTreeNode* sentinal, + intptr_t (*compare)(Thread* t, intptr_t key, object b)) +{ + PROTECT(t, old); + PROTECT(t, node); + PROTECT(t, sentinal); + + GcTreeNode* newRoot = cloneTreeNode(t, old); + PROTECT(t, newRoot); + + GcTreeNode* new_ = newRoot; + PROTECT(t, new_); + + int count = 0; + while (old != sentinal) { + c->ancestors = path(c, new_, c->ancestors); + + intptr_t difference = compare(t, key, getTreeNodeValue(t, old)); + + if (difference < 0) { + old = old->left(); + GcTreeNode* n = cloneTreeNode(t, old); + new_->setLeft(t, n); + new_ = n; + } else if (difference > 0) { + old = old->right(); + GcTreeNode* n = cloneTreeNode(t, old); + new_->setRight(t, n); + new_ = n; + } else { + c->fresh = false; + c->root = newRoot; + c->node = new_; + c->ancestors = c->ancestors->next; + return; + } + + if (++count > 100) { + // if we've gone this deep, we probably have an unbalanced tree, + // which should only happen if there's a serious bug somewhere + // in our insertion process + abort(t); + } + } + + setTreeNodeValue(t, new_, getTreeNodeValue(t, node)); + + c->fresh = true; + c->root = newRoot; + c->node = new_; + c->ancestors = c->ancestors; +} + +GcTreeNode* leftRotate(Thread* t, GcTreeNode* n) +{ + PROTECT(t, n); + + GcTreeNode* child = cloneTreeNode(t, n->right()); + n->setRight(t, child->left()); + child->setLeft(t, n); + return child; +} + +GcTreeNode* rightRotate(Thread* t, GcTreeNode* n) +{ + PROTECT(t, n); + + GcTreeNode* child = cloneTreeNode(t, n->left()); + n->setLeft(t, child->right()); + child->setRight(t, n); + return child; +} + +GcTreeNode* treeAdd(Thread* t, TreeContext* c) +{ + GcTreeNode* new_ = c->node; + PROTECT(t, new_); + + GcTreeNode* newRoot = c->root; + PROTECT(t, newRoot); + + // rebalance + setTreeNodeRed(t, new_, true); + while (c->ancestors != 0 and treeNodeRed(t, c->ancestors->item)) { + if (c->ancestors->item == c->ancestors->next->item->left()) { + if (treeNodeRed(t, c->ancestors->next->item->right())) { + setTreeNodeRed(t, c->ancestors->item, false); + + GcTreeNode* n = cloneTreeNode(t, c->ancestors->next->item->right()); + + c->ancestors->next->item->setRight(t, n); + + setTreeNodeRed(t, c->ancestors->next->item->right(), false); + + setTreeNodeRed(t, c->ancestors->next->item, true); + + new_ = c->ancestors->next->item; + c->ancestors = c->ancestors->next->next; + } else { + if (new_ == c->ancestors->item->right()) { + new_ = c->ancestors->item; + c->ancestors = c->ancestors->next; + + GcTreeNode* n = leftRotate(t, new_); + + if (new_ == c->ancestors->item->right()) { + c->ancestors->item->setRight(t, n); + } else { + c->ancestors->item->setLeft(t, n); + } + c->ancestors = path(c, n, c->ancestors); + } + setTreeNodeRed(t, c->ancestors->item, false); + setTreeNodeRed(t, c->ancestors->next->item, true); + + GcTreeNode* n = rightRotate(t, c->ancestors->next->item); + if (c->ancestors->next->next == 0) { + newRoot = n; + } else if (c->ancestors->next->next->item->right() + == c->ancestors->next->item) { + c->ancestors->next->next->item->setRight(t, n); + } else { + c->ancestors->next->next->item->setLeft(t, n); + } + // done + } + } else { // this is just the reverse of the code above (right and + // left swapped): + if (treeNodeRed(t, c->ancestors->next->item->left())) { + setTreeNodeRed(t, c->ancestors->item, false); + + GcTreeNode* n = cloneTreeNode(t, c->ancestors->next->item->left()); + + c->ancestors->next->item->setLeft(t, n); + + setTreeNodeRed(t, c->ancestors->next->item->left(), false); + + setTreeNodeRed(t, c->ancestors->next->item, true); + + new_ = c->ancestors->next->item; + c->ancestors = c->ancestors->next->next; + } else { + if (new_ == c->ancestors->item->left()) { + new_ = c->ancestors->item; + c->ancestors = c->ancestors->next; + + GcTreeNode* n = rightRotate(t, new_); + + if (new_ == c->ancestors->item->left()) { + c->ancestors->item->setLeft(t, n); + } else { + c->ancestors->item->setRight(t, n); + } + c->ancestors = path(c, n, c->ancestors); + } + setTreeNodeRed(t, c->ancestors->item, false); + setTreeNodeRed(t, c->ancestors->next->item, true); + + GcTreeNode* n = leftRotate(t, c->ancestors->next->item); + if (c->ancestors->next->next == 0) { + newRoot = n; + } else if (c->ancestors->next->next->item->left() + == c->ancestors->next->item) { + c->ancestors->next->next->item->setLeft(t, n); + } else { + c->ancestors->next->next->item->setRight(t, n); + } + // done + } + } + } + + setTreeNodeRed(t, newRoot, false); + + return newRoot; +} + +} // namespace + +namespace vm { + +GcTriple* hashMapFindNode(Thread* t, + GcHashMap* map, + object key, + uint32_t (*hash)(Thread*, object), + bool (*equal)(Thread*, object, object)) +{ + bool weak = objectClass(t, map) == type(t, GcWeakHashMap::Type); + + GcArray* array = map->array(); + if (array) { + unsigned index = hash(t, key) & (array->length() - 1); + for (GcTriple* n = cast(t, array->body()[index]); n; + n = cast(t, n->third())) { + object k = n->first(); + if (weak) { + k = cast(t, k)->target(); + if (k == 0) { + continue; + } + } + + if (equal(t, key, k)) { + return n; + } + } + } + return 0; +} + +void hashMapResize(Thread* t, + GcHashMap* map, + uint32_t (*hash)(Thread*, object), + unsigned size) +{ + PROTECT(t, map); + + GcArray* newArray = 0; + + if (size) { + GcArray* oldArray = map->array(); + PROTECT(t, oldArray); + + unsigned newLength = nextPowerOfTwo(size); + if (oldArray and oldArray->length() == newLength) { + return; + } + + newArray = makeArray(t, newLength); + + if (oldArray != map->array()) { + // a resize was performed during a GC via the makeArray call + // above; nothing left to do + return; + } + + if (oldArray) { + bool weak = objectClass(t, map) == type(t, GcWeakHashMap::Type); + for (unsigned i = 0; i < oldArray->length(); ++i) { + GcTriple* next; + for (GcTriple* p = cast(t, oldArray->body()[i]); p; + p = next) { + next = cast(t, p->third()); + + object k = p->first(); + if (weak) { + k = cast(t, k)->target(); + if (k == 0) { + continue; + } + } + + unsigned index = hash(t, k) & (newLength - 1); + + p->setThird(t, newArray->body()[index]); + newArray->setBodyElement(t, index, p); + } + } + } + } + + map->setArray(t, newArray); +} + +void hashMapInsert(Thread* t, + GcHashMap* map, + object key, + object value, + uint32_t (*hash)(Thread*, object)) +{ + // note that we reinitialize the array variable whenever an + // allocation (and thus possibly a collection) occurs, in case the + // array changes due to a table resize. + + PROTECT(t, map); + + uint32_t h = hash(t, key); + + bool weak = objectClass(t, map) == type(t, GcWeakHashMap::Type); + + GcArray* array = map->array(); + + ++map->size(); + + if (array == 0 or map->size() >= array->length() * 2) { + PROTECT(t, key); + PROTECT(t, value); + + hashMapResize(t, map, hash, array ? array->length() * 2 : 16); + + array = map->array(); + } + + object k = key; + + if (weak) { + PROTECT(t, key); + PROTECT(t, value); + + GcWeakReference* r = makeWeakReference(t, 0, 0, 0, 0); + + r->setTarget(t, key); + r->setVmNext(t, t->m->weakReferences); + t->m->weakReferences = r->as(t); + k = r; + + array = map->array(); + } + + GcTriple* n = makeTriple(t, k, value, 0); + + array = map->array(); + + unsigned index = h & (array->length() - 1); + + n->setThird(t, array->body()[index]); + array->setBodyElement(t, index, n); + + if (map->size() <= array->length() / 3) { + // this might happen if nodes were removed during GC in which case + // we weren't able to resize at the time + hashMapResize(t, map, hash, array->length() / 2); + } +} + +GcTriple* hashMapRemoveNode(Thread* t, + GcHashMap* map, + unsigned index, + GcTriple* p, + GcTriple* n) +{ + if (p) { + p->setThird(t, n->third()); + } else { + map->array()->setBodyElement(t, index, n->third()); + } + --map->size(); + return n; +} + +object hashMapRemove(Thread* t, + GcHashMap* map, + object key, + uint32_t (*hash)(Thread*, object), + bool (*equal)(Thread*, object, object)) +{ + bool weak = objectClass(t, map) == type(t, GcWeakHashMap::Type); + + GcArray* array = map->array(); + object o = 0; + if (array) { + unsigned index = hash(t, key) & (array->length() - 1); + GcTriple* p = 0; + for (GcTriple* n = cast(t, array->body()[index]); n;) { + object k = n->first(); + if (weak) { + k = cast(t, k)->target(); + if (k == 0) { + n = cast(t, + hashMapRemoveNode(t, map, index, p, n)->third()); + continue; + } + } + + if (equal(t, key, k)) { + o = hashMapRemoveNode(t, map, index, p, n)->second(); + break; + } else { + p = n; + n = cast(t, n->third()); + } + } + + if ((not t->m->collecting) and map->size() <= array->length() / 3) { + PROTECT(t, o); + hashMapResize(t, map, hash, array->length() / 2); + } + } + + return o; +} + +void listAppend(Thread* t, GcList* list, object value) +{ + PROTECT(t, list); + + ++list->size(); + + object p = makePair(t, value, 0); + if (list->front()) { + cast(t, list->rear())->setSecond(t, p); + } else { + list->setFront(t, p); + } + list->setRear(t, p); +} + +GcVector* vectorAppend(Thread* t, GcVector* vector, object value) +{ + if (vector->length() == vector->size()) { + PROTECT(t, vector); + PROTECT(t, value); + + GcVector* newVector + = makeVector(t, vector->size(), max(16, vector->size() * 2)); + + if (vector->size()) { + for (size_t i = 0; i < vector->size(); i++) { + newVector->setBodyElement(t, i, vector->body()[i]); + } + } + + vector = newVector; + } + + vector->setBodyElement(t, vector->size(), value); + ++vector->size(); + return vector; +} + +GcArray* growArray(Thread* t, GcArray* array) +{ + PROTECT(t, array); + + GcArray* newArray = makeArray(t, array == 0 ? 16 : (array->length() * 2)); + + if (array) { + for (size_t i = 0; i < array->length(); i++) { + newArray->setBodyElement(t, i, array->body()[i]); + } + } + + return newArray; +} + +object treeQuery(Thread* t, + GcTreeNode* tree, + intptr_t key, + GcTreeNode* sentinal, + intptr_t (*compare)(Thread* t, intptr_t key, object b)) +{ + GcTreeNode* node = treeFind(t, tree, key, sentinal, compare); + return (node ? getTreeNodeValue(t, node) : 0); +} + +GcTreeNode* treeInsert(Thread* t, + Zone* zone, + GcTreeNode* tree, + intptr_t key, + object value, + GcTreeNode* sentinal, + intptr_t (*compare)(Thread* t, intptr_t key, object b)) +{ + PROTECT(t, tree); + PROTECT(t, sentinal); + + GcTreeNode* node = makeTreeNode(t, value, sentinal, sentinal); + + TreeContext c(t, zone); + treeFind(t, &c, tree, key, node, sentinal, compare); + expect(t, c.fresh); + + return treeAdd(t, &c); +} + +void treeUpdate(Thread* t, + GcTreeNode* tree, + intptr_t key, + object value, + GcTreeNode* sentinal, + intptr_t (*compare)(Thread* t, intptr_t key, object b)) +{ + setTreeNodeValue(t, treeFind(t, tree, key, sentinal, compare), value); +} + +} // namespace vm diff --git a/sgx-jvm/avian/src/util/CMakeLists.txt b/sgx-jvm/avian/src/util/CMakeLists.txt new file mode 100644 index 0000000000..d4311a7b7f --- /dev/null +++ b/sgx-jvm/avian/src/util/CMakeLists.txt @@ -0,0 +1 @@ +add_library(avian_util arg-parser.cpp fixed-allocator.cpp) \ No newline at end of file diff --git a/sgx-jvm/avian/src/util/arg-parser.cpp b/sgx-jvm/avian/src/util/arg-parser.cpp new file mode 100644 index 0000000000..50493d44fb --- /dev/null +++ b/sgx-jvm/avian/src/util/arg-parser.cpp @@ -0,0 +1,101 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include +#include + +#include +#include + +namespace avian { +namespace util { + +Arg::Arg(ArgParser& parser, bool required, const char* name, const char* desc) + : next(0), required(required), name(name), desc(desc), value(0) +{ + *parser.last = this; + parser.last = &next; +} + +ArgParser::ArgParser() : first(0), last(&first) +{ +} + +bool ArgParser::parse(int ac, const char* const* av) +{ + Arg* state = 0; + + for (int i = 1; i < ac; i++) { + if (state) { + if (state->value) { + fprintf(stderr, + "duplicate parameter %s: '%s' and '%s'\n", + state->name, + state->value, + av[i]); + return false; + } + state->value = av[i]; + state = 0; + } else { + if (av[i][0] != '-') { + fprintf(stderr, "expected -parameter\n"); + return false; + } + bool found = false; + for (Arg* arg = first; arg; arg = arg->next) { + if (strcmp(arg->name, &av[i][1]) == 0) { + found = true; + if (arg->desc == 0) { + arg->value = "true"; + } else { + state = arg; + } + } + } + if (not found) { + fprintf(stderr, "unrecognized parameter %s\n", av[i]); + return false; + } + } + } + + if (state) { + fprintf(stderr, "expected argument after -%s\n", state->name); + return false; + } + + for (Arg* arg = first; arg; arg = arg->next) { + if (arg->required && !arg->value) { + fprintf(stderr, "expected value for %s\n", arg->name); + return false; + } + } + + return true; +} + +void ArgParser::printUsage(const char* exe) +{ + fprintf(stderr, "usage:\n%s \\\n", exe); + for (Arg* arg = first; arg; arg = arg->next) { + const char* lineEnd = arg->next ? " \\" : ""; + if (arg->required) { + fprintf(stderr, " -%s\t%s%s\n", arg->name, arg->desc, lineEnd); + } else if (arg->desc) { + fprintf(stderr, " [-%s\t%s]%s\n", arg->name, arg->desc, lineEnd); + } else { + fprintf(stderr, " [-%s]%s\n", arg->name, lineEnd); + } + } +} + +} // namespace util +} // namespace avian diff --git a/sgx-jvm/avian/src/util/fixed-allocator.cpp b/sgx-jvm/avian/src/util/fixed-allocator.cpp new file mode 100644 index 0000000000..bc87e6d1c0 --- /dev/null +++ b/sgx-jvm/avian/src/util/fixed-allocator.cpp @@ -0,0 +1,54 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include + +#include + +namespace avian { +namespace util { + +FixedAllocator::FixedAllocator(Aborter* a, Slice memory) + : a(a), memory(memory), offset(0) +{ +} + +void* FixedAllocator::tryAllocate(size_t size) +{ + return allocate(size); +} + +void* FixedAllocator::allocate(size_t size, unsigned padAlignment) +{ + size_t paddedSize = vm::pad(size, padAlignment); + expect(a, offset + paddedSize < memory.count); + + void* p = memory.begin() + offset; + offset += paddedSize; + return p; +} + +void* FixedAllocator::allocate(size_t size) +{ + return allocate(size, vm::BytesPerWord); +} + +void FixedAllocator::free(const void* p, size_t size) +{ + if (p >= memory.begin() + and static_cast(p) + size == memory.begin() + offset) { + offset -= size; + } else { + abort(a); + } +} + +} // namespace util +} // namespace avian diff --git a/sgx-jvm/avian/src/x86_64.S b/sgx-jvm/avian/src/x86_64.S new file mode 100644 index 0000000000..501b8982f1 --- /dev/null +++ b/sgx-jvm/avian/src/x86_64.S @@ -0,0 +1,340 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "avian/types.h" + +#define LOCAL(x) .L##x + +#if defined __APPLE__ \ + || ((defined __MINGW32__ || defined __CYGWIN32__) && ! defined __x86_64__) +# define GLOBAL(x) _##x +#else +# define GLOBAL(x) x +#endif + +.text + +#define CHECKPOINT_THREAD 8 +#define CHECKPOINT_STACK 48 + +#ifdef __MINGW32__ + +.globl GLOBAL(vmNativeCall) +GLOBAL(vmNativeCall): + pushq %rbp + //save nonvolatile registers + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 + movq %rsp, %rbp + + + // %rcx: function + // %rdx: arguments + // %r8: arguments count + // %r9: return type + + movq %rcx, %r10 + movq %rdx, %r11 + movq %r8, %r12 + movq %r9, %r13 + + // %r10: function + // %r11: arguments + // %r12: arguments count + // %r13: return type + + //allocate initial stack space + subq $32, %rsp + + //first arg + cmp $0, %r12 + je LOCAL(call) + movq 0(%r11),%rcx + movq 0(%r11),%xmm0 + subq $1, %r12 + + //second arg + cmp $0, %r12 + je LOCAL(call) + movq 8(%r11),%rdx + movq 8(%r11),%xmm1 + subq $1, %r12 + + //third arg + cmp $0, %r12 + je LOCAL(call) + movq 16(%r11),%r8 + movq 16(%r11),%xmm2 + subq $1, %r12 + + //fourth arg + cmp $0, %r12 + je LOCAL(call) + movq 24(%r11),%r9 + movq 24(%r11),%xmm3 + subq $1, %r12 + + + //calculate stack space for arguments, aligned + movq $8, %r15 + leaq (%r15, %r12, 8), %r15 + andq $0xFFFFFFFFFFFFFFF0, %r15 + + //reserve stack space for arguments + subq %r15, %rsp + + //reset the counter + addq $3, %r12 + jmp LOCAL(loopend) + +LOCAL(loop): + movq (%r11, %r12, 8), %r14 + movq %r14, (%rsp, %r12, 8); + subq $1, %r12 + +LOCAL(loopend): + //we don't need to move arg 3 and lower + cmpq $3, %r12 + jne LOCAL(loop) + +LOCAL(call): + call *%r10 + +LOCAL(void): + cmpq $VOID_TYPE,%r13 + jne LOCAL(float) + jmp LOCAL(exit) + +LOCAL(float): + cmpq $FLOAT_TYPE,%r13 + je LOCAL(copy) + cmpq $DOUBLE_TYPE,%r13 + jne LOCAL(exit) + +LOCAL(copy): + movq %xmm0,%rax + +LOCAL(exit): + + movq %rbp, %rsp + //return nonvolatile registers to their former state + popq %r15 + popq %r14 + popq %r13 + popq %r12 + + popq %rbp + ret + +.globl GLOBAL(vmJump) +GLOBAL(vmJump): + movq %rdx,%rbp + movq 40(%rsp),%rax + movq 48(%rsp),%rdx + movq %r8,%rsp + movq %r9,%rbx + jmp *%rcx + +#define VMRUN_FRAME_SIZE 80 + +.globl GLOBAL(vmRun) +GLOBAL(vmRun): + // %rcx: function + // %rdx: arguments + // %r8 : checkpoint + pushq %rbp + movq %rsp,%rbp + subq $VMRUN_FRAME_SIZE,%rsp + + movq %rbx,16(%rsp) + movq %r12,24(%rsp) + movq %r13,32(%rsp) + movq %r14,40(%rsp) + movq %r15,48(%rsp) + movq %rsi,56(%rsp) + movq %rdi,64(%rsp) + + movq %rsp,CHECKPOINT_STACK(%r8) + + movq %rcx,%r11 + movq CHECKPOINT_THREAD(%r8),%rcx + + call *%r11 + +.globl GLOBAL(vmRun_returnAddress) +GLOBAL(vmRun_returnAddress): + + movq 16(%rsp),%rbx + movq 24(%rsp),%r12 + movq 32(%rsp),%r13 + movq 40(%rsp),%r14 + movq 48(%rsp),%r15 + movq 56(%rsp),%rsi + movq 64(%rsp),%rdi + + addq $VMRUN_FRAME_SIZE,%rsp + popq %rbp + ret + +#else // not __MINGW32__ + +.globl GLOBAL(vmNativeCall) +GLOBAL(vmNativeCall): + pushq %rbp + movq %rsp,%rbp + + // %rdi aka -48(%rbp): function + // %rsi aka -40(%rbp): stack + // %rdx aka -32(%rbp): stackSize + // %rcx aka -24(%rbp): gprTable + // %r8 aka -16(%rbp): sseTable + // %r9 aka -8(%rbp): returnType + + // save our argument registers so we can clobber them + pushq %r9 + pushq %r8 + pushq %rcx + pushq %rdx + pushq %rsi + pushq %rdi + + // reserve space for arguments passed via memory + subq %rdx,%rsp + + // align to a 16 byte boundary + andq $0xFFFFFFFFFFFFFFF0,%rsp + + // copy memory arguments into place + movq $0,%rcx + jmp LOCAL(test) + +LOCAL(loop): + movq %rcx,%rax + movq %rcx,%rdx + addq %rsp,%rdx + addq -40(%rbp),%rax + movq (%rax),%rax + movq %rax,(%rdx) + addq $8,%rcx + +LOCAL(test): + cmpq -32(%rbp),%rcx + jb LOCAL(loop) + + // do we need to load the general-purpose registers? + cmpq $0,-24(%rbp) + je LOCAL(sse) + + // yes, we do + movq -24(%rbp),%rax + movq 0(%rax),%rdi + movq 8(%rax),%rsi + movq 16(%rax),%rdx + movq 24(%rax),%rcx + movq 32(%rax),%r8 + movq 40(%rax),%r9 + +LOCAL(sse): + // do we need to load the SSE registers? + cmpq $0,-16(%rbp) + je LOCAL(call) + + // yes, we do + movq -16(%rbp),%rax + movq 0(%rax),%xmm0 + movq 8(%rax),%xmm1 + movq 16(%rax),%xmm2 + movq 24(%rax),%xmm3 + movq 32(%rax),%xmm4 + movq 40(%rax),%xmm5 + movq 48(%rax),%xmm6 + movq 56(%rax),%xmm7 + +LOCAL(call): + call *-48(%rbp) + + // handle return value based on expected type + movq -8(%rbp),%rcx + +LOCAL(void): + cmpq $VOID_TYPE,%rcx + jne LOCAL(float) + jmp LOCAL(exit) + +LOCAL(float): + cmpq $FLOAT_TYPE,%rcx + je LOCAL(copy) + cmpq $DOUBLE_TYPE,%rcx + jne LOCAL(exit) + +LOCAL(copy): +#ifdef __APPLE__ + // as of OS X 10.6, Apple is still using an assembler that doesn't + // understand movq SSE,GPR, but movd does the same thing, despite + // the name + movd %xmm0,%rax +#else + movq %xmm0,%rax +#endif + +LOCAL(exit): + movq %rbp,%rsp + popq %rbp + ret + +.globl GLOBAL(vmJump) +GLOBAL(vmJump): + movq %rsi,%rbp + movq %rdx,%rsp + movq %rcx,%rbx + movq %r8,%rax + movq %r9,%rdx + jmp *%rdi + +#define VMRUN_FRAME_SIZE 64 + +.globl GLOBAL(vmRun) +GLOBAL(vmRun): + // %rdi: function + // %rsi: arguments + // %rdx: checkpoint + pushq %rbp + movq %rsp,%rbp + subq $VMRUN_FRAME_SIZE,%rsp + + movq %rbx,16(%rsp) + movq %r12,24(%rsp) + movq %r13,32(%rsp) + movq %r14,40(%rsp) + movq %r15,48(%rsp) + + movq %rsp,CHECKPOINT_STACK(%rdx) + + movq %rdi,%r11 + movq CHECKPOINT_THREAD(%rdx),%rdi + + call *%r11 + +.globl GLOBAL(vmRun_returnAddress) +GLOBAL(vmRun_returnAddress): + + movq 16(%rsp),%rbx + movq 24(%rsp),%r12 + movq 32(%rsp),%r13 + movq 40(%rsp),%r14 + movq 48(%rsp),%r15 + + addq $VMRUN_FRAME_SIZE,%rsp + popq %rbp + ret + +#endif // not __MINGW32__ diff --git a/sgx-jvm/avian/test/AllFloats.java b/sgx-jvm/avian/test/AllFloats.java new file mode 100644 index 0000000000..0bba388b9b --- /dev/null +++ b/sgx-jvm/avian/test/AllFloats.java @@ -0,0 +1,86 @@ +public class AllFloats { + private static float multiplyByFive(float a) {return 5f * a;} + private static double multiplyByFive(double a) {return 5d * a;} + private static float multiply(float a, float b) {return a * b;} + private static double multiply(double a, double b) {return a * b;} + private static double multiply(float a, double b) {return a * b;} + private static float divide(float a, float b) {return a / b;} + private static double divide(double a, double b) {return a / b;} + private static double divide(float a, double b) {return a / b;} + private static float remainder(float a, float b) {return a % b;} + private static double remainder(double a, double b) {return a % b;} + private static double remainder(float a, double b) {return a % b;} + private static float add(float a, float b) {return a + b;} + private static double add(double a, double b) {return a + b;} + private static double add(float a, double b) {return a + b;} + private static float subtract(float a, float b) {return a - b;} + private static double subtract(double a, double b) {return a - b;} + private static double subtract(float a, double b) {return a - b;} + private static float complex(float a, float b) {return (a - b) / (a * b) + (float)Math.sqrt(a);} + private static double complex(double a, double b) {return (a - b) / (a * b) + Math.sqrt(a);} + private static double complex(float a, double b) {return (a - b) / (a * b) + Math.sqrt(a);} + private static double sqrt(double a) {return Math.sqrt(a);} + private static float complexNoIntrinsic(float a, float b) {return (a - b) / (a * b) + (float)sqrt(a);} + private static int f2i(float a) {return (int)a;} + private static long f2l(float a) {return (long)a;} + private static float i2f(int a) {return (float)a;} + private static double i2d(int a) {return (double)a;} + private static int d2i(double a) {return (int)a;} + private static long d2l(double a) {return (long)a;} + private static float l2f(long a) {return (float)a;} + private static double l2d(long a) {return (double)a;} + private static float negate(float a) {return -a;} + private static double negate(double a) {return -a;} + private static int abs(int a) {return Math.abs(a);} + private static float abs(float a) {return Math.abs(a);} + + private static void expect(boolean v) { + if(!v)throw new RuntimeException(); + } + + private static int last(){return 0;} + + public static void main(String[] args) { + expect(multiplyByFive(36f) == 5f * 36f); + expect(multiplyByFive(36d) == 5d * 36d); + expect(multiply(5f, 4f) == 5f*4f); + expect(multiply(5d, 4d) == 5d*4d); + expect(multiply(5f, 4d) == 5f*4d); + expect(divide(5f, 2f) == 5f/2f); + expect(divide(5d, 2d) == 5d/2d); + expect(divide(5f, 2d) == 5f/2d); + expect(remainder(5f, 2f) == 5f%2f); + expect(remainder(5d, 2d) == 5d%2d); + expect(remainder(5f, 2d) == 5f%2d); + expect(add(5f, 4f) == 5f+4f); + expect(add(5d, 4d) == 5f+4d); + expect(add(5f, 4d) == 5f+4d); + expect(subtract(5f, 4f) == 5f-4f); + expect(subtract(5d, 4d) == 5f-4d); + expect(subtract(5f, 4d) == 5f-4d); + expect(complex(4f, 3f) == (4f-3f)/(4f*3f) + 2f); + expect(complex(4d, 3d) == (4d-3d)/(4d*3d) + 2d); + expect(complex(4f, 3d) == (4f-3d)/(4f*3d) + 2f); + expect(complexNoIntrinsic(4f, 3f) == (4f-3f)/(4f*3f) + 2f); + + expect(f2i(4f) == 4); + expect(f2l(4f) == 4); + expect(i2f(4) == 4f); + expect(i2d(4) == 4d); + + expect(d2i(4d) == 4); + expect(d2l(4d) == 4); + expect(l2f(4) == 4f); + expect(l2d(4) == 4d); + + expect(negate(4f) == -4f); + expect(negate(4d) == -4d); + + expect(abs(-4) == 4); + expect(abs(12) == 12); + expect(abs(-4f) == 4f); + expect(abs(12f) == 12f); + + int unused = last(); + } +} diff --git a/sgx-jvm/avian/test/Annotations.java b/sgx-jvm/avian/test/Annotations.java new file mode 100644 index 0000000000..f09b0251eb --- /dev/null +++ b/sgx-jvm/avian/test/Annotations.java @@ -0,0 +1,89 @@ +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import avian.testing.annotations.Color; +import avian.testing.annotations.Test; +import avian.testing.annotations.TestComplex; +import avian.testing.annotations.TestEnum; +import avian.testing.annotations.TestInteger; + +public class Annotations { + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + public static void main(String[] args) throws Exception { + Method m = Annotations.class.getMethod("foo"); + + expect(m.isAnnotationPresent(Test.class)); + + expect(((Test) m.getAnnotation(Test.class)).value().equals("couscous")); + + expect(((TestEnum) m.getAnnotation(TestEnum.class)).value() + .equals(Color.Red)); + + expect(((TestInteger) m.getAnnotation(TestInteger.class)).value() == 42); + + expect(m.getAnnotations().length == 3); + + Method noAnno = Annotations.class.getMethod("noAnnotation"); + expect(noAnno.getAnnotation(Test.class) == null); + expect(noAnno.getAnnotations().length == 0); + testProxyDefaultValue(); + testComplexAnnotation(); + } + + @Test("couscous") + @TestEnum(Color.Red) + @TestInteger(42) + public static void foo() { + + } + + public static void noAnnotation() { + + } + + private static void testProxyDefaultValue() { + ClassLoader loader = Annotations.class.getClassLoader(); + InvocationHandler handler = new InvocationHandler() { + public Object invoke(Object proxy, Method method, Object... args) { + return method.getDefaultValue(); + } + }; + Test test = (Test) + Proxy.newProxyInstance(loader, new Class[] { Test.class }, handler); + expect("Hello, world!".equals(test.value())); + } + + private interface World { + @TestComplex(arrayValue = { @Test, @Test(value = "7/9") }, + stringValue = "adjunct element", charValue = '7', doubleValue = 0.7778, + classValue = TestInteger.class) + int hello(); + } + + private static void testComplexAnnotation(TestComplex annotation) + throws Exception + { + expect(2 == annotation.arrayValue().length); + expect("Hello, world!".equals(annotation.arrayValue()[0].value())); + expect("7/9".equals(annotation.arrayValue()[1].value())); + expect("adjunct element".equals(annotation.stringValue())); + expect('7' == annotation.charValue()); + expect(0.7778 == annotation.doubleValue()); + expect(TestInteger.class == annotation.classValue()); + } + + public static void testComplexAnnotation() throws Exception { + ClassLoader loader = Annotations.class.getClassLoader(); + TestComplex annotation = (TestComplex) + World.class.getMethod("hello").getAnnotation(TestComplex.class); + testComplexAnnotation(annotation); + Class clazz = Proxy.getProxyClass(loader, new Class[] { World.class }); + annotation = (TestComplex) + clazz.getMethod("hello").getAnnotation(TestComplex.class); + expect(annotation == null); + } +} diff --git a/sgx-jvm/avian/test/ArrayDequeTest.java b/sgx-jvm/avian/test/ArrayDequeTest.java new file mode 100644 index 0000000000..103cda4fa3 --- /dev/null +++ b/sgx-jvm/avian/test/ArrayDequeTest.java @@ -0,0 +1,161 @@ +import java.util.ArrayDeque; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.NoSuchElementException; + +public class ArrayDequeTest { + private static void verify(boolean val) { + if (! val) { + throw new RuntimeException(); + } + } + + public static void main(String[] args) throws InterruptedException { + QueueHelper.sizeTest(new ArrayDeque()); + QueueHelper.isEmptyTest(new ArrayDeque()); + QueueHelper.addTest(new ArrayDeque()); + QueueHelper.addAllTest(new ArrayDeque()); + QueueHelper.elementTest(new ArrayDeque()); + QueueHelper.elementFail(new ArrayDeque()); + QueueHelper.removeEmptyFail(new ArrayDeque()); + QueueHelper.removeTest(new ArrayDeque()); + QueueHelper.containsTest(new ArrayDeque()); + QueueHelper.containsAllTest(new ArrayDeque()); + QueueHelper.removeObjectTest(new ArrayDeque()); + QueueHelper.removeAllTest(new ArrayDeque()); + QueueHelper.clearTest(new ArrayDeque()); + QueueHelper.toArrayTest(new ArrayDeque()); + + DequeHelper.addFirstTest(new ArrayDeque()); + DequeHelper.addLastTest(new ArrayDeque()); + DequeHelper.removeFirstTest(new ArrayDeque()); + DequeHelper.removeLastTest(new ArrayDeque()); + + iterateTest(false); + iterateTest(true); + iteratorRemoveTest(false); + iteratorRemoveTest(true); + iteratorNoElementFail(false); + iteratorNoElementFail(true); + } + + private static void iterateTest(boolean desc) { + int testQty = 10; + LinkedList compareList = new LinkedList(); + ArrayDeque ad = new ArrayDeque(); + + for (int i = 0; i < testQty; i++) { + Object o = new Object(); + compareList.add(o); + ad.add(o); + } + + Iterator compIt; + Iterator testIt; + if (desc) { + compIt = compareList.descendingIterator(); + testIt = ad.descendingIterator(); + } else { + compIt = compareList.iterator(); + testIt = ad.iterator(); + } + while (testIt.hasNext()) { + verify(testIt.next() == compIt.next()); + } + + // remove from the front + compareList.removeFirst(); + ad.removeFirst(); + + if (desc) { + compIt = compareList.descendingIterator(); + testIt = ad.descendingIterator(); + } else { + compIt = compareList.iterator(); + testIt = ad.iterator(); + } + while (testIt.hasNext()) { + verify(testIt.next() == compIt.next()); + } + + // remove from the end + compareList.removeLast(); + ad.removeLast(); + + if (desc) { + compIt = compareList.descendingIterator(); + testIt = ad.descendingIterator(); + } else { + compIt = compareList.iterator(); + testIt = ad.iterator(); + } + while (testIt.hasNext()) { + verify(testIt.next() == compIt.next()); + } + } + + private static void iteratorRemoveTest(boolean desc) { + int testQty = 20; + LinkedList compareList = new LinkedList(); + ArrayDeque ad = new ArrayDeque(); + + for (int i = 0; i < testQty; i++) { + Object o = new Object(); + compareList.add(o); + ad.add(o); + } + + Iterator compIt; + Iterator testIt; + if (desc) { + compIt = compareList.descendingIterator(); + testIt = ad.descendingIterator(); + } else { + compIt = compareList.iterator(); + testIt = ad.iterator(); + } + boolean flip = true; // start with true to ensure first is removed + while (testIt.hasNext()) { + // advance iterators + testIt.next(); + compIt.next(); + + if (flip || ! testIt.hasNext()) { + compIt.remove(); + testIt.remove(); + flip = false; + } else { + flip = true; + } + } + + if (desc) { + compIt = compareList.descendingIterator(); + testIt = ad.descendingIterator(); + } else { + compIt = compareList.iterator(); + testIt = ad.iterator(); + } + while (testIt.hasNext()) { + verify(testIt.next() == compIt.next()); + } + } + + private static void iteratorNoElementFail(boolean desc) { + ArrayDeque ad = new ArrayDeque(); + + Iterator testIt; + if (desc) { + testIt = ad.descendingIterator(); + } else { + testIt = ad.iterator(); + } + + try { + testIt.next(); + throw new RuntimeException("Exception should have thrown"); + } catch (NoSuchElementException e) { + // expected + } + } +} diff --git a/sgx-jvm/avian/test/ArraysTest.java b/sgx-jvm/avian/test/ArraysTest.java new file mode 100644 index 0000000000..cd008065b6 --- /dev/null +++ b/sgx-jvm/avian/test/ArraysTest.java @@ -0,0 +1,174 @@ +import java.util.Arrays; + +public class ArraysTest { + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static > void expectSorted(T[] array) { + for (int i = 1; i < array.length; ++i) { + expect(array[i - 1].compareTo(array[i]) <= 0); + } + } + + private static int pseudoRandom(int seed) { + return 3170425 * seed + 132102; + } + + private static > int shuffle(T[] array, int seed) { + for (int i = array.length; i > 1; --i) { + int i2 = (seed < 0 ? -seed : seed) % i; + T value = array[i - 1]; + array[i - 1] = array[i2]; + array[i2] = value; + seed = pseudoRandom(seed); + } + return seed; + } + + public static void testSort() { + Integer[] array = new Integer[64]; + for (int i = 0; i < array.length; ++i) { + array[i] = Integer.valueOf(i + 1); + } + ; + int random = 12345; + for (int i = 0; i < 32; ++i) { + random = shuffle(array, random); + Arrays.sort(array); + expectSorted(array); + } + } + + public static void main(String[] args) { + { int[] array = new int[0]; + Exception exception = null; + try { + int x = array[0]; + } catch (ArrayIndexOutOfBoundsException e) { + exception = e; + } + + expect(exception != null); + } + + { int[] array = new int[0]; + Exception exception = null; + try { + int x = array[-1]; + } catch (ArrayIndexOutOfBoundsException e) { + exception = e; + } + + expect(exception != null); + } + + { int[] array = new int[3]; + int i = 0; + array[i++] = 1; + array[i++] = 2; + array[i++] = 3; + + expect(array[--i] == 3); + expect(array[--i] == 2); + expect(array[--i] == 1); + } + + { Object[][] array = new Object[1][1]; + expect(array.length == 1); + expect(array[0].length == 1); + } + + { Object[][] array = new Object[2][3]; + expect(array.length == 2); + expect(array[0].length == 3); + } + + { int j = 0; + byte[] decodeTable = new byte[256]; + for (int i = 'A'; i <= 'Z'; ++i) decodeTable[i] = (byte) j++; + for (int i = 'a'; i <= 'z'; ++i) decodeTable[i] = (byte) j++; + for (int i = '0'; i <= '9'; ++i) decodeTable[i] = (byte) j++; + decodeTable['+'] = (byte) j++; + decodeTable['/'] = (byte) j++; + decodeTable['='] = 0; + + expect(decodeTable['a'] != 0); + } + + { boolean p = true; + int[] array = new int[] { 1, 2 }; + expect(array[0] == array[p ? 0 : 1]); + p = false; + expect(array[1] == array[p ? 0 : 1]); + } + + { int[] array = new int[1024]; + array[1023] = -1; + expect(array[1023] == -1); + expect(array[1022] == 0); + } + + { Integer[] array = (Integer[]) + java.lang.reflect.Array.newInstance(Integer.class, 1); + array[0] = Integer.valueOf(42); + expect(array[0].intValue() == 42); + } + + { Object[] a = new Object[3]; + Object[] b = new Object[3]; + + expect(Arrays.equals(a, b)); + a[0] = new Object(); + expect(! Arrays.equals(a, b)); + expect(! Arrays.equals(b, new Object[4])); + expect(! Arrays.equals(a, null)); + expect(! Arrays.equals(null, b)); + expect(Arrays.equals((Object[])null, (Object[])null)); + b[0] = a[0]; + expect(Arrays.equals(a, b)); + + Arrays.hashCode(a); + Arrays.hashCode((Object[])null); + } + + { String[] list = new String[] { "Hello", "World", "!" }; + Object[] result = Arrays.copyOf(list, 2, Object[].class); + expect(list[1] == result[1]); + expect(result.length == 2); + expect(result.getClass().getComponentType() == Object.class); + } + + { Object[] a = new Object[3]; + Object[] b = new Object[3]; + + expect(Arrays.deepEquals(a, b)); + + a[0] = new Object(); + expect(! Arrays.deepEquals(a, b)); + expect(! Arrays.deepEquals(b, new Object[4])); + expect(! Arrays.deepEquals(a, null)); + expect(! Arrays.deepEquals(null, b)); + expect(Arrays.deepEquals((Object[])null, (Object[])null)); + + b[0] = a[0]; + expect(Arrays.deepEquals(a, b)); + + a[0] = new Object[] {1}; + expect(! Arrays.deepEquals(a, b)); + b[0] = new Object[] {1}; + expect(Arrays.deepEquals(a, b)); + ((Object[])a[0])[0] = (Long)1L; + expect(! Arrays.deepEquals(a, b)); + a[0] = new Integer[] {1}; + expect(Arrays.deepEquals(a, b)); + + a[0] = new int[] {1}; + expect(! Arrays.deepEquals(a, b)); + b[0] = new int[] {1}; + expect(Arrays.deepEquals(a, b)); + } + + testSort(); + } +} diff --git a/sgx-jvm/avian/test/AtomicTests.java b/sgx-jvm/avian/test/AtomicTests.java new file mode 100644 index 0000000000..9dab42703e --- /dev/null +++ b/sgx-jvm/avian/test/AtomicTests.java @@ -0,0 +1,231 @@ +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + + +public class AtomicTests { + private static final int threadCount = 10; + private static final int iterationsPerThread = 100; + + public static void main(String[] args) { + runAtomicIntegerTest(true); + runAtomicIntegerTest(false); + + runAtomicLongTest(true); + runAtomicLongTest(false); + + runAtomicReferenceTest(); + } + + private static void blockTillThreadsDone(AtomicInteger threadDoneCount) throws InterruptedException { + synchronized (threadDoneCount) { + while (threadDoneCount.get() < threadCount) { + threadDoneCount.wait(); + } + } + } + + private static void runAtomicIntegerTest(final boolean increment) { + final AtomicInteger result = new AtomicInteger(); + final AtomicInteger threadDoneCount = new AtomicInteger(); + // only using an AtomicBoolean here so I don't need two variables to do the synchronize/wait/notify + final AtomicBoolean threadsStart = new AtomicBoolean(false); + + Runnable operationRunnable = new Runnable() { + @Override + public void run() { + boolean flip = true; + for (int i = 0; i < iterationsPerThread; i++) { + if (flip) { + if (increment) { + result.incrementAndGet(); + } else { + result.decrementAndGet(); + } + flip = false; + } else { + if (increment) { + result.getAndIncrement(); + } else { + result.getAndDecrement(); + } + flip = true; + } + } + } + }; + + for (int i = 0; i < threadCount; i++) { + new Thread(new DelayedRunnable(threadsStart, + operationRunnable, + threadDoneCount)).start(); + } + + synchronized (threadsStart) { + threadsStart.set(true); + + threadsStart.notifyAll(); + } + + try { + blockTillThreadsDone(threadDoneCount); + } catch (InterruptedException e) { + // let thread exit + return; + } + + int expectedResult = threadCount * iterationsPerThread; + if (! increment) { + expectedResult *= -1; + } + int resultValue = result.get(); + if (resultValue != expectedResult) { + throw new IllegalStateException(resultValue + " != " + expectedResult); + } + } + + private static void runAtomicLongTest(final boolean increment) { + final AtomicLong result = new AtomicLong(); + final AtomicInteger threadDoneCount = new AtomicInteger(); + // only using an AtomicBoolean here so I don't need two variables to do the synchronize/wait/notify + final AtomicBoolean threadsStart = new AtomicBoolean(false); + + Runnable operationRunnable = new Runnable() { + @Override + public void run() { + boolean flip = true; + for (int i = 0; i < iterationsPerThread; i++) { + if (flip) { + if (increment) { + result.incrementAndGet(); + } else { + result.decrementAndGet(); + } + flip = false; + } else { + if (increment) { + result.getAndIncrement(); + } else { + result.getAndDecrement(); + } + flip = true; + } + } + } + }; + + for (int i = 0; i < threadCount; i++) { + new Thread(new DelayedRunnable(threadsStart, + operationRunnable, + threadDoneCount)).start(); + } + + synchronized (threadsStart) { + threadsStart.set(true); + + threadsStart.notifyAll(); + } + + try { + blockTillThreadsDone(threadDoneCount); + } catch (InterruptedException e) { + // let thread exit + return; + } + + long expectedResult = threadCount * iterationsPerThread; + if (! increment) { + expectedResult *= -1; + } + long resultValue = result.get(); + if (resultValue != expectedResult) { + throw new IllegalStateException(resultValue + " != " + expectedResult); + } + } + + private static void runAtomicReferenceTest() { + final AtomicReference result = new AtomicReference(0); + final AtomicInteger threadDoneCount = new AtomicInteger(0); + // only using an AtomicBoolean here so I don't need two variables to do the synchronize/wait/notify + final AtomicBoolean threadsStart = new AtomicBoolean(false); + + Runnable operationRunnable = new Runnable() { + @Override + public void run() { + for (int i = 0; i < iterationsPerThread; i++) { + Integer current = result.get(); + while (! result.compareAndSet(current, current + 1)) { + current = result.get(); + } + } + } + }; + + for (int i = 0; i < threadCount; i++) { + new Thread(new DelayedRunnable(threadsStart, + operationRunnable, + threadDoneCount)).start(); + } + + synchronized (threadsStart) { + threadsStart.set(true); + + threadsStart.notifyAll(); + } + + try { + blockTillThreadsDone(threadDoneCount); + } catch (InterruptedException e) { + // let thread exit + return; + } + + long expectedResult = threadCount * iterationsPerThread; + Integer resultValue = result.get(); + if (resultValue != expectedResult) { + throw new IllegalStateException(resultValue + " != " + expectedResult); + } + } + + private static class DelayedRunnable implements Runnable { + private final AtomicBoolean threadsStart; + private final Runnable operationRunnable; + private final AtomicInteger threadDoneCount; + + private DelayedRunnable(AtomicBoolean threadsStart, + Runnable operationRunnable, + AtomicInteger threadDoneCount) { + this.threadsStart = threadsStart; + this.operationRunnable = operationRunnable; + this.threadDoneCount = threadDoneCount; + } + + @Override + public void run() { + try { + try { + waitTillReady(); + } catch (InterruptedException e) { + // let thread exit + return; + } + operationRunnable.run(); + } finally { + synchronized (threadDoneCount) { + threadDoneCount.incrementAndGet(); + + threadDoneCount.notifyAll(); + } + } + } + + private void waitTillReady() throws InterruptedException { + synchronized (threadsStart) { + while (! threadsStart.get()) { + threadsStart.wait(); + } + } + } + } +} diff --git a/sgx-jvm/avian/test/BitsetTest.java b/sgx-jvm/avian/test/BitsetTest.java new file mode 100644 index 0000000000..6609eb60dd --- /dev/null +++ b/sgx-jvm/avian/test/BitsetTest.java @@ -0,0 +1,128 @@ +import java.util.BitSet; + +public class BitsetTest { + + public static void main(String[] args) { + BitSet bits = new BitSet(16); + bits.set(5); + bits.set(1); + + BitSet other = new BitSet(16); + other.set(5); + + assertTrue("bit 1 is set", bits.get(1)); + assertTrue("bit 5 is set", bits.get(5)); + assertTrue("bit 0 is not set", !bits.get(0)); + assertTrue("bit 16 is not set", !bits.get(16)); + assertCardinality(bits, 2); + + bits.and(other); + + assertTrue("bit 5 is set", bits.get(5)); + assertTrue("bit 1 is not set", !bits.get(1)); + assertCardinality(bits, 1); + + bits.set(100); + + assertTrue("bit 100 is set", bits.get(100)); + assertTrue("bit 101 is not set", !bits.get(101)); + assertCardinality(bits, 2); + + other.set(101); + + bits.or(other); + + assertTrue("bit 101 is set", bits.get(101)); + + assertEquals("first bit is 5", 5, bits.nextSetBit(0)); + assertEquals("first bit is 5 from 3", 5, bits.nextSetBit(4)); + assertEquals("first bit is 5 from 5", 5, bits.nextSetBit(5)); + assertEquals("second bit is 100", 100, bits.nextSetBit(6)); + assertEquals("second bit is 100 from 100", 100, bits.nextSetBit(100)); + assertEquals("third bit is 101", 101, bits.nextSetBit(101)); + assertEquals("there is no 4th bit", -1, bits.nextSetBit(102)); + assertCardinality(bits, 3); + + assertEquals("first empty bit is 0", 0, bits.nextClearBit(0)); + assertEquals("after 5, 6 is empty", 6, bits.nextClearBit(5)); + assertEquals("after 100, 102 is empty", 102, bits.nextClearBit(100)); + + testFlip(); + testClear(); + + BitSet expandingSet = new BitSet(); + //should force us to have 3 partitions. + expandingSet.set(128); + } + + private static void testFlip() { + /* simple case */ + BitSet bitset = new BitSet(); + bitset.set(0); + bitset.flip(0, 0); + assertTrue("Should not be flipped with 0 length range", bitset.get(0)); + bitset.flip(0, 1); + assertTrue("Should be false with range of one", !bitset.get(0)); + bitset.flip(0); + assertTrue("Should be true again", bitset.get(0)); + + /* need to grow */ + bitset.flip(1000); + assertTrue("1000 should be true", bitset.get(1000)); + assertTrue("1001 should be false", !bitset.get(1001)); + assertTrue("999 should be false", !bitset.get(999)); + + /* Range over 2 segments */ + bitset.flip(60, 70); + assertTrue("59 should be false", !bitset.get(59)); + for (int i=60; i < 70; ++i) { + assertTrue(i + " should be true", bitset.get(i)); + } + assertTrue("70 should be false", !bitset.get(70)); + } + + private static void testClear() { + BitSet bitset = new BitSet(); + bitset.set(0, 20); + assertCardinality(bitset, 20); + + bitset.clear(1); + assertTrue("bit 1 should be 0", !bitset.get(1)); + assertCardinality(bitset, 19); + + bitset.clear(0, 3); + assertTrue("bit 0 should be 0", !bitset.get(0)); + assertTrue("bit 1 should be 0", !bitset.get(1)); + assertTrue("bit 2 should be 0", !bitset.get(2)); + assertTrue("bit 3 should be 1", bitset.get(3)); + assertCardinality(bitset, 17); + + bitset = new BitSet(70); + bitset.flip(0, 65); + for (int i=0; i < 65; ++i) { + assertTrue("bit " + i + " should be set", bitset.get(i)); + } + assertTrue("bit 65 should not be set", !bitset.get(65)); + } + + static void assertTrue(String msg, boolean flag) { + if (flag) { + System.out.println(msg + " : OK."); + } else { + throw new RuntimeException("Error:"+msg); + } + } + + static void assertEquals(String msg, int expected, int actual) { + if (expected==actual) { + System.out.println(msg + " : OK. ["+actual+']'); + } else { + throw new RuntimeException("Error:"+msg+" expected:"+expected+", actual:"+actual); + } + } + + static void assertCardinality(BitSet set, int expectedCardinality) { + assertEquals("Checking cardinality", expectedCardinality, set.cardinality()); + } + +} diff --git a/sgx-jvm/avian/test/BufferedInputStreamTest.java b/sgx-jvm/avian/test/BufferedInputStreamTest.java new file mode 100644 index 0000000000..ba978cd5ef --- /dev/null +++ b/sgx-jvm/avian/test/BufferedInputStreamTest.java @@ -0,0 +1,80 @@ + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.BufferedInputStream; + +/** + * Checks that BufferedInputStream does not block if data is available in it's internal buffer. + */ +public class BufferedInputStreamTest +{ + public static void main(String[] args) throws IOException + { + MyByteArrayStream in = new MyByteArrayStream(new byte[100]); + + BufferedInputStream bin = new BufferedInputStream(in); + //read a single byte to fill the buffer + int b = bin.read(); + byte[] buf = new byte[10]; + //now try to read 10 bytes. this should a least return the content of the buffer. On OpenJDK this are + //4 bytes (the rest of the buffer returned by MyByteArrayStream in the first call). + //It should definately NOT block. + int count = bin.read(buf); + System.out.println("Read bytes: " + count); + } + + /** + * Internal Stream used to show the BufferedInputStream behaviour. + */ + static class MyByteArrayStream extends ByteArrayInputStream + { + boolean stopReading = false; + + /** + * @param buf + */ + public MyByteArrayStream(byte[] buf) + { + super(buf); + } + + /* (non-Javadoc) + * @see java.io.ByteArrayInputStream#read(byte[], int, int) + */ + @Override + public synchronized int read(byte[] b, int off, int len) + { + if(stopReading == false) + { //On the first call 5 bytes are returned. + stopReading = true; + return super.read(b, off, 5); + } + //on all following calls block. The spec says, that a least one byte is returned, if the + //stream is not at EOF. + while(available() == 0) + { + try + { + Thread.sleep(100); + } + catch (InterruptedException e) + { + } + } + return 0; + } + + /* (non-Javadoc) + * @see java.io.ByteArrayInputStream#available() + */ + @Override + public synchronized int available() + { + if(stopReading) + { + return 0; + } + return super.available(); + } + } +} diff --git a/sgx-jvm/avian/test/Buffers.java b/sgx-jvm/avian/test/Buffers.java new file mode 100644 index 0000000000..63a24ec928 --- /dev/null +++ b/sgx-jvm/avian/test/Buffers.java @@ -0,0 +1,195 @@ +import java.nio.ByteBuffer; +import java.nio.BufferUnderflowException; +import java.nio.BufferOverflowException; +import static avian.testing.Asserts.*; + +public class Buffers { + static { + System.loadLibrary("test"); + } + + private static void testArrays(Factory factory1, Factory factory2) { + final int size = 64; + ByteBuffer b1 = factory1.allocate(size); + ByteBuffer b2 = factory2.allocate(size); + + String s = "1234567890abcdefghijklmnopqrstuvwxyz"; + b1.put(s.getBytes()); + b1.flip(); + byte[] ba = new byte[s.length()]; + b1.get(ba); + assertEquals(s, new String(ba)); + b1.position(0); + b2.put(b1); + b2.flip(); + b2.get(ba); + assertEquals(s, new String(ba)); + b1.position(0); + b2.position(0); + b1.limit(b1.capacity()); + b2.limit(b2.capacity()); + b1.put(s.getBytes(), 4, 5); + b1.flip(); + ba = new byte[5]; + b1.get(ba); + assertEquals(s.substring(4, 9), new String(ba)); + } + + private static void testPrimativeGetAndSet(Factory factory1, Factory factory2) { + { final int size = 64; + ByteBuffer b1 = factory1.allocate(size); + try { + + for (int i = 0; i < size; ++i) + b1.put(i, (byte) 42); + + for (int i = 0; i < size; ++i) + assertEquals(b1.get(i), 42); + + for (int i = 0; i < size/4; i++) + b1.putFloat(i*4, (float) 19.12); + + for (int i = 0; i < size/4; i++) + assertEquals(b1.getFloat(i*4), (float) 19.12); + + ByteBuffer b3 = b1.duplicate(); + for (int i = 0; i < size/4; i++) + assertEquals(b3.getFloat(), (float) 19.12); + assertEquals(64, b3.position()); + + for (int i = 0; i < size/8; i++) + b1.putDouble(i*8, (double) 19.12); + + for (int i = 0; i < size/8; i++) + assertEquals(b1.getDouble(i*8), (double) 19.12); + + b3.position(0); + + for (int i = 0; i < size/8; i++) + assertEquals(b3.getDouble(i*8), (double) 19.12); + + for (int i = 0; i < size / 2; ++i) + b1.putShort(i * 2, (short) -12345); + + for (int i = 0; i < size / 2; ++i) + assertEquals(b1.getShort(i * 2), -12345); + + for (int i = 0; i < size / 4; ++i) + b1.putInt(i * 4, 0x12345678); + + for (int i = 0; i < size / 4; ++i) + assertEquals(b1.getInt(i * 4), 0x12345678); + + for (int i = 0; i < size / 8; ++i) + b1.putLong(i * 8, 0x1234567890ABCDEFL); + + for (int i = 0; i < size / 8; ++i) + assertEquals(b1.getLong(i * 8), 0x1234567890ABCDEFL); + + ByteBuffer b2 = factory2.allocate(size); + try { + b2.put(b1); + + for (int i = 0; i < size / 8; ++i) + assertTrue(b2.getLong(i * 8) == 0x1234567890ABCDEFL); + + } finally { + factory2.dispose(b2); + } + } finally { + factory1.dispose(b1); + } + } + } + + private static native ByteBuffer allocateNative(int capacity); + + private static native void freeNative(ByteBuffer b); + + public static void main(String[] args) throws Exception { + Factory array = new Factory() { + public ByteBuffer allocate(int capacity) { + return ByteBuffer.allocate(capacity); + } + + public void dispose(ByteBuffer b) { + // ignore + } + }; + + Factory direct = new Factory() { + public ByteBuffer allocate(int capacity) { + return ByteBuffer.allocateDirect(capacity); + } + + public void dispose(ByteBuffer b) { + // ignore + } + }; + + Factory native_ = new Factory() { + public ByteBuffer allocate(int capacity) { + return allocateNative(capacity); + } + + public void dispose(ByteBuffer b) { + freeNative(b); + } + }; + + testPrimativeGetAndSet(array, array); + testArrays(array, array); + testPrimativeGetAndSet(array, direct); + testArrays(array, direct); + testPrimativeGetAndSet(array, native_); + testArrays(array, native_); + + testPrimativeGetAndSet(direct, array); + testArrays(direct, array); + testPrimativeGetAndSet(direct, direct); + testArrays(direct, direct); + testPrimativeGetAndSet(direct, native_); + testArrays(direct, native_); + + testPrimativeGetAndSet(native_, array); + testArrays(native_, array); + testPrimativeGetAndSet(native_, direct); + testArrays(native_, direct); + testPrimativeGetAndSet(native_, native_); + testArrays(native_, native_); + + try { + ByteBuffer.allocate(1).getInt(); + assertTrue(false); + } catch (BufferUnderflowException e) { + // cool + } + + try { + ByteBuffer.allocate(1).getInt(0); + assertTrue(false); + } catch (IndexOutOfBoundsException e) { + // cool + } + + try { + ByteBuffer.allocate(1).putInt(1); + assertTrue(false); + } catch (BufferOverflowException e) { + // cool + } + + try { + ByteBuffer.allocate(1).putInt(0, 1); + assertTrue(false); + } catch (IndexOutOfBoundsException e) { + // cool + } + } + + private interface Factory { + public ByteBuffer allocate(int capacity); + + public void dispose(ByteBuffer b); + } +} diff --git a/sgx-jvm/avian/test/Busy.java b/sgx-jvm/avian/test/Busy.java new file mode 100644 index 0000000000..e301896416 --- /dev/null +++ b/sgx-jvm/avian/test/Busy.java @@ -0,0 +1,25 @@ +public class Busy { + private static volatile int foo = 0; + private static volatile boolean go; + + public static void main(String[] args) { + final Object lock = new Object(); + + synchronized (lock) { + new Thread() { + public void run() { + while (foo < 100) { + go = true; + } + } + }.start(); + + while (foo < 100) { + while (! go) { } + go = false; + byte[] array = new byte[256 * 1024]; + ++ foo; + } + } + } +} \ No newline at end of file diff --git a/sgx-jvm/avian/test/Collections.java b/sgx-jvm/avian/test/Collections.java new file mode 100644 index 0000000000..d778267dde --- /dev/null +++ b/sgx-jvm/avian/test/Collections.java @@ -0,0 +1,68 @@ +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public class Collections { + public static void main(String[] args) { + testValues(); + testSort(); + } + + @SuppressWarnings("rawtypes") + private static void testValues() { + Map testMap = java.util.Collections.unmodifiableMap(java.util.Collections.emptyMap()); + Collection values = testMap.values(); + + if (values == null) { + throw new NullPointerException(); + } + + try { + values.clear(); + + throw new IllegalStateException("Object should be immutable, exception should have thrown"); + } catch (Exception e) { + // expected + } + } + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static > void expectSorted(List list) { + for (int i = 1; i < list.size(); ++i) { + expect(list.get(i - 1).compareTo(list.get(i)) <= 0); + } + } + + private static int pseudoRandom(int seed) { + return 3170425 * seed + 132102; + } + + private static > int shuffle(List list, int seed) { + for (int i = list.size(); i > 1; --i) { + int i2 = (seed < 0 ? -seed : seed) % i; + T value = list.get(i - 1); + list.set(i - 1, list.get(i2)); + list.set(i2, value); + seed = pseudoRandom(seed); + } + return seed; + } + + public static void testSort() { + List list = new ArrayList(); + for (int i = 0; i < 64; ++i) { + list.add(Integer.valueOf(i + 1)); + } + ; + int random = 12345; + for (int i = 0; i < 32; ++i) { + random = shuffle(list, random); + java.util.Collections.sort(list); + expectSorted(list); + } + } +} diff --git a/sgx-jvm/avian/test/CompletionServiceTest.java b/sgx-jvm/avian/test/CompletionServiceTest.java new file mode 100644 index 0000000000..975f632855 --- /dev/null +++ b/sgx-jvm/avian/test/CompletionServiceTest.java @@ -0,0 +1,54 @@ +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorCompletionService; +import java.util.concurrent.TimeUnit; + +public class CompletionServiceTest { + public static void main(String args[]) throws InterruptedException, ExecutionException { + Executor dumbExecutor = new Executor() { + @Override + public void execute(Runnable task) { + new Thread(task).start(); + } + }; + + pollNoResultTest(dumbExecutor); + pollTimeoutNoResultTest(dumbExecutor); + takeTest(dumbExecutor); + } + + private static void verify(boolean val) { + if (! val) { + throw new RuntimeException(); + } + } + + private static void pollNoResultTest(Executor executor) { + ExecutorCompletionService ecs = new ExecutorCompletionService(executor); + + verify(ecs.poll() == null); + } + + private static void pollTimeoutNoResultTest(Executor executor) throws InterruptedException { + long delayTime = 0; + ExecutorCompletionService ecs = new ExecutorCompletionService(executor); + + long startTime = System.currentTimeMillis(); + verify(ecs.poll(delayTime, TimeUnit.MILLISECONDS) == null); + verify(System.currentTimeMillis() - startTime >= delayTime); + } + + private static void takeTest(Executor executor) throws InterruptedException, ExecutionException { + ExecutorCompletionService ecs = new ExecutorCompletionService(executor); + final Object result = new Object(); + ecs.submit(new Callable() { + @Override + public Object call() throws Exception { + return result; + } + }); + + verify(ecs.take().get() == result); + } +} diff --git a/sgx-jvm/avian/test/ConcurrentHashMapTest.java b/sgx-jvm/avian/test/ConcurrentHashMapTest.java new file mode 100644 index 0000000000..f0d271f412 --- /dev/null +++ b/sgx-jvm/avian/test/ConcurrentHashMapTest.java @@ -0,0 +1,166 @@ +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ConcurrentHashMap; + +public class ConcurrentHashMapTest { + private static final int ThreadCount = 4; + private static final int IterationCount = 100; + private static final int Range = 10; + private static final int CommonBase = -Range; + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + public static void main(String[] args) throws Throwable { + final ConcurrentMap map = new ConcurrentHashMap(); + final int[] counter = new int[1]; + final int[] step = new int[1]; + final Throwable[] exception = new Throwable[1]; + + synchronized (map) { + for (int i = 0; i < ThreadCount; ++i) { + final int index = i; + new Thread() { + public void run() { + try { + synchronized (map) { + ++ counter[0]; + map.notifyAll(); + while (exception[0] == null && step[0] == 0) { + map.wait(); + } + } + + for (int i = 0; i < IterationCount; ++i) { + populateCommon(map); + populate(map, index * Range); + } + + synchronized (map) { + -- counter[0]; + map.notifyAll(); + while (exception[0] == null && step[0] == 1) { + map.wait(); + } + } + + for (int i = 0; i < IterationCount; ++i) { + populate(map, index * Range); + depopulate(map, index * Range); + } + + synchronized (map) { + ++ counter[0]; + map.notifyAll(); + } + } catch (Throwable e) { + synchronized (map) { + exception[0] = e; + map.notifyAll(); + } + e.printStackTrace(); + } + } + }.start(); + } + + try { + while (exception[0] == null && counter[0] < ThreadCount) { + map.wait(); + } + + step[0] = 1; + map.notifyAll(); + + while (exception[0] == null && counter[0] > 0) { + map.wait(); + } + + if (map.size() != ThreadCount * Range) { + System.err.println + ("expected " + (ThreadCount * Range) + " got " + map.size()); + } + expect(map.size() == ThreadCount * Range); + for (int i = CommonBase, j = CommonBase + Range; i < j; ++i) { + expect(! map.containsKey(i)); + } + + step[0] = 2; + map.notifyAll(); + + while (exception[0] == null && counter[0] < ThreadCount) { + map.wait(); + } + + expect(map.isEmpty()); + expect(exception[0] == null); + } catch (Throwable e) { + exception[0] = e; + throw e; + } finally { + map.notifyAll(); + } + } + } + + private static void populateCommon(ConcurrentMap map) { + Object value = new Object(); + for (int i = CommonBase, j = CommonBase + Range; i < j; ++i) { + map.remove(i); + map.put(i, value); + map.remove(i); + } + } + + private static void populate(ConcurrentMap map, int base) { + for (int i = base, j = base + Range; i < j; ++i) { + map.remove(i); + Object value = new Object(); + expect(map.put(i, value) == null); + expect(map.containsKey(i)); + expect(map.get(i).equals(value)); + expect(map.putIfAbsent(i, new Object()) == value); + expect(map.get(i).equals(value)); + expect(! map.remove(i, new Object())); + expect(map.remove(i, value)); + expect(map.replace(i, value) == null); + expect(! map.containsKey(i)); + expect(map.get(i) == null); + expect(map.putIfAbsent(i, value) == null); + expect(map.containsKey(i)); + expect(map.get(i) == value); + Object newValue = new Object(); + expect(map.replace(i, newValue) == value); + expect(map.get(i) == newValue); + + boolean found = false; + for (Iterator> it = map.entrySet().iterator(); + it.hasNext();) + { + Map.Entry e = it.next(); + if (e.getKey() == i) { + expect(! found); + expect(e.getValue() == newValue); + found = true; + it.remove(); + } + } + + expect(found); + expect(! map.containsKey(i)); + expect(map.putIfAbsent(i, value) == null); + expect(map.containsKey(i)); + expect(map.get(i) == value); + } + } + + private static void depopulate(ConcurrentMap map, int base) + { + for (int i = base, j = base + Range; i < j; ++i) { + expect(map.containsKey(i)); + expect(map.remove(i) != null); + } + } +} diff --git a/sgx-jvm/avian/test/Datagrams.java b/sgx-jvm/avian/test/Datagrams.java new file mode 100644 index 0000000000..d5dcd88d07 --- /dev/null +++ b/sgx-jvm/avian/test/Datagrams.java @@ -0,0 +1,100 @@ +import java.net.SocketAddress; +import java.net.InetSocketAddress; +import java.net.ProtocolFamily; +import java.net.StandardProtocolFamily; +import java.nio.ByteBuffer; +import java.nio.channels.DatagramChannel; +import java.nio.channels.Selector; +import java.nio.channels.SelectionKey; + +public class Datagrams { + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static boolean equal(byte[] a, int aOffset, byte[] b, int bOffset, + int length) + { + for (int i = 0; i < length; ++i) { + if (a[aOffset + i] != b[bOffset + i]) return false; + } + return true; + } + + public static void main(String[] args) throws Exception { + test(true); + test(false); + } + + private static void test(boolean send) throws Exception { + final String Hostname = "localhost"; + final int InPort = 22043; + final int OutPort = 22044; + final SocketAddress InAddress = new InetSocketAddress(Hostname, InPort); + final SocketAddress OutAddress = new InetSocketAddress(Hostname, OutPort); + final byte[] Message = "hello, world!".getBytes(); + + DatagramChannel out = DatagramChannel.open(); + try { + out.configureBlocking(false); + out.socket().bind(OutAddress); + if (! send) out.connect(InAddress); + + DatagramChannel in = DatagramChannel.open(); + try { + in.configureBlocking(false); + in.socket().bind(InAddress); + + Selector selector = Selector.open(); + try { + SelectionKey outKey = out.register + (selector, SelectionKey.OP_WRITE, null); + + SelectionKey inKey = in.register + (selector, SelectionKey.OP_READ, null); + + int state = 0; + ByteBuffer inBuffer = ByteBuffer.allocate(Message.length); + loop: while (true) { + selector.select(); + + switch (state) { + case 0: { + if (outKey.isWritable()) { + if (send) { + out.send(ByteBuffer.wrap(Message), InAddress); + } else { + out.write(ByteBuffer.wrap(Message)); + } + state = 1; + } + } break; + + case 1: { + if (inKey.isReadable()) { + expect(in.receive(inBuffer).equals(OutAddress)); + if (! inBuffer.hasRemaining()) { + expect(equal(inBuffer.array(), + inBuffer.arrayOffset(), + Message, + 0, + Message.length)); + break loop; + } + } + } break; + + default: throw new RuntimeException(); + } + } + } finally { + selector.close(); + } + } finally { + in.close(); + } + } finally { + out.close(); + } + } +} diff --git a/sgx-jvm/avian/test/Dates.java b/sgx-jvm/avian/test/Dates.java new file mode 100644 index 0000000000..dfd6d72273 --- /dev/null +++ b/sgx-jvm/avian/test/Dates.java @@ -0,0 +1,27 @@ +import java.text.FieldPosition; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +public class Dates { + private final static long EPOCH = 1234567890; + private final static String TEXT = "2009-02-13T23:31:30"; + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + public static void main(String[] args) throws Exception { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + format.setTimeZone(TimeZone.getTimeZone("GMT")); + Date date = format.parse("1970-01-01T00:00:00"); + expect(0 == date.getTime()); + + date = new Date(EPOCH * 1000l); + String actual = format.format(date, new StringBuffer(), new FieldPosition(0)).toString(); + expect(TEXT.equals(actual)); + + date = format.parse(TEXT); + expect(EPOCH == date.getTime() / 1000l); + } +} diff --git a/sgx-jvm/avian/test/DefineClass.java b/sgx-jvm/avian/test/DefineClass.java new file mode 100644 index 0000000000..4cff3ecfde --- /dev/null +++ b/sgx-jvm/avian/test/DefineClass.java @@ -0,0 +1,86 @@ +import java.io.IOException; +import java.io.File; +import java.io.FileInputStream; + +public class DefineClass { + private static File findClass(String name, File directory) { + for (File file: directory.listFiles()) { + if (file.isFile()) { + if (file.getName().equals(name + ".class")) { + return file; + } + } else if (file.isDirectory()) { + File result = findClass(name, file); + if (result != null) { + return result; + } + } + } + return null; + } + + private static byte[] read(File file) throws IOException { + byte[] bytes = new byte[(int) file.length()]; + FileInputStream in = new FileInputStream(file); + try { + if (in.read(bytes) != (int) file.length()) { + throw new RuntimeException(); + } + return bytes; + } finally { + in.close(); + } + } + + private static Class loadClass(String name) throws Exception { + return new MyClassLoader(DefineClass.class.getClassLoader()).defineClass + (name, read(findClass(name, new File(System.getProperty("user.dir"))))); + } + + private static void testStatic() throws Exception { + loadClass("DefineClass$Hello") + .getMethod("main", String[].class).invoke(null, (Object) new String[0]); + } + + private static void testDerived() throws Exception { + System.out.println + (String.valueOf + (((Base) loadClass("DefineClass$Derived").newInstance()).zip())); + } + + public static void main(String[] args) throws Exception { + testStatic(); + testDerived(); + } + + private static class MyClassLoader extends ClassLoader { + public MyClassLoader(ClassLoader parent) { + super(parent); + } + + public Class defineClass(String name, byte[] bytes) { + return defineClass(name, bytes, 0, bytes.length); + } + } + + public static class Hello { + public static void main(String[] args) { + System.out.println("hello, world!"); + } + } + + public abstract static class Base { + public int foo; + public int[] array; + + public void bar() { } + + public abstract int zip(); + } + + public static class Derived extends Base { + public int zip() { + return 42; + } + } +} diff --git a/sgx-jvm/avian/test/DequeHelper.java b/sgx-jvm/avian/test/DequeHelper.java new file mode 100644 index 0000000000..4d19041a6b --- /dev/null +++ b/sgx-jvm/avian/test/DequeHelper.java @@ -0,0 +1,55 @@ +import java.util.Deque; + +public class DequeHelper { + private static void verify(boolean val) { + if (! val) { + throw new RuntimeException(); + } + } + + public static void main(String args[]) { + // prevents unit test failure + } + + public static void addFirstTest(Deque q) { + Object firstObject = new Object(); + Object lastObject = new Object(); + q.addFirst(lastObject); + q.addFirst(firstObject); + + verify(q.size() == 2); + verify(q.peekFirst() == firstObject); + verify(q.peekLast() == lastObject); + } + + public static void addLastTest(Deque q) { + Object firstObject = new Object(); + Object lastObject = new Object(); + q.addLast(firstObject); + q.addLast(lastObject); + + verify(q.size() == 2); + verify(q.peekFirst() == firstObject); + verify(q.peekLast() == lastObject); + } + + public static void removeFirstTest(Deque q) { + Object firstObject = new Object(); + Object lastObject = new Object(); + q.addLast(firstObject); + q.addLast(lastObject); + + verify(q.removeFirst() == firstObject); + verify(q.removeFirst() == lastObject); + } + + public static void removeLastTest(Deque q) { + Object firstObject = new Object(); + Object lastObject = new Object(); + q.addLast(firstObject); + q.addLast(lastObject); + + verify(q.removeLast() == lastObject); + verify(q.removeLast() == firstObject); + } +} diff --git a/sgx-jvm/avian/test/DivideByZero.java b/sgx-jvm/avian/test/DivideByZero.java new file mode 100644 index 0000000000..80f308adcb --- /dev/null +++ b/sgx-jvm/avian/test/DivideByZero.java @@ -0,0 +1,135 @@ +public class DivideByZero { + private static int divide(int n, int d) { + return n / d; + } + + private static int modulo(int n, int d) { + return n % d; + } + + private static long divide(long n, long d) { + return n / d; + } + + private static long modulo(long n, long d) { + return n % d; + } + + private static float divide(float n, float d) { + return n / d; + } + + private static float modulo(float n, float d) { + return n % d; + } + + private static double divide(double n, double d) { + return n / d; + } + + private static double modulo(double n, double d) { + return n % d; + } + + public static void main(String[] args) { + try { + int x = 1 / 0; + throw new RuntimeException(); + } catch (ArithmeticException e) { + e.printStackTrace(); + } + + try { + int x = 1 % 0; + throw new RuntimeException(); + } catch (ArithmeticException e) { + e.printStackTrace(); + } + + try { + int y = 2; + int x = y / 0; + throw new RuntimeException(); + } catch (ArithmeticException e) { + e.printStackTrace(); + } + + try { + int y = 2; + int x = y % 0; + throw new RuntimeException(); + } catch (ArithmeticException e) { + e.printStackTrace(); + } + + try { + int z = 0; + int y = 2; + int x = y / z; + throw new RuntimeException(); + } catch (ArithmeticException e) { + e.printStackTrace(); + } + + try { + int z = 0; + int y = 2; + int x = y % z; + throw new RuntimeException(); + } catch (ArithmeticException e) { + e.printStackTrace(); + } + + try { + long z = 0; + long y = 2; + long x = y / z; + throw new RuntimeException(); + } catch (ArithmeticException e) { + e.printStackTrace(); + } + + try { + long z = 0; + long y = 2; + long x = y % z; + throw new RuntimeException(); + } catch (ArithmeticException e) { + e.printStackTrace(); + } + + try { + divide(5, 0); + throw new RuntimeException(); + } catch (ArithmeticException e) { + e.printStackTrace(); + } + + try { + modulo(6, 0); + throw new RuntimeException(); + } catch (ArithmeticException e) { + e.printStackTrace(); + } + + try { + divide(5L, 0L); + throw new RuntimeException(); + } catch (ArithmeticException e) { + e.printStackTrace(); + } + + try { + modulo(6L, 0L); + throw new RuntimeException(); + } catch (ArithmeticException e) { + e.printStackTrace(); + } + + divide(5F, 0F); + modulo(6F, 0F); + + divide(5D, 0D); + modulo(6D, 0D); + } +} diff --git a/sgx-jvm/avian/test/EnumSetTest.java b/sgx-jvm/avian/test/EnumSetTest.java new file mode 100644 index 0000000000..3b1317b7ec --- /dev/null +++ b/sgx-jvm/avian/test/EnumSetTest.java @@ -0,0 +1,107 @@ +import java.util.EnumSet; +import java.util.Iterator; +import java.util.NoSuchElementException; + +public class EnumSetTest { + private enum SmallEnum { + ONE, + TWO, + THREE + } + + private enum LargerEnum { + LARGEONE, + LARGETWO, + LARGETHREE, + LARGEFOUR, + LARGEFIVE, + LARGESIX + } + + public static void main(String[] args) { + testAllOf(); + testNoneOf(); + testIterators(); + testOf(); + testCopyOf(); + testComplimentOf(); + } + + private static void testComplimentOf() { + EnumSet one = EnumSet.of(SmallEnum.ONE, SmallEnum.THREE); + EnumSet two = EnumSet.complementOf(one); + assertElementInSet(SmallEnum.TWO, two); + assertSize(1, two); + } + + private static void testCopyOf() { + EnumSet one = EnumSet.of(SmallEnum.ONE, SmallEnum.THREE); + EnumSet two = EnumSet.copyOf(one); + assertElementInSet(SmallEnum.ONE, two); + assertElementInSet(SmallEnum.THREE, two); + assertSize(2, two); + } + + private static void testOf() { + EnumSet set = EnumSet.of(LargerEnum.LARGEONE, LargerEnum.LARGEFIVE, LargerEnum.LARGETWO); + assertElementInSet(LargerEnum.LARGEONE, set); + assertElementInSet(LargerEnum.LARGEFIVE, set); + assertElementInSet(LargerEnum.LARGETWO, set); + assertSize(3, set); + } + + private static void testAllOf() { + EnumSet set = EnumSet.allOf(SmallEnum.class); + for (SmallEnum current : SmallEnum.values()) { + assertElementInSet(current, set); + } + assertSize(3, set); + } + + private static void testNoneOf() { + EnumSet set = EnumSet.noneOf(SmallEnum.class); + assertSize(0, set); + } + + private static void testIterators() { + EnumSet set = EnumSet.allOf(SmallEnum.class); + Iterator iterator = set.iterator(); + boolean exceptionCaught = false; + try { + iterator.remove(); + } catch (IllegalStateException e) { + exceptionCaught = true; + } + if (!exceptionCaught) { + throw new RuntimeException("Calling remove() before next() should throw IllegalStateException"); + } + + while (iterator.hasNext()) { + iterator.next(); + iterator.remove(); + } + assertSize(0, set); + + exceptionCaught = false; + try { + iterator.next(); + } catch (NoSuchElementException e) { + exceptionCaught = true; + } + if (!exceptionCaught) { + throw new RuntimeException("Calling next() when hasNext() == false should throw NoSuchElementException"); + } + } + + private static void assertElementInSet(Enum element, EnumSet set) { + if (!set.contains(element)) { + throw new RuntimeException("expected " + element + " in the set!"); + } + } + + private static void assertSize(int expectedSize, EnumSet set) { + if (set.size() != expectedSize) { + throw new RuntimeException("expected the set to be size=" + expectedSize + ", actual=" + set.size()); + } + } +} diff --git a/sgx-jvm/avian/test/Enums.java b/sgx-jvm/avian/test/Enums.java new file mode 100644 index 0000000000..9cb397fbc3 --- /dev/null +++ b/sgx-jvm/avian/test/Enums.java @@ -0,0 +1,39 @@ +public class Enums { + private enum Suit { CLUBS, HEARTS, SPADES, DIAMONDS }; + private enum Rank { ACE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, + NINE, TEN, JACK, QUEEN, KING }; + private enum Person { Joe(4), Mike(5) ; + private final int age; + private Person(int age) { + this.age = age; + } + public int getAge() { + return age; + } + }; + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static boolean checkFaceCard(Rank r) { + switch (r) { + case ACE: + case JACK: + case QUEEN: + case KING: + return true; + } + return false; + } + + public static void main(String[] args) { + expect(Suit.CLUBS.ordinal() == 0); + expect(Suit.valueOf("DIAMONDS") == Suit.DIAMONDS); + System.out.println(Suit.SPADES); + expect(Suit.values()[1] == Suit.HEARTS); + expect(!checkFaceCard(Rank.FIVE)); + expect(checkFaceCard(Rank.KING)); + expect(Person.Mike.getAge() == 5); + } +} diff --git a/sgx-jvm/avian/test/Exceptions.java b/sgx-jvm/avian/test/Exceptions.java new file mode 100644 index 0000000000..15f1c99c1d --- /dev/null +++ b/sgx-jvm/avian/test/Exceptions.java @@ -0,0 +1,63 @@ +public class Exceptions { + static class ThrowError { + static { + if (true) throw new AssertionError(); + } + + static void foo() { } + } + + static class ThrowException { + static { + if (true) throw new RuntimeException(); + } + + static void foo() { } + } + + private static void evenMoreDangerous() { + throw new RuntimeException("chaos! panic! overwhelming anxiety!"); + } + + private static void moreDangerous() { + evenMoreDangerous(); + } + + private static void dangerous() { + moreDangerous(); + } + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + public static void main(String[] args) { + boolean threw = false; + try { + dangerous(); + } catch (Exception e) { + e.printStackTrace(); + threw = true; + } + expect(threw); + threw = false; + + try { + ThrowError.foo(); + } catch (AssertionError e) { + e.printStackTrace(); + threw = true; + } + expect(threw); + threw = false; + + try { + ThrowException.foo(); + } catch (ExceptionInInitializerError e) { + e.printStackTrace(); + threw = true; + } + expect(threw); + } + +} diff --git a/sgx-jvm/avian/test/FileOutput.java b/sgx-jvm/avian/test/FileOutput.java new file mode 100644 index 0000000000..539b73fc59 --- /dev/null +++ b/sgx-jvm/avian/test/FileOutput.java @@ -0,0 +1,47 @@ +import java.io.FileOutputStream; +import java.io.FileInputStream; +import java.io.File; +import java.io.IOException; + +public class FileOutput { + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static void test(boolean appendFirst) throws IOException { + try { + FileOutputStream f = new FileOutputStream("test.txt", appendFirst); + f.write("Hello world!\n".getBytes()); + f.close(); + + FileOutputStream f2 = new FileOutputStream("test.txt", true); + f2.write("Hello world again!".getBytes()); + f2.close(); + + FileInputStream in = new FileInputStream("test.txt"); + byte[] buffer = new byte[256]; + int c; + int offset = 0; + while ((c = in.read(buffer, offset, buffer.length - offset)) != -1) { + offset += c; + } + in.close(); + + if (! "Hello world!\nHello world again!".equals + (new String(buffer, 0, offset))) + { + throw new RuntimeException(); + } + } finally { + expect(new File("test.txt").delete()); + } + } + + public static void main(String[] args) throws IOException { + expect(new File("nonexistent-file").length() == 0); + + test(false); + test(true); + } + +} diff --git a/sgx-jvm/avian/test/Files.java b/sgx-jvm/avian/test/Files.java new file mode 100644 index 0000000000..21bd5cf07c --- /dev/null +++ b/sgx-jvm/avian/test/Files.java @@ -0,0 +1,100 @@ +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; + +public class Files { + private static final boolean IsWindows + = System.getProperty("os.name").equals("Windows"); + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static void isAbsoluteTest(boolean absolutePath) { + File file = new File("test.txt"); + if (absolutePath) { + file = file.getAbsoluteFile(); + } + + boolean isAbsolute = file.isAbsolute(); + + if (absolutePath) { + expect(isAbsolute); + } else { + expect(!isAbsolute); + } + + } + + private static void setExecutableTestWithPermissions(boolean executable) + throws Exception + { + File file = File.createTempFile("avian.", null); + try { + file.setExecutable(executable); + if (executable) { + expect(file.canExecute()); + } else { + // Commented out because this will fail on Windows - both on Avian and on OpenJDK + // The implementation for Windows considers canExecute() to be the same as canRead() + // expect(!file.canExecute()); + } + } finally { + expect(file.delete()); + } + } + + public static void main(String[] args) throws Exception { + isAbsoluteTest(true); + isAbsoluteTest(false); + setExecutableTestWithPermissions(true); + setExecutableTestWithPermissions(false); + + { File f = new File("test.txt"); + f.createNewFile(); + expect(! f.createNewFile()); + f.delete(); + } + + { File f = new File("test.txt"); + FileOutputStream out = new FileOutputStream(f); + try { + byte[] message = "hello, world!\n".getBytes(); + out.write(message); + out.close(); + + expect(f.lastModified() > 0); + + FileInputStream in = new FileInputStream(f); + try { + expect(in.available() == message.length); + + for (int i = 0; i < message.length; ++i) { + in.read(); + expect(in.available() == message.length - i - 1); + } + + expect(in.read() == -1); + expect(in.available() == 0); + } finally { + in.close(); + } + } finally { + f.delete(); + } + } + + if(IsWindows) { + expect(new File("/c:\\test").getPath().equals("c:\\test")); + } else { + expect(new File("/c:\\test").getPath().equals("/c:\\test")); + } + + expect(new File("foo/bar").getParent().equals("foo")); + expect(new File("foo/bar/").getParent().equals("foo")); + expect(new File("foo/bar//").getParent().equals("foo")); + + expect(new File("foo/nonexistent-directory").listFiles() == null); + } + +} diff --git a/sgx-jvm/avian/test/Finalizers.java b/sgx-jvm/avian/test/Finalizers.java new file mode 100644 index 0000000000..220a32ba4c --- /dev/null +++ b/sgx-jvm/avian/test/Finalizers.java @@ -0,0 +1,44 @@ +public class Finalizers { + private static final Object lock = new Object(); + private static boolean finalized = false; + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + protected void finalize() { + synchronized (lock) { + finalized = true; + lock.notifyAll(); + } + } + + public static void main(String[] args) throws Exception { + new Finalizers(); + + expect(! finalized); + + synchronized (lock) { + System.gc(); + lock.wait(5000); + } + + expect(finalized); + + new Finalizers2(); + + finalized = false; + + expect(! finalized); + + synchronized (lock) { + System.gc(); + lock.wait(5000); + } + + expect(finalized); + } + + private static class Finalizers2 extends Finalizers { } + +} diff --git a/sgx-jvm/avian/test/Floats.java b/sgx-jvm/avian/test/Floats.java new file mode 100644 index 0000000000..eca59b1bc9 --- /dev/null +++ b/sgx-jvm/avian/test/Floats.java @@ -0,0 +1,351 @@ +public class Floats { + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static double multiply(double a, double b) { + return a * b; + } + + private static float multiply(float a, float b) { + return a * b; + } + + private static double divide(double a, double b) { + return a / b; + } + + private static double subtract(double a, double b) { + return a - b; + } + + private double field = 100d; + + private static int doubleToInt(Floats f) { + return (int) f.field; + } + + private static void multiplyAndStore(double a, double b, Floats f) { + f.field = a * b; + } + + private static double loadAndMultiply(double a, Floats f) { + return f.field * a; + } + + private static void subdivide(double src[], int srcoff, + double left[], int leftoff, + double right[], int rightoff) + { + double x1 = src[srcoff + 0]; + double y1 = src[srcoff + 1]; + double ctrlx1 = src[srcoff + 2]; + double ctrly1 = src[srcoff + 3]; + double ctrlx2 = src[srcoff + 4]; + double ctrly2 = src[srcoff + 5]; + double x2 = src[srcoff + 6]; + double y2 = src[srcoff + 7]; + if (left != null) { + left[leftoff + 0] = x1; + left[leftoff + 1] = y1; + } + if (right != null) { + right[rightoff + 6] = x2; + right[rightoff + 7] = y2; + } + x1 = (x1 + ctrlx1) / 2.0; + y1 = (y1 + ctrly1) / 2.0; + x2 = (x2 + ctrlx2) / 2.0; + y2 = (y2 + ctrly2) / 2.0; + double centerx = (ctrlx1 + ctrlx2) / 2.0; + double centery = (ctrly1 + ctrly2) / 2.0; + ctrlx1 = (x1 + centerx) / 2.0; + ctrly1 = (y1 + centery) / 2.0; + ctrlx2 = (x2 + centerx) / 2.0; + ctrly2 = (y2 + centery) / 2.0; + centerx = (ctrlx1 + ctrlx2) / 2.0; + centery = (ctrly1 + ctrly2) / 2.0; + if (left != null) { + left[leftoff + 2] = x1; + left[leftoff + 3] = y1; + left[leftoff + 4] = ctrlx1; + left[leftoff + 5] = ctrly1; + left[leftoff + 6] = centerx; + left[leftoff + 7] = centery; + } + if (right != null) { + right[rightoff + 0] = centerx; + right[rightoff + 1] = centery; + right[rightoff + 2] = ctrlx2; + right[rightoff + 3] = ctrly2; + right[rightoff + 4] = x2; + right[rightoff + 5] = y2; + } + } + + public static class Rectangle { + public double x; + public double y; + public double width; + public double height; + + public void setX(double x) { + this.x = x; + } + } + + public static void main(String[] args) throws Exception { + expect(new Double(42.0) == 42.0); + + { Rectangle r = new Rectangle(); + Rectangle.class.getMethod("setX", double.class).invoke(r, 42.0); + expect(r.x == 42.0); + } + + { double input[] = new double[8]; + double left[] = new double[8]; + double right[] = new double[8]; + + input[0] = 732.0; + input[1] = 952.0; + input[2] = 761.0; + input[3] = 942.0; + input[4] = 786.0; + input[5] = 944.0; + input[6] = 813.0; + input[7] = 939.0; + + subdivide(input, 0, left, 0, right, 0); + + expect(left[0] == 732.0); + expect(left[1] == 952.0); + expect(left[2] == 746.5); + expect(left[3] == 947.0); + expect(left[4] == 760.0); + expect(left[5] == 945.0); + expect(left[6] == 773.25); + expect(left[7] == 943.625); + + expect(right[0] == 773.25); + expect(right[1] == 943.625); + expect(right[2] == 786.5); + expect(right[3] == 942.25); + expect(right[4] == 799.5); + expect(right[5] == 941.5); + expect(right[6] == 813.0); + expect(right[7] == 939.0); + } + + expect(multiply(0.5d, 0.5d) == 0.25d); + expect(multiply(0.5f, 0.5f) == 0.25f); + + expect(multiply(0.5d, 0.1d) == 0.05d); + expect(multiply(0.5f, 0.1f) == 0.05f); + + expect(multiply(0.5d, 0.5d) < 0.5d); + expect(multiply(0.5f, 0.5f) < 0.5f); + + expect(multiply(0.5d, 0.1d) < 0.5d); + expect(multiply(0.5f, 0.1f) < 0.5f); + + expect(multiply(0.5d, 0.5d) > 0.1d); + expect(multiply(0.5f, 0.5f) > 0.1f); + + expect(multiply(0.5d, 0.1d) > 0.01d); + expect(multiply(0.5f, 0.1f) > 0.01f); + + expect(divide(0.5d, 0.5d) == 1.0d); + + expect(divide(0.5d, 0.1d) == 5.0d); + + expect(subtract(0.5d, 0.5d) == 0.0d); + + expect(subtract(0.5d, 0.1d) == 0.4d); + + { double d = 1d; + expect(((int) d) == 1); + } + + { double d = 12345d; + expect(((int) d) == 12345); + } + + expect(doubleToInt(new Floats()) == 100); + + { Floats f = new Floats(); + f.field = 32.0d; + expect(loadAndMultiply(2.0d, f) == 64.0d); + } + + { Floats f = new Floats(); + f.field = 32.0d; + expect(multiply(2.0d, f.field) == 64.0d); + } + + { Floats f = new Floats(); + multiplyAndStore(32.0d, 0.5d, f); + expect(f.field == 16.0d); + } + + { float f = 1f; + expect(((int) f) == 1); + } + + { float f = 1f; + expect(((long) f) == 1); + } + + expect(Math.round(0.4f) == 0); + expect(Math.round(0.5f) == 1); + expect(Math.round(1.0f) == 1); + expect(Math.round(1.9f) == 2); + + expect(Math.round(0.4d) == 0); + expect(Math.round(0.5d) == 1); + expect(Math.round(1.0d) == 1); + expect(Math.round(1.9d) == 2); + + { float b = 1.0f; + int blue = (int)(b * 255 + 0.5); + expect(blue == 255); + } + + { long z = 6553311036568663L; + double d = (double) z; + expect(d == 6553311036568663.0); + } + + { long z = 12345L; + float f = (float) z; + expect(f == 12345.0); + } + + { int z = 12345; + float f = (float) z; + expect(f == 12345.0); + } + + { int z = 12345; + double d = (double) z; + expect(d == 12345.0); + } + + // Test floatToIntBits + { + int orig = 0x7f800001; + float NaN = Float.intBitsToFloat(orig); + int result = Float.floatToIntBits(NaN); + int expected = 0x7fc00000; + expect(result == expected); + } + + { + int orig = 0x7f801001; + float NaN = Float.intBitsToFloat(orig); + int result = Float.floatToIntBits(NaN); + int expected = 0x7fc00000; + expect(result == expected); + } + + { + int orig = 0x00800001; + float number = Float.intBitsToFloat(orig); + int result = Float.floatToIntBits(number); + expect(result == orig); + } + + { + int orig = 0x80800003; + float number = Float.intBitsToFloat(orig); + int result = Float.floatToIntBits(number); + expect(result == orig); + } + + for (int x = 0; x < 1000; ++x) { + int m = 100; + int n = 200; + double array[][] = new double[m][n]; + + for (int i = 0; i < m; ++i) { + for (int j = 0; j < n; ++j) { + array[i][j] = 1234567890.0; + } + } + } + + { double v = Double.NaN; + expect(0 == (int) v); } + + { double v = Double.NEGATIVE_INFINITY; + expect(Integer.MIN_VALUE == (int) v); } + + { double v = Long.MIN_VALUE; + expect(Integer.MIN_VALUE == (int) v); } + + { double v = Double.POSITIVE_INFINITY; + expect(Integer.MAX_VALUE == (int) v); } + + { double v = Long.MAX_VALUE; + expect(Integer.MAX_VALUE == (int) v); } + + { float v = Float.NaN; + expect(0 == (int) v); } + + { float v = Float.NEGATIVE_INFINITY; + expect(Integer.MIN_VALUE == (int) v); } + + { float v = Integer.MIN_VALUE; + expect(Integer.MIN_VALUE == (int) v); } + + { float v = Float.POSITIVE_INFINITY; + expect(Integer.MAX_VALUE == (int) v); } + + { float v = Integer.MAX_VALUE; + expect(Integer.MAX_VALUE == (int) v); } + + { double v = Double.NaN; + expect(0 == (long) v); } + + { double v = Double.NEGATIVE_INFINITY; + expect(Long.MIN_VALUE == (long) v); } + + { double v = Long.MIN_VALUE; + expect(Long.MIN_VALUE == (long) v); } + + { double v = Double.POSITIVE_INFINITY; + expect(Long.MAX_VALUE == (long) v); } + + { double v = Long.MAX_VALUE; + expect(Long.MAX_VALUE == (long) v); } + + { float v = Float.NaN; + expect(0 == (long) v); } + + { float v = Float.NEGATIVE_INFINITY; + expect(Long.MIN_VALUE == (long) v); } + + { float v = Integer.MIN_VALUE; + expect(Integer.MIN_VALUE == (long) v); } + + { float v = Float.POSITIVE_INFINITY; + expect(Long.MAX_VALUE == (long) v); } + + expect(Double.NaN != Double.NaN); + expect(! (Double.NaN == Double.NaN)); + + { double d = Double.NaN; + expect(Double.NaN != d); + expect(! (Double.NaN == d)); + expect(d != d); + expect(! (d == d)); } + + expect(Float.NaN != Float.NaN); + expect(! (Float.NaN == Float.NaN)); + + { float d = Float.NaN; + expect(Float.NaN != d); + expect(! (Float.NaN == d)); + expect(d != d); + expect(! (d == d)); } + } +} diff --git a/sgx-jvm/avian/test/FormatStrings.java b/sgx-jvm/avian/test/FormatStrings.java new file mode 100644 index 0000000000..32e8305012 --- /dev/null +++ b/sgx-jvm/avian/test/FormatStrings.java @@ -0,0 +1,296 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +/* + * @author bcg + */ +public class FormatStrings { + + public static void main(String... args) throws Exception { + FormatStrings test = new FormatStrings(); + test.testLiteral(); + test.testString(); + test.testNewline(); + test.testPercent(); + test.testBoolean(); + test.testCharacter(); + test.testHashCode(); + test.testIntegers(); + test.testWidths(); + test.testPrecisions(); + } + + private void _testFormat(String expected, String format, Object... args) { + String actual = String.format(format, args); + ensureEquals(expected, actual); + System.err.println("Expected: " + expected + ", Actual: " + actual); + } + + private static void ensureEquals(String expected, String actual) { + if (expected != actual) { + if ((expected == null || actual == null) || !(expected.equals(actual))) { + throw new IllegalArgumentException( + "Expected `" + expected + "` but was actually `" + actual + "`."); + } + } + } + + public void testLiteral() { + _testFormat("test Literal 1", "test Literal 1"); + _testFormat("test Literal 2", "test Literal 2", (Object[]) null); + _testFormat("test Literal 3", "test Literal 3", new Object[0]); + } + + public void testString() { + _testFormat("test String 1", "test %s", "String 1"); + _testFormat("test String null", "test String %s", new Object[]{null} ); + _testFormat("test String 2", "test %2$s", "String 1", "String 2"); + _testFormat("String `string`", "String `%s`", new String("string")); + _testFormat("String `STRING`", "String `%S`", new String("string")); + _testFormat("String `another string`", "String `%s`", new String("another string")); + _testFormat("String `ANOTHER STRING`", "String `%S`", new String("another string")); + _testFormat("String `null`", "String `%s`", (String)null); + _testFormat("String `NULL`", "String `%S`", (String)null); + _testFormat("String `true`", "String `%s`", new Boolean("true")); + _testFormat("String `TRUE`", "String `%S`", new Boolean("true")); + _testFormat("String `false`", "String `%s`", new Boolean("false")); + _testFormat("String `FALSE`", "String `%S`", new Boolean("false")); + } + + public void testNewline() { + final String newline = System.getProperty("line.separator"); + _testFormat( + "<<<" + newline + " test newlines" + newline + ">>>", + "<<<%n test newlines%n>>>" + ); + } + + public void testBoolean() { + _testFormat("test boolean true", "test boolean %b", true); + _testFormat("test Boolean true", "test Boolean %b", Boolean.TRUE); + _testFormat("test boolean false", "test boolean %b", false); + _testFormat("test Boolean false", "test Boolean %b", Boolean.FALSE); + _testFormat("test null Boolean (false)", "test null Boolean (%b)", new Object[]{(Boolean)null}); + _testFormat("test non-null Boolean (true)", "test non-null Boolean (%b)", new Object()); + _testFormat("test boolean string (true)", "test boolean string (%b)", "false"); + _testFormat("Boolean `true`", "Boolean `%b`", new Boolean("true")); + _testFormat("Boolean `TRUE`", "Boolean `%B`", new Boolean("true")); + _testFormat("Boolean `false`", "Boolean `%b`", new Boolean("false")); + _testFormat("Boolean `FALSE`", "Boolean `%B`", new Boolean("false")); + _testFormat("Boolean `false`", "Boolean `%b`", (String)null); + _testFormat("Boolean `FALSE`", "Boolean `%B`", (String)null); + _testFormat("Boolean `true`", "Boolean `%b`", new String("")); + _testFormat("Boolean `TRUE`", "Boolean `%B`", new String("")); + _testFormat("Boolean `true`", "Boolean `%b`", new String("true")); + _testFormat("Boolean `TRUE`", "Boolean `%B`", new String("true")); + _testFormat("Boolean `true`", "Boolean `%b`", new String("false")); + _testFormat("Boolean `TRUE`", "Boolean `%B`", new String("false")); + } + + public void testPercent() { + _testFormat("Percents work 100%", "Percents work 100%%"); + } + + public void testCharacter() { + _testFormat("test character such as a", "test character such as %c", 'a'); + _testFormat("test character such as b", "test character such as %c", (int) 98); + _testFormat("test character such as c", "test character such as %c", (byte) 99); + _testFormat("test character such as d", "test character such as %c", (short) 100); + } + + public void testHashCode() { + final Object obj1 = new Object(); + final Object obj2 = new Object(); + final String hc1 = Integer.toHexString(obj1.hashCode()); + final String hc2 = Integer.toHexString(obj2.hashCode()); + _testFormat("test hashcode 1 (" + hc1 + ")" , "test hashcode 1 (%h)", obj1, obj2); + _testFormat("test hashcode 2 (" + hc2 + ")" , "test hashcode 2 (%2$h)", obj1, obj2); + _testFormat("test hashcode null", "test hashcode %h", (String) null); + } + + public void testIntegers() { + + _testFormat("Long 1", "Long %d", new Long(1)); + _testFormat("Long 2", "Long %2$d", new Long(1), new Long(2)); + _testFormat("Integer 1", "Integer %d", new Integer(1)); + _testFormat("Integer 2", "Integer %2$d", new Integer(1), new Integer(2)); + _testFormat("Short 1", "Short %d", new Short((short)1)); + _testFormat("Short 2", "Short %2$d", new Short((short)1), new Short((short)2)); + _testFormat("Byte 1", "Byte %d", new Byte((byte)1)); + _testFormat("Byte 2", "Byte %2$d", new Byte((byte)1), new Byte((byte)2)); + + _testFormat("Long 144", "Long %o", new Long(100)); + _testFormat("Long 310", "Long %2$o", new Long(100), new Long(200)); + _testFormat("Integer 144", "Integer %o", new Integer(100)); + _testFormat("Integer 310", "Integer %2$o", new Integer(100), new Integer(200)); + _testFormat("Short 144", "Short %o", new Short((short)100)); + _testFormat("Short 310", "Short %2$o", new Short((short)100), new Short((short)200)); + _testFormat("Byte 144", "Byte %o", new Byte((byte)100)); + _testFormat("Byte 310", "Byte %2$o", new Byte((byte)100), new Byte((byte)200)); + + _testFormat("Long 64", "Long %x", new Long(100)); + _testFormat("Long c8", "Long %2$x", new Long(100), new Long(200)); + _testFormat("Long C8", "Long %2$X", new Long(100), new Long(200)); + _testFormat("Integer 64", "Integer %x", new Integer(100)); + _testFormat("Integer c8", "Integer %2$x", new Integer(100), new Integer(200)); + _testFormat("Short 64", "Short %x", new Short((short)100)); + _testFormat("Short C8", "Short %2$X", new Short((short)100), new Short((short)200)); + _testFormat("Byte 64", "Byte %x", new Byte((byte)100)); + _testFormat("Byte c8", "Byte %2$x", new Byte((byte)100), new Byte((byte)200)); + + _testFormat("Decimal `1`", "Decimal `%d`", new Integer((int)1)); + _testFormat("Decimal `0`", "Decimal `%d`", new Integer((int)0)); + _testFormat("Decimal `100`", "Decimal `%d`", new Integer((int)100)); + _testFormat("Decimal `100000`", "Decimal `%d`", new Integer((int)100000)); + _testFormat("Decimal `63`", "Decimal `%d`", new Integer((int)63)); + _testFormat("Decimal `64`", "Decimal `%d`", new Integer((int)64)); + _testFormat("Decimal `-1`", "Decimal `%d`", new Integer((int)-1)); + _testFormat("Decimal `-100`", "Decimal `%d`", new Integer((int)-100)); + _testFormat("Decimal `-100000`", "Decimal `%d`", new Integer((int)-100000)); + _testFormat("Decimal `1`", "Decimal `%d`", new Byte((byte)1)); + _testFormat("Decimal `0`", "Decimal `%d`", new Byte((byte)0)); + _testFormat("Decimal `100`", "Decimal `%d`", new Byte((byte)100)); + _testFormat("Decimal `63`", "Decimal `%d`", new Byte((byte)63)); + _testFormat("Decimal `64`", "Decimal `%d`", new Byte((byte)64)); + _testFormat("Decimal `-1`", "Decimal `%d`", new Byte((byte)-1)); + _testFormat("Decimal `-100`", "Decimal `%d`", new Byte((byte)-100)); + _testFormat("Decimal `1`", "Decimal `%d`", new Long((long)1)); + _testFormat("Decimal `0`", "Decimal `%d`", new Long((long)0)); + _testFormat("Decimal `100`", "Decimal `%d`", new Long((long)100)); + _testFormat("Decimal `100000`", "Decimal `%d`", new Long((long)100000)); + _testFormat("Decimal `63`", "Decimal `%d`", new Long((long)63)); + _testFormat("Decimal `64`", "Decimal `%d`", new Long((long)64)); + _testFormat("Decimal `-1`", "Decimal `%d`", new Long((long)-1)); + _testFormat("Decimal `-100`", "Decimal `%d`", new Long((long)-100)); + _testFormat("Decimal `-100000`", "Decimal `%d`", new Long((long)-100000)); + _testFormat("Decimal `1`", "Decimal `%d`", new Short((short)1)); + _testFormat("Decimal `0`", "Decimal `%d`", new Short((short)0)); + _testFormat("Decimal `100`", "Decimal `%d`", new Short((short)100)); + _testFormat("Decimal `63`", "Decimal `%d`", new Short((short)63)); + _testFormat("Decimal `64`", "Decimal `%d`", new Short((short)64)); + _testFormat("Decimal `-1`", "Decimal `%d`", new Short((short)-1)); + _testFormat("Decimal `-100`", "Decimal `%d`", new Short((short)-100)); + + _testFormat("Octal `1`", "Octal `%o`", new Integer((int)1)); + _testFormat("Octal `0`", "Octal `%o`", new Integer((int)0)); + _testFormat("Octal `144`", "Octal `%o`", new Integer((int)100)); + _testFormat("Octal `303240`", "Octal `%o`", new Integer((int)100000)); + _testFormat("Octal `77`", "Octal `%o`", new Integer((int)63)); + _testFormat("Octal `100`", "Octal `%o`", new Integer((int)64)); + _testFormat("Octal `37777777777`", "Octal `%o`", new Integer((int)-1)); + _testFormat("Octal `37777777634`", "Octal `%o`", new Integer((int)-100)); + _testFormat("Octal `37777474540`", "Octal `%o`", new Integer((int)-100000)); + _testFormat("Octal `1`", "Octal `%o`", new Byte((byte)1)); + _testFormat("Octal `0`", "Octal `%o`", new Byte((byte)0)); + _testFormat("Octal `144`", "Octal `%o`", new Byte((byte)100)); + _testFormat("Octal `77`", "Octal `%o`", new Byte((byte)63)); + _testFormat("Octal `100`", "Octal `%o`", new Byte((byte)64)); + _testFormat("Octal `377`", "Octal `%o`", new Byte((byte)-1)); + _testFormat("Octal `234`", "Octal `%o`", new Byte((byte)-100)); + _testFormat("Octal `1`", "Octal `%o`", new Long((long)1)); + _testFormat("Octal `0`", "Octal `%o`", new Long((long)0)); + _testFormat("Octal `144`", "Octal `%o`", new Long((long)100)); + _testFormat("Octal `303240`", "Octal `%o`", new Long((long)100000)); + _testFormat("Octal `77`", "Octal `%o`", new Long((long)63)); + _testFormat("Octal `100`", "Octal `%o`", new Long((long)64)); + _testFormat("Octal `1`", "Octal `%o`", new Short((short)1)); + _testFormat("Octal `0`", "Octal `%o`", new Short((short)0)); + _testFormat("Octal `144`", "Octal `%o`", new Short((short)100)); + _testFormat("Octal `77`", "Octal `%o`", new Short((short)63)); + _testFormat("Octal `100`", "Octal `%o`", new Short((short)64)); + _testFormat("Octal `177777`", "Octal `%o`", new Short((short)-1)); + _testFormat("Octal `177634`", "Octal `%o`", new Short((short)-100)); + + _testFormat("HexDec `1`", "HexDec `%x`", new Integer((int)1)); + _testFormat("HexDec `1`", "HexDec `%X`", new Integer((int)1)); + _testFormat("HexDec `0`", "HexDec `%x`", new Integer((int)0)); + _testFormat("HexDec `0`", "HexDec `%X`", new Integer((int)0)); + _testFormat("HexDec `64`", "HexDec `%x`", new Integer((int)100)); + _testFormat("HexDec `64`", "HexDec `%X`", new Integer((int)100)); + _testFormat("HexDec `186a0`", "HexDec `%x`", new Integer((int)100000)); + _testFormat("HexDec `186A0`", "HexDec `%X`", new Integer((int)100000)); + _testFormat("HexDec `3f`", "HexDec `%x`", new Integer((int)63)); + _testFormat("HexDec `3F`", "HexDec `%X`", new Integer((int)63)); + _testFormat("HexDec `40`", "HexDec `%x`", new Integer((int)64)); + _testFormat("HexDec `40`", "HexDec `%X`", new Integer((int)64)); + _testFormat("HexDec `ffffffff`", "HexDec `%x`", new Integer((int)-1)); + _testFormat("HexDec `FFFFFFFF`", "HexDec `%X`", new Integer((int)-1)); + _testFormat("HexDec `ffffff9c`", "HexDec `%x`", new Integer((int)-100)); + _testFormat("HexDec `FFFFFF9C`", "HexDec `%X`", new Integer((int)-100)); + _testFormat("HexDec `fffe7960`", "HexDec `%x`", new Integer((int)-100000)); + _testFormat("HexDec `FFFE7960`", "HexDec `%X`", new Integer((int)-100000)); + _testFormat("HexDec `1`", "HexDec `%x`", new Byte((byte)1)); + _testFormat("HexDec `1`", "HexDec `%X`", new Byte((byte)1)); + _testFormat("HexDec `0`", "HexDec `%x`", new Byte((byte)0)); + _testFormat("HexDec `0`", "HexDec `%X`", new Byte((byte)0)); + _testFormat("HexDec `64`", "HexDec `%x`", new Byte((byte)100)); + _testFormat("HexDec `64`", "HexDec `%X`", new Byte((byte)100)); + _testFormat("HexDec `3f`", "HexDec `%x`", new Byte((byte)63)); + _testFormat("HexDec `3F`", "HexDec `%X`", new Byte((byte)63)); + _testFormat("HexDec `40`", "HexDec `%x`", new Byte((byte)64)); + _testFormat("HexDec `40`", "HexDec `%X`", new Byte((byte)64)); + _testFormat("HexDec `ff`", "HexDec `%x`", new Byte((byte)-1)); + _testFormat("HexDec `FF`", "HexDec `%X`", new Byte((byte)-1)); + _testFormat("HexDec `9c`", "HexDec `%x`", new Byte((byte)-100)); + _testFormat("HexDec `9C`", "HexDec `%X`", new Byte((byte)-100)); + _testFormat("HexDec `1`", "HexDec `%x`", new Long((long)1)); + _testFormat("HexDec `1`", "HexDec `%X`", new Long((long)1)); + _testFormat("HexDec `0`", "HexDec `%x`", new Long((long)0)); + _testFormat("HexDec `0`", "HexDec `%X`", new Long((long)0)); + _testFormat("HexDec `64`", "HexDec `%x`", new Long((long)100)); + _testFormat("HexDec `64`", "HexDec `%X`", new Long((long)100)); + _testFormat("HexDec `186a0`", "HexDec `%x`", new Long((long)100000)); + _testFormat("HexDec `186A0`", "HexDec `%X`", new Long((long)100000)); + _testFormat("HexDec `3f`", "HexDec `%x`", new Long((long)63)); + _testFormat("HexDec `3F`", "HexDec `%X`", new Long((long)63)); + _testFormat("HexDec `40`", "HexDec `%x`", new Long((long)64)); + _testFormat("HexDec `40`", "HexDec `%X`", new Long((long)64)); + _testFormat("HexDec `ffffffffffffffff`", "HexDec `%x`", new Long((long)-1)); + _testFormat("HexDec `FFFFFFFFFFFFFFFF`", "HexDec `%X`", new Long((long)-1)); + _testFormat("HexDec `ffffffffffffff9c`", "HexDec `%x`", new Long((long)-100)); + _testFormat("HexDec `FFFFFFFFFFFFFF9C`", "HexDec `%X`", new Long((long)-100)); + _testFormat("HexDec `fffffffffffe7960`", "HexDec `%x`", new Long((long)-100000)); + _testFormat("HexDec `FFFFFFFFFFFE7960`", "HexDec `%X`", new Long((long)-100000)); + _testFormat("HexDec `1`", "HexDec `%x`", new Short((short)1)); + _testFormat("HexDec `1`", "HexDec `%X`", new Short((short)1)); + _testFormat("HexDec `0`", "HexDec `%x`", new Short((short)0)); + _testFormat("HexDec `0`", "HexDec `%X`", new Short((short)0)); + _testFormat("HexDec `64`", "HexDec `%x`", new Short((short)100)); + _testFormat("HexDec `64`", "HexDec `%X`", new Short((short)100)); + _testFormat("HexDec `3f`", "HexDec `%x`", new Short((short)63)); + _testFormat("HexDec `3F`", "HexDec `%X`", new Short((short)63)); + _testFormat("HexDec `40`", "HexDec `%x`", new Short((short)64)); + _testFormat("HexDec `40`", "HexDec `%X`", new Short((short)64)); + _testFormat("HexDec `ffff`", "HexDec `%x`", new Short((short)-1)); + _testFormat("HexDec `FFFF`", "HexDec `%X`", new Short((short)-1)); + _testFormat("HexDec `ff9c`", "HexDec `%x`", new Short((short)-100)); + _testFormat("HexDec `FF9C`", "HexDec `%X`", new Short((short)-100)); + } + + public void testWidths() { + _testFormat("0001", "%04d", 1); + _testFormat(" 1", "%4d", 1); + _testFormat(" 11", "%4x", 17); + _testFormat("0011", "%04x", 17); + _testFormat(" a", "%2x", 10); + _testFormat(" A", "%2X", 10); + _testFormat("a ", "%-2x", 10); + _testFormat("A ", "%-2X", 10); + _testFormat("10000", "%4d", 10000); + _testFormat("Hello World ", "%-15s", "Hello World"); + _testFormat(" Hello World", "%15s", "Hello World"); + } + + public void testPrecisions() { + _testFormat("Hello", "%-1.5s", "Hello World"); + _testFormat("Hello", "%1.5s", "Hello World"); + } + +} diff --git a/sgx-jvm/avian/test/FutureTaskTest.java b/sgx-jvm/avian/test/FutureTaskTest.java new file mode 100644 index 0000000000..7a8ea9d9a7 --- /dev/null +++ b/sgx-jvm/avian/test/FutureTaskTest.java @@ -0,0 +1,105 @@ +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; +import java.util.concurrent.RunnableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class FutureTaskTest { + private static final int DELAY_TIME = 10; + + public static void main(String[] args) throws InterruptedException, ExecutionException { + isDoneTest(false); + isDoneTest(true); + getCallableResultTest(); + getRunnableResultTest(); + getTimeoutFail(); + getExecutionExceptionTest(); + } + + private static void isDoneTest(final boolean throwException) { + RunnableFuture future = new FutureTask(new Runnable() { + @Override + public void run() { + if (throwException) { + throw new RuntimeException(); + } + } + }, null); + + // should finish the future + future.run(); + + if (! future.isDone()) { + throw new RuntimeException("Future should be done"); + } + } + + private static void getCallableResultTest() throws InterruptedException, ExecutionException { + final Object result = new Object(); + FutureTask future = new FutureTask(new Callable() { + @Override + public Object call() throws Exception { + return result; + } + }); + + future.run(); + if (future.get() != result) { + throw new RuntimeException("Bad result returned: " + future.get()); + } + } + + private static void getRunnableResultTest() throws InterruptedException, ExecutionException { + final Object result = new Object(); + FutureTask future = new FutureTask(new Runnable() { + @Override + public void run() { + // nothing here + } + }, result); + + future.run(); + if (future.get() != result) { + throw new RuntimeException("Bad result returned: " + future.get()); + } + } + + private static void getTimeoutFail() throws InterruptedException, + ExecutionException { + RunnableFuture future = new FutureTask(new Runnable() { + @Override + public void run() { + // wont run + } + }, null); + + long startTime = System.currentTimeMillis(); + try { + future.get(DELAY_TIME, TimeUnit.MILLISECONDS); + throw new RuntimeException("Exception should have been thrown"); + } catch (TimeoutException e) { + long catchTime = System.currentTimeMillis(); + if (catchTime - startTime < DELAY_TIME) { + throw new RuntimeException("get with timeout did not block long enough"); + } + } + } + + private static void getExecutionExceptionTest() throws InterruptedException, ExecutionException { + FutureTask future = new FutureTask(new Runnable() { + @Override + public void run() { + throw new RuntimeException(); + } + }, null); + + future.run(); + try { + future.get(); + throw new RuntimeException("Exception should have thrown"); + } catch (ExecutionException e) { + // expected + } + } +} diff --git a/sgx-jvm/avian/test/GC.java b/sgx-jvm/avian/test/GC.java new file mode 100644 index 0000000000..9fabee0cc9 --- /dev/null +++ b/sgx-jvm/avian/test/GC.java @@ -0,0 +1,203 @@ +public class GC { + private static final Integer cache[] = new Integer[100]; + private static final Integer MAX_INT_OBJ = new Integer(Integer.MAX_VALUE); + + private static Integer valueOf(int i) { + try { + return cache[i]; + } catch (ArrayIndexOutOfBoundsException e) { + return (i == Integer.MAX_VALUE) ? MAX_INT_OBJ : new Integer(i); + } + } + + private static void small() { + for (int i = 0; i < 1024; ++i) { + byte[] a = new byte[4 * 1024]; + } + } + + private static void medium() { + for (int i = 0; i < 8; ++i) { + Object[] array = new Object[32]; + for (int j = 0; j < 32; ++j) { + array[j] = new byte[32 * 1024]; + } + } + } + + private static void large() { + for (int i = 0; i < 8; ++i) { + byte[] a = new byte[16 * 1024 * 1024]; + } + + for (int i = 0; i < 8; ++i) { + byte[] a = new byte[16 * 1024 * 1024]; + for (int j = 0; j < 32; ++j) { + byte[] b = new byte[32 * 1024]; + } + } + } + + private static void stackMap1(boolean predicate) { + if (predicate) { + Object a = null; + } + + System.gc(); + } + + private static void stackMap2(boolean predicate) { + if (predicate) { + int a = 42; + } else { + Object a = null; + } + + System.gc(); + } + + private static void stackMap3(boolean predicate) { + if (predicate) { + Object a = null; + } else { + int a = 42; + } + + System.gc(); + } + + private static void stackMap4(boolean predicate) { + int i = 2; + if (predicate) { + Object a = null; + } else { + Object a = null; + } + + do { + System.gc(); + int a = 42; + -- i; + } while (i >= 0); + } + + private static void noop() { } + + private static void stackMap5(boolean predicate) { + if (predicate) { + noop(); + } + + if (predicate) { + noop(); + } else { + Object a = null; + } + + System.gc(); + } + + private static void stackMap6(boolean predicate) { + if (predicate) { + int a = 42; + } else { + Object a = null; + } + + if (predicate) { + noop(); + } else { + Object a = null; + } + + noop(); + System.gc(); + } + + private static void stackMap7(boolean predicate) { + try { + if (predicate) { + Object a = null; + } else { + Object a = null; + } + + try { + int a = 42; + throw new DummyException(); + } finally { + System.gc(); + } + } catch (DummyException e) { + e.toString(); + } + } + + private static void stackMap8(boolean predicate) { + try { + Object x = new Object(); + if (predicate) { + Object a = null; + } else { + Object a = null; + } + + try { + int a = 42; + throw new DummyException(); + } finally { + System.gc(); + x.toString(); + } + } catch (DummyException e) { + e.toString(); + } + } + + public static void main(String[] args) { + valueOf(1000); + + Object[] array = new Object[1024 * 1024]; + array[0] = new Object(); + + small(); + + array[1] = new Object(); + + medium(); + + array[2] = new Object(); + + large(); + + array[0].toString(); + array[1].toString(); + array[2].toString(); + + stackMap1(true); + stackMap1(false); + + stackMap2(true); + stackMap2(false); + + stackMap3(true); + stackMap3(false); + + stackMap4(true); + stackMap4(false); + + stackMap5(true); + stackMap5(false); + + stackMap6(true); + stackMap6(false); + + stackMap7(true); + stackMap7(false); + + stackMap8(true); + stackMap8(false); + } + + private static class DummyException extends RuntimeException { } +} diff --git a/sgx-jvm/avian/test/Hello.java b/sgx-jvm/avian/test/Hello.java new file mode 100644 index 0000000000..d7d4f91935 --- /dev/null +++ b/sgx-jvm/avian/test/Hello.java @@ -0,0 +1,5 @@ +public class Hello { + public static void main(String[] args) { + System.out.println("hello, world!"); + } +} diff --git a/sgx-jvm/avian/test/Initializers.java b/sgx-jvm/avian/test/Initializers.java new file mode 100644 index 0000000000..5b6fe2c2cb --- /dev/null +++ b/sgx-jvm/avian/test/Initializers.java @@ -0,0 +1,24 @@ +public class Initializers { + private static class Static2 { + public static String foo = "Static2.foo"; + + static { + System.gc(); + new Exception().printStackTrace(); + } + } + + private static class Static1 { + public static String foo = "Static1.foo"; + + static { + System.out.println(Static2.foo); + } + } + + public static void main(String[] args) { + Object x = new Object(); + System.out.println(Static1.foo); + x.toString(); + } +} diff --git a/sgx-jvm/avian/test/Integers.java b/sgx-jvm/avian/test/Integers.java new file mode 100644 index 0000000000..7c977d6136 --- /dev/null +++ b/sgx-jvm/avian/test/Integers.java @@ -0,0 +1,324 @@ +public class Integers { + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static int gcd(int m, int n) { + int temp; + m = Math.abs(m); + n = Math.abs(n); + if (m < n) { + temp = m; + m = n; + n = temp; + } + while (n != 0) { + temp = m; + m = n; + n = temp % n; + } + return m; + } + + public static void main(String[] args) throws Exception { + { int foo = 1028; + foo -= 1023; + expect(foo == 5); + } + + expect(gcd(12, 4) == 4); + + { int a = 2; + int b = 2; + int c = a + b; + } + + { int a = 2; + int c = a + a; + } + + { int a = -5; + int b = 2; + expect(a >> b == -5 >> 2); + expect(a >>> b == -5 >>> 2); + expect(a << b == -5 << 2); + expect(a + b == -5 + 2); + expect(a - b == -5 - 2); + expect(a * b == -5 * 2); + expect(a / b == -5 / 2); + expect(a % b == -5 % 2); + expect((a & b) == (-5 & 2)); + expect((a | b) == (-5 | 2)); + expect((a ^ b) == (-5 ^ 2)); + expect(-a == 5); + expect(~a == ~-5); + + a = 5; + b = 2; + expect(a >> b == 5 >> 2); + expect(a >>> b == 5 >>> 2); + expect(a << b == 5 << 2); + expect(a + b == 5 + 2); + expect(a - b == 5 - 2); + expect(a * b == 5 * 2); + expect(a / b == 5 / 2); + expect(a % b == 5 % 2); + expect((a & b) == (5 & 2)); + expect((a | b) == (5 | 2)); + expect((a ^ b) == (5 ^ 2)); + expect(-a == -5); + expect(~a == ~5); + } + + { int a = -5; + expect(a >> 2 == -5 >> 2); + expect(a >>> 2 == -5 >>> 2); + expect(a << 2 == -5 << 2); + expect(a + 2 == -5 + 2); + expect(a - 2 == -5 - 2); + expect(a * 2 == -5 * 2); + expect(a / 2 == -5 / 2); + expect(a % 2 == -5 % 2); + expect((a & 2) == (-5 & 2)); + expect((a | 2) == (-5 | 2)); + expect((a ^ 2) == (-5 ^ 2)); + + a = 5; + expect(a >> 2 == 5 >> 2); + expect(a >>> 2 == 5 >>> 2); + expect(a << 2 == 5 << 2); + expect(a + 2 == 5 + 2); + expect(a - 2 == 5 - 2); + expect(a * 2 == 5 * 2); + expect(a / 2 == 5 / 2); + expect(a % 2 == 5 % 2); + expect((a & 2) == (5 & 2)); + expect((a | 2) == (5 | 2)); + expect((a ^ 2) == (5 ^ 2)); + } + + { int a = -5; + int b = 1234567; + expect(a + b == -5 + 1234567); + expect(a - b == -5 - 1234567); + expect(a * b == -5 * 1234567); + expect(a / b == -5 / 1234567); + expect(a % b == -5 % 1234567); + expect((a & b) == (-5 & 1234567)); + expect((a | b) == (-5 | 1234567)); + expect((a ^ b) == (-5 ^ 1234567)); + + a = 5; + b = 1234567; + expect(a + b == 5 + 1234567); + expect(a - b == 5 - 1234567); + expect(a * b == 5 * 1234567); + expect(a / b == 5 / 1234567); + expect(a % b == 5 % 1234567); + expect((a & b) == (5 & 1234567)); + expect((a | b) == (5 | 1234567)); + expect((a ^ b) == (5 ^ 1234567)); + } + + { int a = -5; + expect(a + 1234567 == -5 + 1234567); + expect(a - 1234567 == -5 - 1234567); + expect(a * 1234567 == -5 * 1234567); + expect(a / 1234567 == -5 / 1234567); + expect(a % 1234567 == -5 % 1234567); + expect((a & 1234567) == (-5 & 1234567)); + expect((a | 1234567) == (-5 | 1234567)); + expect((a ^ 1234567) == (-5 ^ 1234567)); + + a = 5; + expect(a + 1234567 == 5 + 1234567); + expect(a - 1234567 == 5 - 1234567); + expect(a * 1234567 == 5 * 1234567); + expect(a / 1234567 == 5 / 1234567); + expect(a % 1234567 == 5 % 1234567); + expect((a & 1234567) == (5 & 1234567)); + expect((a | 1234567) == (5 | 1234567)); + expect((a ^ 1234567) == (5 ^ 1234567)); + } + + { int a = -1234567; + int b = 2; + expect(a >> b == -1234567 >> 2); + expect(a >>> b == -1234567 >>> 2); + expect(a << b == -1234567 << 2); + expect(a + b == -1234567 + 2); + expect(a - b == -1234567 - 2); + expect(a * b == -1234567 * 2); + expect(a / b == -1234567 / 2); + expect(a % b == -1234567 % 2); + expect((a & b) == (-1234567 & 2)); + expect((a | b) == (-1234567 | 2)); + expect((a ^ b) == (-1234567 ^ 2)); + expect(-a == 1234567); + expect(~a == ~-1234567); + + a = 1234567; + b = 2; + expect(a >> b == 1234567 >> 2); + expect(a >>> b == 1234567 >>> 2); + expect(a << b == 1234567 << 2); + expect(a + b == 1234567 + 2); + expect(a - b == 1234567 - 2); + expect(a * b == 1234567 * 2); + expect(a / b == 1234567 / 2); + expect(a % b == 1234567 % 2); + expect((a & b) == (1234567 & 2)); + expect((a | b) == (1234567 | 2)); + expect((a ^ b) == (1234567 ^ 2)); + expect(-a == -1234567); + expect(~a == ~1234567); + } + + { int a = -1234567; + expect(a >> 2 == -1234567 >> 2); + expect(a >>> 2 == -1234567 >>> 2); + expect(a << 2 == -1234567 << 2); + expect(a + 2 == -1234567 + 2); + expect(a - 2 == -1234567 - 2); + expect(a * 2 == -1234567 * 2); + expect(a / 2 == -1234567 / 2); + expect(a % 2 == -1234567 % 2); + expect((a & 2) == (-1234567 & 2)); + expect((a | 2) == (-1234567 | 2)); + expect((a ^ 2) == (-1234567 ^ 2)); + + a = 1234567; + expect(a >> 2 == 1234567 >> 2); + expect(a >>> 2 == 1234567 >>> 2); + expect(a << 2 == 1234567 << 2); + expect(a + 2 == 1234567 + 2); + expect(a - 2 == 1234567 - 2); + expect(a * 2 == 1234567 * 2); + expect(a / 2 == 1234567 / 2); + expect(a % 2 == 1234567 % 2); + expect((a & 2) == (1234567 & 2)); + expect((a | 2) == (1234567 | 2)); + expect((a ^ 2) == (1234567 ^ 2)); + } + + { int a = -1234567; + int b = 1234567; + expect(a + b == -1234567 + 1234567); + expect(a - b == -1234567 - 1234567); + expect(a * b == -1234567 * 1234567); + expect(a / b == -1234567 / 1234567); + expect(a % b == -1234567 % 1234567); + expect((a & b) == (-1234567 & 1234567)); + expect((a | b) == (-1234567 | 1234567)); + expect((a ^ b) == (-1234567 ^ 1234567)); + + a = 1234567; + b = 1234567; + expect(a + b == 1234567 + 1234567); + expect(a - b == 1234567 - 1234567); + expect(a * b == 1234567 * 1234567); + expect(a / b == 1234567 / 1234567); + expect(a % b == 1234567 % 1234567); + expect((a & b) == (1234567 & 1234567)); + expect((a | b) == (1234567 | 1234567)); + expect((a ^ b) == (1234567 ^ 1234567)); + } + + { int a = -1234567; + expect(a + 1234567 == -1234567 + 1234567); + expect(a - 1234567 == -1234567 - 1234567); + expect(a * 1234567 == -1234567 * 1234567); + expect(a / 1234567 == -1234567 / 1234567); + expect(a % 1234567 == -1234567 % 1234567); + expect((a & 1234567) == (-1234567 & 1234567)); + expect((a | 1234567) == (-1234567 | 1234567)); + expect((a ^ 1234567) == (-1234567 ^ 1234567)); + + a = 1234567; + expect(a + 1234567 == 1234567 + 1234567); + expect(a - 1234567 == 1234567 - 1234567); + expect(a * 1234567 == 1234567 * 1234567); + expect(a / 1234567 == 1234567 / 1234567); + expect(a % 1234567 == 1234567 % 1234567); + expect((a & 1234567) == (1234567 & 1234567)); + expect((a | 1234567) == (1234567 | 1234567)); + expect((a ^ 1234567) == (1234567 ^ 1234567)); + } + + { int get_buffer = 2144642881; + int bits_left = 30; + int l = 9; + int code = (((get_buffer >> (bits_left -= (l)))) & ((1<<(l))-1)); + expect(code == 510); + } + + { int width = 8; + int height = 8; + int depth = 24; + int scanlinePad = 4; + + int bytesPerLine = (((width * depth + 7) / 8) + (scanlinePad - 1)) + / scanlinePad * scanlinePad; + expect(bytesPerLine == 24); + } + + { int y = -11760768; + expect((y + 0x8000) == (-11760768 + 0x8000)); } + + expect(Math.min(796, 1069) == 796); + + { int b = 1; + expect((b << 32) == 1); } + + { int b = 0xFFFFFFFF; + expect((b >>> -1) == 1); } + + { int b = 0x10000000; + expect((b >> -31) == 0x8000000); } + + { int b = 1; int s = 32; + expect((b << s) == 1); } + + { int b = 0xFFFFFFFF; int s = -1; + expect((b >>> s) == 1); } + + { int b = 0x10000000; int s = -31; + expect((b >> s) == 0x8000000); } + + { int b = 0xBE; + expect((b & 0xFF) == 0xBE); } + + { int b = 0xBE; + expect((b >>> 0) == 0xBE); } + + { int b = 0xBE; + expect((b >> 0) == 0xBE); } + + { int b = 0xBE; + expect((b << 0) == 0xBE); } + + { int b = 0xBE; + expect(((b >>> 0) & 0xFF) == 0xBE); } + + { int b = 0xBE; int x = 0xFF; + expect((b & x) == 0xBE); } + + { int b = 0xBE; int x = 0; + expect((b >>> x) == 0xBE); } + + { int b = 0xBE; int x = 0; + expect((b >> x) == 0xBE); } + + { int b = 0xBE; int x = 0; + expect((b << x) == 0xBE); } + + { int b = 0xBE; int x = 0; int y = 0xFF; + expect(((b >>> x) & y) == 0xBE); } + + expect(123 == Integer.decode("123").intValue()); + expect(-123 == Integer.decode("-123").intValue()); + expect(-83 == Integer.decode("-0123").intValue()); + expect(-291 == Integer.decode("-0x123").intValue()); + expect(291 == Integer.decode("#123").intValue()); + } +} diff --git a/sgx-jvm/avian/test/InvokeDynamic.java b/sgx-jvm/avian/test/InvokeDynamic.java new file mode 100644 index 0000000000..6d6e2842aa --- /dev/null +++ b/sgx-jvm/avian/test/InvokeDynamic.java @@ -0,0 +1,111 @@ +public class InvokeDynamic { + private final int foo; + + private InvokeDynamic(int foo) { + this.foo = foo; + } + + private interface Operation { + int operate(int a, int b); + } + + private interface Operation2 { + long operate(long a, int b); + } + + private static class Pair { + public final A first; + public final B second; + + public Pair(A first, B second) { + this.first = first; + this.second = second; + } + } + + private interface Supplier extends java.io.Serializable { + T get(); + } + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + public static void main(String[] args) { + int c = 4; + Operation op = (a, b) -> a + b - c; + expect(op.operate(2, 3) == (2 + 3) - 4); + + for (int i = 0; i < 4; ++i) { + new InvokeDynamic(i).test(); + } + } + + private interface Foo extends java.io.Serializable { + void someFunction(Integer a, Integer b, String s); + } + + private interface UnboxedSerializable extends java.io.Serializable { + int add(int a, int b); + } + + private interface Unboxed { + int add(int a, int b); + } + + private void requiresBridge(Number a, Object... rest) { + String s = "" + a; + for (Object r : rest) { + s += r; + } + } + + private static Integer addBoxed(Integer a, Integer b) { + return a + b; + } + + private interface Marker { + } + + private void test() { + { int c = 2; + Operation op = (a, b) -> ((a + b) * c) - foo; + expect(op.operate(2, 3) == ((2 + 3) * 2) - foo); + } + + { int c = 2; + Operation2 op = (a, b) -> ((a + b) * c) - foo; + expect(op.operate(2, 3) == ((2 + 3) * 2) - foo); + } + + { Supplier> s = () -> new Pair(42L, 77.1D); + expect(s.get().first == 42L); + expect(s.get().second == 77.1D); + } + + { double[] a = new double[] { 3.14D }; + Supplier> s = () -> new Pair(42L, a[0]); + expect(s.get().first == 42L); + expect(s.get().second == 3.14D); + } + + { Foo s = this::requiresBridge; + s.someFunction(1, 2, ""); + } + + // This abort()s in machine.cpp + // { Foo s = (Foo & Marker) this::requiresBridge; + // s.someFunction(1, 2, ""); + // } + + // NPE + // { UnboxedSerializable s = InvokeDynamic::addBoxed; + // expect(s.add(1, 2) == 3); + // } + + // NPE + // { Unboxed s = InvokeDynamic::addBoxed; + // expect(s.add(1, 2) == 3); + // } + } +} diff --git a/sgx-jvm/avian/test/JNI.java b/sgx-jvm/avian/test/JNI.java new file mode 100644 index 0000000000..b0583b6606 --- /dev/null +++ b/sgx-jvm/avian/test/JNI.java @@ -0,0 +1,141 @@ +import java.lang.reflect.Method; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; + +public class JNI { + private static boolean onLoadCalled; + + static { + System.loadLibrary("test"); + } + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static float echo(float f) { + return f; + } + + private static native float doEcho(float f); + + private static double echo(double f) { + return f; + } + + private static native double doEcho(double f); + + private static native double addDoubles + (double a1, double a2, double a3, double a4, double a5, double a6, + double a7, double a8, double a9, double a10, double a11, double a12, + double a13, double a14, double a15, double a16, double a17, double a18, + double a19, double a20); + + private static native float addFloats + (float a1, float a2, float a3, float a4, float a5, float a6, + float a7, float a8, float a9, float a10, float a11, float a12, + float a13, float a14, float a15, float a16, float a17, float a18, + float a19, float a20); + + private static native double addMix + (float a1, double a2, float a3, double a4, float a5, float a6, + float a7, float a8, float a9, float a10, float a11, float a12, + float a13, float a14, float a15, double a16, float a17, float a18, + float a19, float a20); + + private static native int addStackBoundary2 + (Object o1, Object o2, Object o3, int i1, int i2); + + private static native int addStackBoundary3 + (Object o1, Object o2, Object o3, int i1, int i2, int i3); + + private static native int addStackBoundary4 + (Object o1, Object o2, Object o3, int i1, int i2, int i3, int i4); + + private static native int addStackBoundary5 + (Object o1, Object o2, Object o3, int i1, int i2, int i3, int i4, int i5); + + private static native int addStackBoundary6 + (Object o1, Object o2, Object o3, int i1, int i2, int i3, int i4, int i5, int i6); + + private static native long fromReflectedMethod(Object m); + + private static native Object toReflectedMethod(Class c, long id, + boolean isStatic); + + private static native int callStaticIntMethod(Class c, long id); + + private static native Object newObject(Class c, long id); + + private static native long fromReflectedField(Field f); + + private static native Field toReflectedField(Class c, long id, + boolean isStatic); + + private static native int getStaticIntField(Class c, long id); + + private static native Object testLocalRef(Object o); + + public static int method242() { return 242; } + + public static final int field950 = 950; + + public static void main(String[] args) throws Exception { + expect(onLoadCalled); + + expect(addDoubles + (1.0d, 2.0d, 3.0d, 4.0d, 5.0d, 6.0d, 7.0d, 8.0d, 9.0d, 10.0d, 11.0d, + 12.0d, 13.0d, 14.0d, 15.0d, 16.0d, 17.0d, 18.0d, 19.0d, 20.0d) + == 210.0d); + + expect(addFloats + (1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, + 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f, 20.0f) + == 210.0f); + + expect(addMix + (1.0f, 2.0d, 3.0f, 4.0d, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, + 12.0f, 13.0f, 14.0f, 15.0f, 16.0d, 17.0f, 18.0f, 19.0f, 20.0f) + == 210.0d); + + expect(addStackBoundary2(null, null, null, 1, 10) == 11); + expect(addStackBoundary3(null, null, null, 1, 10, 100) == 111); + expect(addStackBoundary4(null, null, null, 1, 10, 100, 1000) == 1111); + expect(addStackBoundary5(null, null, null, 1, 10, 100, 1000, 10000) == 11111); + expect(addStackBoundary6(null, null, null, 1, 10, 100, 1000, 10000, 100000) == 111111); + + expect(doEcho(42.0f) == 42.0f); + expect(doEcho(42.0d) == 42.0d); + + expect(callStaticIntMethod + (JNI.class, fromReflectedMethod + (JNI.class.getMethod("method242"))) == 242); + + expect(((Method) toReflectedMethod + (JNI.class, fromReflectedMethod + (JNI.class.getMethod("method242")), true)) + .getName().equals("method242")); + + expect(newObject + (JNI.class, fromReflectedMethod + (JNI.class.getConstructor())) instanceof JNI); + + expect(((Constructor) toReflectedMethod + (JNI.class, fromReflectedMethod + (JNI.class.getConstructor()), false)) + .getDeclaringClass().equals(JNI.class)); + + expect(getStaticIntField + (JNI.class, fromReflectedField + (JNI.class.getField("field950"))) == 950); + + expect(toReflectedField + (JNI.class, fromReflectedField + (JNI.class.getField("field950")), true) + .getName().equals("field950")); + + { Object o = new Object(); + expect(testLocalRef(o) == o); + } + } +} diff --git a/sgx-jvm/avian/test/LazyLoading.java b/sgx-jvm/avian/test/LazyLoading.java new file mode 100644 index 0000000000..80d279e3f3 --- /dev/null +++ b/sgx-jvm/avian/test/LazyLoading.java @@ -0,0 +1,183 @@ +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +public class LazyLoading { + public static boolean loadLazy; + + public static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static File findClass(String name, File directory) { + for (File file: directory.listFiles()) { + if (file.isFile()) { + if (file.getName().equals(name + ".class")) { + return file; + } + } else if (file.isDirectory()) { + File result = findClass(name, file); + if (result != null) { + return result; + } + } + } + return null; + } + + private static byte[] read(File file) throws IOException { + byte[] bytes = new byte[(int) file.length()]; + FileInputStream in = new FileInputStream(file); + try { + if (in.read(bytes) != (int) file.length()) { + throw new RuntimeException(); + } + return bytes; + } finally { + in.close(); + } + } + + public static void main(String[] args) throws Exception { + Class c = new MyClassLoader(LazyLoading.class.getClassLoader()).loadClass + ("LazyLoading$Test"); + + c.getMethod("test").invoke(null); + } + + private static class MyClassLoader extends ClassLoader { + public MyClassLoader(ClassLoader parent) { + super(parent); + } + + protected Class findClass(String name) throws ClassNotFoundException { + try { + return defineClass + (name, read + (LazyLoading.findClass + (name, new File(System.getProperty("user.dir"))))); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public Class loadClass(String name) throws ClassNotFoundException { + if ("LazyLoading$Test".equals(name)) { + return findClass(name); + } else if ("LazyLoading$Lazy".equals(name) + || "LazyLoading$Interface".equals(name)) + { + if (loadLazy) { + return findClass(name); + } else { + throw new ClassNotFoundException(name); + } + } else { + return super.loadClass(name); + } + } + + private Class defineClass(String name, byte[] bytes) { + return defineClass(name, bytes, 0, bytes.length); + } + } + + public static class Test { + public static void test() { + doTest(); + loadLazy = true; + doTest(); + } + + private static void doTest() { + if (loadLazy) { + // anewarray + Lazy[] array = new Lazy[1]; + + // new and invokespecial + Object lazy = new Lazy(); + + // checkcast + array[0] = (Lazy) lazy; + + // instanceof + expect(lazy instanceof Lazy); + + // invokeinterface + expect(array[0].interfaceMethod() == 42); + + // invokestatic + expect(Lazy.staticMethod() == 43); + + // invokevirtual + expect(array[0].virtualMethod() == 44); + + // ldc + expect(Lazy.class == lazy.getClass()); + + // multianewarray + Lazy[][] multiarray = new Lazy[5][6]; + multiarray[2][3] = array[0]; + expect(multiarray[2][3] == array[0]); + + // getfield + expect(array[0].intField == 45); + + // getstatic + expect(Lazy.intStaticField == 46); + + // putfield int + array[0].intField = 47; + expect(array[0].intField == 47); + + // putfield long + array[0].longField = 48; + expect(array[0].longField == 48); + + // putfield object + Object x = new Object(); + array[0].objectField = x; + expect(array[0].objectField == x); + + // putstatic int + array[0].intStaticField = 49; + expect(array[0].intStaticField == 49); + + // putstatic long + array[0].longStaticField = 50; + expect(array[0].longStaticField == 50); + + // putstatic object + Object y = new Object(); + array[0].objectStaticField = y; + expect(array[0].objectStaticField == y); + } + } + } + + private interface Interface { + public int interfaceMethod(); + } + + private static class Lazy implements Interface { + public static int intStaticField = 46; + public static long longStaticField; + public static Object objectStaticField; + + public int intField = 45; + public long longField; + public Object objectField; + + public int interfaceMethod() { + return 42; + } + + public static int staticMethod() { + return 43; + } + + public int virtualMethod() { + return 44; + } + } +} diff --git a/sgx-jvm/avian/test/LinkedBlockingQueueTest.java b/sgx-jvm/avian/test/LinkedBlockingQueueTest.java new file mode 100644 index 0000000000..f0eed44994 --- /dev/null +++ b/sgx-jvm/avian/test/LinkedBlockingQueueTest.java @@ -0,0 +1,208 @@ +import java.util.LinkedList; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +public class LinkedBlockingQueueTest { + private static final int DELAY_TILL_ACTION = 10; + + public static void main(String[] args) throws InterruptedException { + remainingCapacityTest(); + QueueHelper.sizeTest(new LinkedBlockingQueue()); + QueueHelper.isEmptyTest(new LinkedBlockingQueue()); + QueueHelper.addTest(new LinkedBlockingQueue()); + addCapacityFail(); + offerTest(); + offerWithTimeoutTest(); + offerTimeoutTest(); + putTest(); + QueueHelper.addAllTest(new LinkedBlockingQueue()); + addAllFail(); + QueueHelper.elementTest(new LinkedBlockingQueue()); + QueueHelper.elementFail(new LinkedBlockingQueue()); + pollEmptyTest(); + pollTest(); + pollTimeoutTest(); + takeTest(); + QueueHelper.removeEmptyFail(new LinkedBlockingQueue()); + QueueHelper.removeTest(new LinkedBlockingQueue()); + drainToTest(); + drainToLimitTest(); + QueueHelper.containsTest(new LinkedBlockingQueue()); + QueueHelper.containsAllTest(new LinkedBlockingQueue()); + QueueHelper.removeObjectTest(new LinkedBlockingQueue()); + QueueHelper.removeAllTest(new LinkedBlockingQueue()); + QueueHelper.clearTest(new LinkedBlockingQueue()); + QueueHelper.toArrayTest(new LinkedBlockingQueue()); + } + + private static void verify(boolean val) { + if (! val) { + throw new RuntimeException(); + } + } + + private static void remainingCapacityTest() { + LinkedBlockingQueue lbq = new LinkedBlockingQueue(2); + verify(lbq.remainingCapacity() == 2); + + lbq.add(new Object()); + verify(lbq.remainingCapacity() == 1); + } + + private static void addCapacityFail() { + LinkedBlockingQueue lbq = new LinkedBlockingQueue(1); + Object testObject = new Object(); + lbq.add(testObject); + + try { + lbq.add(new Object()); + throw new RuntimeException("Exception should have thrown"); + } catch (IllegalStateException e) { + // expected + } + + verify(lbq.size() == 1); + verify(lbq.peek() == testObject); + } + + private static void offerTest() { + LinkedBlockingQueue lbq = new LinkedBlockingQueue(1); + Object testObject = new Object(); + verify(lbq.offer(testObject)); + verify(! lbq.offer(new Object())); + + verify(lbq.size() == 1); + verify(lbq.peek() == testObject); + } + + private static void offerWithTimeoutTest() throws InterruptedException { + final LinkedBlockingQueue lbq = new LinkedBlockingQueue(1); + lbq.add(new Object()); + + new Thread(new Runnable() { + @Override + public void run() { + try { + // sleep to make sure offer call starts first + Thread.sleep(DELAY_TILL_ACTION); + lbq.take(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + }).start(); + + // should accept once thread starts + verify(lbq.offer(new Object(), 10, TimeUnit.SECONDS)); + } + + private static void offerTimeoutTest() throws InterruptedException { + LinkedBlockingQueue lbq = new LinkedBlockingQueue(1); + lbq.add(new Object()); + + verify(! lbq.offer(new Object(), 10, TimeUnit.MILLISECONDS)); + } + + private static void putTest() throws InterruptedException { + LinkedBlockingQueue lbq = new LinkedBlockingQueue(); + Object testObject = new Object(); + lbq.put(testObject); + + verify(lbq.size() == 1); + verify(lbq.peek() == testObject); + } + + private static void addAllFail() { + LinkedBlockingQueue lbq = new LinkedBlockingQueue(1); + LinkedList toAdd = new LinkedList(); + toAdd.add(new Object()); + toAdd.add(new Object()); + + try { + lbq.addAll(toAdd); + throw new RuntimeException("Exception should have thrown"); + } catch (IllegalStateException e) { + // expected + } + } + + private static void pollEmptyTest() { + LinkedBlockingQueue lbq = new LinkedBlockingQueue(); + + verify(lbq.poll() == null); + } + + private static void pollTest() { + LinkedBlockingQueue lbq = new LinkedBlockingQueue(); + Object testObject = new Object(); + lbq.add(testObject); + + verify(lbq.poll() == testObject); + } + + private static void pollTimeoutTest() throws InterruptedException { + final LinkedBlockingQueue lbq = new LinkedBlockingQueue(); + final Object testObject = new Object(); + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(DELAY_TILL_ACTION); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + lbq.add(testObject); + } + }).start(); + + + verify(lbq.poll(DELAY_TILL_ACTION * 10, TimeUnit.MILLISECONDS) == testObject); + } + + private static void takeTest() throws InterruptedException { + final LinkedBlockingQueue lbq = new LinkedBlockingQueue(); + final Object testObject = new Object(); + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(DELAY_TILL_ACTION); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + lbq.add(testObject); + } + }).start(); + + + verify(lbq.take() == testObject); + } + + private static void drainToTest() { + int objQty = 2; + LinkedBlockingQueue lbq = new LinkedBlockingQueue(); + for (int i = 0; i < objQty; i++) { + lbq.add(new Object()); + } + + LinkedList drainToResult = new LinkedList(); + verify(lbq.drainTo(drainToResult) == objQty); + verify(drainToResult.size() == objQty); + } + + private static void drainToLimitTest() { + int objQty = 4; + int limit = 2; + LinkedBlockingQueue lbq = new LinkedBlockingQueue(); + for (int i = 0; i < objQty; i++) { + lbq.add(new Object()); + } + + LinkedList drainToResult = new LinkedList(); + verify(lbq.drainTo(drainToResult, limit) == limit); + verify(drainToResult.size() == limit); + verify(lbq.size() == objQty - limit); + } +} diff --git a/sgx-jvm/avian/test/List.java b/sgx-jvm/avian/test/List.java new file mode 100644 index 0000000000..c25950c525 --- /dev/null +++ b/sgx-jvm/avian/test/List.java @@ -0,0 +1,193 @@ +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.ListIterator; + +public class List { + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static String printList(ArrayList list) { + StringBuilder sb = new StringBuilder(); + + for (Integer i : list) { + sb.append(i); + sb.append(", "); + } + sb.setLength(sb.length()-2); + return sb.toString(); + } + + private static void isEqual(String s1, String s2) { + System.out.println(s1); + expect(s1.equals(s2)); + } + + private static void testIterators(java.util.List l) { + l.add(1); + l.add(2); + l.add(3); + + ListIterator it = l.listIterator(); + expect(it.next().equals(Integer.valueOf(1))); + expect(it.next().equals(Integer.valueOf(2))); + expect(it.next().equals(Integer.valueOf(3))); + expect(! it.hasNext()); + + it = l.listIterator(1); + expect(it.next().equals(Integer.valueOf(2))); + expect(it.next().equals(Integer.valueOf(3))); + expect(! it.hasNext()); + + it = l.listIterator(2); + expect(it.next().equals(Integer.valueOf(3))); + expect(! it.hasNext()); + + it = l.listIterator(3); + expect(it.previous().equals(Integer.valueOf(3))); + expect(it.previous().equals(Integer.valueOf(2))); + expect(it.previous().equals(Integer.valueOf(1))); + expect(! it.hasPrevious()); + + it = l.listIterator(2); + expect(it.previous().equals(Integer.valueOf(2))); + expect(it.previous().equals(Integer.valueOf(1))); + expect(! it.hasPrevious()); + + it = l.listIterator(1); + expect(it.previous().equals(Integer.valueOf(1))); + expect(! it.hasPrevious()); + } + + private static void testIterators2(java.util.List l) { + l.add(1); + l.add(2); + l.add(3); + + ListIterator it = l.listIterator(); + expect(it.next().equals(Integer.valueOf(1))); + it.remove(); + expect(it.next().equals(Integer.valueOf(2))); + it.remove(); + expect(it.next().equals(Integer.valueOf(3))); + it.remove(); + expect(! it.hasNext()); + expect(l.isEmpty()); + + l.add(1); + l.add(2); + l.add(3); + + it = l.listIterator(1); + expect(it.next().equals(Integer.valueOf(2))); + it.remove(); + expect(it.next().equals(Integer.valueOf(3))); + it.remove(); + expect(! it.hasNext()); + expect(l.size() == 1); + + l.add(2); + l.add(3); + + it = l.listIterator(2); + expect(it.next().equals(Integer.valueOf(3))); + it.remove(); + expect(! it.hasNext()); + expect(l.size() == 2); + + l.add(3); + + it = l.listIterator(3); + expect(it.previous().equals(Integer.valueOf(3))); + it.remove(); + expect(it.previous().equals(Integer.valueOf(2))); + it.remove(); + expect(it.previous().equals(Integer.valueOf(1))); + it.remove(); + expect(! it.hasPrevious()); + expect(l.isEmpty()); + + l.add(1); + l.add(2); + l.add(3); + + it = l.listIterator(2); + expect(it.previous().equals(Integer.valueOf(2))); + it.remove(); + expect(it.previous().equals(Integer.valueOf(1))); + it.remove(); + expect(! it.hasPrevious()); + expect(l.size() == 1); + + l.clear(); + l.add(1); + l.add(2); + l.add(3); + + it = l.listIterator(1); + expect(it.previous().equals(Integer.valueOf(1))); + it.remove(); + expect(! it.hasPrevious()); + expect(l.size() == 2); + } + + private static void testGrow() { + ArrayList foo = new ArrayList(2); + foo.add(0); + foo.add(1); + foo.add(2); // first grow + foo.add(3); + foo.add(4); // second grow + foo.add(5); + + for (int i = 0; i < foo.size(); i++) { + expect(i == foo.get(i)); + } + } + + private static void testRemove() { + ArrayList foo = new ArrayList(2); + foo.add("Uno"); + foo.add("Dos"); + foo.add("Tres"); + foo.add("Cuatro"); + + ArrayList fooToRemove = new ArrayList(2); + fooToRemove.add(foo.get(0)); + fooToRemove.add(foo.get(1)); + + for (String s : fooToRemove) { + foo.remove(s); + } + + expect(foo.size() == 2); + } + + public static void main(String args[]) { + ArrayList l = new ArrayList(); + l.add(1); l.add(2); l.add(3); l.add(4); l.add(5); + isEqual(printList(l), "1, 2, 3, 4, 5"); + l.add(0, 6); + isEqual(printList(l), "6, 1, 2, 3, 4, 5"); + l.add(2, 7); + isEqual(printList(l), "6, 1, 7, 2, 3, 4, 5"); + l.remove(1); + isEqual(printList(l), "6, 7, 2, 3, 4, 5"); + l.add(6, 8); + isEqual(printList(l), "6, 7, 2, 3, 4, 5, 8"); + Integer[] ints = new Integer[15]; + Integer[] z = l.toArray(ints); + expect(z == ints); + for (int i=0; i < z.length; i++) { + System.out.println(z[i]); + } + + testIterators(new ArrayList()); + testIterators(new LinkedList()); + + testIterators2(new ArrayList()); + testIterators2(new LinkedList()); + testGrow(); + testRemove(); + } +} diff --git a/sgx-jvm/avian/test/Logging.java b/sgx-jvm/avian/test/Logging.java new file mode 100644 index 0000000000..cbfc663d25 --- /dev/null +++ b/sgx-jvm/avian/test/Logging.java @@ -0,0 +1,150 @@ +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +public class Logging { + private static final Logger log = Logger.getLogger("Logging"); + + private static class MyHandler extends Handler { + private static final int NAME_WIDTH = 18; + private static final int METHOD_WIDTH = 20; + private static final int LEVEL_WIDTH = 8; + + public Object clone() { return this; } + public void close() { } + public void flush() { } + + private void maybeLogThrown(StringBuilder sb, Throwable t) { + if (t != null) { + sb.append("\nCaused by: "); + sb.append(t.getClass().getName()); + sb.append(": "); + sb.append(t.getMessage()); + sb.append('\n'); + + for (StackTraceElement elt : t.getStackTrace()) { + sb.append('\t'); + sb.append(elt.getClassName()); + sb.append('.'); + sb.append(elt.getMethodName()); + sb.append('('); + sb.append(elt.getFileName()); + sb.append(':'); + sb.append(elt.getLineNumber()); + sb.append(')'); + sb.append('\n'); + } + maybeLogThrown(sb, t.getCause()); + } + } + + private void indent(StringBuilder sb, int amount) { + do { + sb.append(' '); + } while (--amount > 0); + } + + public void publish(LogRecord r) { + StringBuilder sb = new StringBuilder(); + sb.append(r.getLoggerName()); + indent(sb, NAME_WIDTH - r.getLoggerName().length()); + String methodName = r.getSourceMethodName(); + if (methodName == null) { + methodName = ""; + } + sb.append(methodName); + indent(sb, METHOD_WIDTH - methodName.length()); + sb.append(r.getLevel().getName()); + indent(sb, LEVEL_WIDTH - r.getLevel().getName().length()); + sb.append(r.getMessage()); + maybeLogThrown(sb, r.getThrown()); + System.out.println(sb.toString()); + } + } + + public void run() { + log.info("Started run"); + a(); + log.log(Level.INFO, "Ended {}!", "run"); + } + + private void a() { + log.fine("Started a()"); + b(); + } + + private void b() { + log.info("Started b()"); + c(); + } + + private void c() { + log.warning("Started c()"); + try { + d(); + } catch (Exception ex) { + log.log(Level.SEVERE, "Exception caught in c", ex); + } + } + + private void d() throws Exception { + e(); + } + + private void e() throws Exception { + throw new Exception("Started here"); + } + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static final boolean useCustomHandler = true; + public static void main(String args[]) { + if (useCustomHandler) { + Logger root = Logger.getLogger(""); + root.addHandler(new MyHandler()); + for (Handler h : root.getHandlers()) root.removeHandler(h); + root.addHandler(new MyHandler()); + } + + Logging me = new Logging(); + me.run(); + + { final boolean[] logged = new boolean[1]; + Logger root = Logger.getLogger(""); + for (Handler h : root.getHandlers()) root.removeHandler(h); + root.setLevel(Level.FINER); + root.addHandler(new Handler() { + public void publish(LogRecord r) { + logged[0] = true; + } + + public void close() { } + public void flush() { } + }); + + Logger foo = Logger.getLogger("foo"); + expect(foo.getLevel() == null); + expect(foo.getParent() == root); + + foo.info("hi"); + expect(logged[0]); + + logged[0] = false; + foo.fine("hi"); + expect(logged[0]); + + logged[0] = false; + foo.finest("hi"); + expect(! logged[0]); + + root.setLevel(Level.FINEST); + + logged[0] = false; + foo.finest("hi"); + expect(logged[0]); + } + } +} diff --git a/sgx-jvm/avian/test/Longs.java b/sgx-jvm/avian/test/Longs.java new file mode 100644 index 0000000000..4d9395f7b0 --- /dev/null +++ b/sgx-jvm/avian/test/Longs.java @@ -0,0 +1,394 @@ +public class Longs { + private static volatile long volatileLong = getConstant(); + + private static long getConstant() { + return 0x123456789ABCDEFL; + } + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + public static long readLongFrom(java.io.InputStream in) + throws java.io.IOException + { + long b1 = in.read(); + long b2 = in.read(); + long b3 = in.read(); + long b4 = in.read(); + long b5 = in.read(); + long b6 = in.read(); + long b7 = in.read(); + long b8 = in.read(); + if (b8 == -1) throw new java.io.EOFException(); + return (long) ((b1 << 56) | (b2 << 48) | (b3 << 40) | (b4 << 32) | + (b5 << 24) | (b6 << 16) | (b7 << 8) | (b8)); + } + + public static void putInt(int val, byte[] dst, int offset) { + System.out.println("put " + val); + dst[offset] = (byte)((val >> 24) & 0xff); + dst[offset+1] = (byte)((val >> 16) & 0xff); + dst[offset+2] = (byte)((val >> 8) & 0xff); + dst[offset+3] = (byte)((val ) & 0xff); + } + + public static void putLong(long val, byte[] dst, int offset) { + putInt((int)(val >> 32), dst, offset); + putInt((int)val, dst, offset + 4); + } + + public static int getInt(byte[] src, int offset) { + int r = ((src[offset] & 0xFF) << 24) + | ((src[offset + 1] & 0xFF) << 16) + | ((src[offset + 2] & 0xFF) << 8) + | ((src[offset + 3] & 0xFF)); + System.out.println("get " + r); + return r; + } + + public static long getLong(byte[] src, int offset) { + return ((long) getInt(src, offset) << 32) + | ((long) getInt(src, offset + 4) & 0xffffffffL); + } + + private static long roundUp(long a, long b) { + a += b - 1L; + return a - (a % b); + } + + private static int negativeOne() { + return -1; + } + + private static long unsignedShiftRight32(long x) { + return x >>> 32; + } + + public static class Rectangle { + public long x; + public long y; + public long width; + public long height; + + public void setX(long x) { + this.x = x; + } + } + + public static void main(String[] args) throws Exception { + expect(volatileLong == getConstant()); + + { Rectangle r = new Rectangle(); + Rectangle.class.getMethod("setX", long.class).invoke(r, 42L); + expect(r.x == 42L); + } + + { long a = 0x1FFFFFFFFL; + long b = -1; + expect(a != b); + } + + expect(Math.abs(-123L) == 123L); + + expect(readLongFrom(new java.io.InputStream() { + int step; + + public int read() { + return ++step; + } + }) == 0x0102030405060708L); + + expect(((long) negativeOne()) == -1); + + { long foo = 25214903884L; + int radix = 10; + expect(foo > 0); + foo /= radix; + expect(foo > 0); + } + + expect(roundUp(156, 2) == 156); + expect(((int) roundUp(156, 2)) == 156); + + expect(Long.parseLong("25214903884") == 25214903884L); + + expect(Long.parseLong("-9223372036854775808") == -9223372036854775808L); + + expect(String.valueOf(25214903884L).equals("25214903884")); + + expect(String.valueOf(-9223372036854775808L).equals + ("-9223372036854775808")); + + { long a = -5; + long b = 2; + expect(a >> b == -5L >> 2); + expect(a >>> b == -5L >>> 2); + expect(a << b == -5L << 2); + expect(a + b == -5L + 2L); + expect(a - b == -5L - 2L); + expect(a * b == -5L * 2L); + expect(a / b == -5L / 2L); + expect(a % b == -5L % 2L); + expect((a & b) == (-5L & 2L)); + expect((a | b) == (-5L | 2L)); + expect((a ^ b) == (-5L ^ 2L)); + expect(-a == 5L); + expect(~a == ~-5L); + + a = 5; + b = 2; + expect(a >> b == 5L >> 2); + expect(a >>> b == 5L >>> 2); + expect(a << b == 5L << 2); + expect(a + b == 5L + 2L); + expect(a - b == 5L - 2L); + expect(a * b == 5L * 2L); + expect(a / b == 5L / 2L); + expect(a % b == 5L % 2L); + expect((a & b) == (5L & 2L)); + expect((a | b) == (5L | 2L)); + expect((a ^ b) == (5L ^ 2L)); + expect(-a == -5L); + expect(~a == ~5L); + } + + { long a = -25214903884L; + long b = 2; + expect(a >> b == -25214903884L >> 2); + expect(a >>> b == -25214903884L >>> 2); + expect(a << b == -25214903884L << 2); + expect(a + b == -25214903884L + 2L); + expect(a - b == -25214903884L - 2L); + expect(a * b == -25214903884L * 2L); + expect(a / b == -25214903884L / 2L); + expect(a % b == -25214903884L % 2L); + expect((a & b) == (-25214903884L & 2L)); + expect((a | b) == (-25214903884L | 2L)); + expect((a ^ b) == (-25214903884L ^ 2L)); + expect(-a == 25214903884L); + expect(~a == ~-25214903884L); + + a = 25214903884L; + b = 2; + expect(a >> b == 25214903884L >> 2); + expect(a >>> b == 25214903884L >>> 2); + expect(a << b == 25214903884L << 2); + expect(a + b == 25214903884L + 2L); + expect(a - b == 25214903884L - 2L); + expect(a * b == 25214903884L * 2L); + expect(a / b == 25214903884L / 2L); + expect(a % b == 25214903884L % 2L); + expect((a & b) == (25214903884L & 2L)); + expect((a | b) == (25214903884L | 2L)); + expect((a ^ b) == (25214903884L ^ 2L)); + expect(-a == -25214903884L); + expect(~a == ~25214903884L); + } + + { long b = 2; + expect((-25214903884L) >> b == -25214903884L >> 2); + expect((-25214903884L) >>> b == -25214903884L >>> 2); + expect((-25214903884L) << b == -25214903884L << 2); + expect((-25214903884L) + b == -25214903884L + 2L); + expect((-25214903884L) - b == -25214903884L - 2L); + expect((-25214903884L) * b == -25214903884L * 2L); + expect((-25214903884L) / b == -25214903884L / 2L); + expect((-25214903884L) % b == -25214903884L % 2L); + expect(((-25214903884L) & b) == (-25214903884L & 2L)); + expect(((-25214903884L) | b) == (-25214903884L | 2L)); + expect(((-25214903884L) ^ b) == (-25214903884L ^ 2L)); + + b = 2; + expect(25214903884L >> b == 25214903884L >> 2); + expect(25214903884L >>> b == 25214903884L >>> 2); + expect(25214903884L << b == 25214903884L << 2); + expect(25214903884L + b == 25214903884L + 2L); + expect(25214903884L - b == 25214903884L - 2L); + expect(25214903884L * b == 25214903884L * 2L); + expect(25214903884L / b == 25214903884L / 2L); + expect(25214903884L % b == 25214903884L % 2L); + expect((25214903884L & b) == (25214903884L & 2L)); + expect((25214903884L | b) == (25214903884L | 2L)); + expect((25214903884L ^ b) == (25214903884L ^ 2L)); + } + + { long a = 2L; + expect(a + (-25214903884L) == 2L + (-25214903884L)); + expect(a - (-25214903884L) == 2L - (-25214903884L)); + expect(a * (-25214903884L) == 2L * (-25214903884L)); + expect(a / (-25214903884L) == 2L / (-25214903884L)); + expect(a % (-25214903884L) == 2L % (-25214903884L)); + expect((a & (-25214903884L)) == (2L & (-25214903884L))); + expect((a | (-25214903884L)) == (2L | (-25214903884L))); + expect((a ^ (-25214903884L)) == (2L ^ (-25214903884L))); + + a = 2L; + expect(a + 25214903884L == 2L + 25214903884L); + expect(a - 25214903884L == 2L - 25214903884L); + expect(a * 25214903884L == 2L * 25214903884L); + expect(a / 25214903884L == 2L / 25214903884L); + expect(a % 25214903884L == 2L % 25214903884L); + expect((a & 25214903884L) == (2L & 25214903884L)); + expect((a | 25214903884L) == (2L | 25214903884L)); + expect((a ^ 25214903884L) == (2L ^ 25214903884L)); + } + + { long b = 2; + expect((-281474976710656L) >> b == -281474976710656L >> 2); + expect((-281474976710656L) >>> b == -281474976710656L >>> 2); + expect((-281474976710656L) << b == -281474976710656L << 2); + expect((-281474976710656L) + b == -281474976710656L + 2L); + expect((-281474976710656L) - b == -281474976710656L - 2L); + expect((-281474976710656L) * b == -281474976710656L * 2L); + expect((-281474976710656L) / b == -281474976710656L / 2L); + expect((-281474976710656L) % b == -281474976710656L % 2L); + expect(((-281474976710656L) & b) == (-281474976710656L & 2L)); + expect(((-281474976710656L) | b) == (-281474976710656L | 2L)); + expect(((-281474976710656L) ^ b) == (-281474976710656L ^ 2L)); + + b = 2; + expect(281474976710656L >> b == 281474976710656L >> 2); + expect(281474976710656L >>> b == 281474976710656L >>> 2); + expect(281474976710656L << b == 281474976710656L << 2); + expect(281474976710656L + b == 281474976710656L + 2L); + expect(281474976710656L - b == 281474976710656L - 2L); + expect(281474976710656L * b == 281474976710656L * 2L); + expect(281474976710656L / b == 281474976710656L / 2L); + expect(281474976710656L % b == 281474976710656L % 2L); + expect((281474976710656L & b) == (281474976710656L & 2L)); + expect((281474976710656L | b) == (281474976710656L | 2L)); + expect((281474976710656L ^ b) == (281474976710656L ^ 2L)); + } + + { long a = 2L; + expect(a + (-281474976710656L) == 2L + (-281474976710656L)); + expect(a - (-281474976710656L) == 2L - (-281474976710656L)); + expect(a * (-281474976710656L) == 2L * (-281474976710656L)); + expect(a / (-281474976710656L) == 2L / (-281474976710656L)); + expect(a % (-281474976710656L) == 2L % (-281474976710656L)); + expect((a & (-281474976710656L)) == (2L & (-281474976710656L))); + expect((a | (-281474976710656L)) == (2L | (-281474976710656L))); + expect((a ^ (-281474976710656L)) == (2L ^ (-281474976710656L))); + + a = 2L; + expect(a + 281474976710656L == 2L + 281474976710656L); + expect(a - 281474976710656L == 2L - 281474976710656L); + expect(a * 281474976710656L == 2L * 281474976710656L); + expect(a / 281474976710656L == 2L / 281474976710656L); + expect(a % 281474976710656L == 2L % 281474976710656L); + expect((a & 281474976710656L) == (2L & 281474976710656L)); + expect((a | 281474976710656L) == (2L | 281474976710656L)); + expect((a ^ 281474976710656L) == (2L ^ 281474976710656L)); + } + + { long x = 231; + expect((x >> 32) == 0); + expect((x >>> 32) == 0); + expect((x << 32) == 992137445376L); + + int shift = 32; + expect((x >> shift) == 0); + expect((x >>> shift) == 0); + expect((x << shift) == 992137445376L); + + long y = -231; + expect((y >> 32) == 0xffffffffffffffffL); + expect((y >>> 32) == 0xffffffffL); + } + + expect(Long.valueOf(231L) == 231L); + + { byte[] array = new byte[8]; + putLong(231, array, 0); + + expect((array[0] & 0xff) == 0); + expect((array[1] & 0xff) == 0); + expect((array[2] & 0xff) == 0); + expect((array[3] & 0xff) == 0); + expect((array[4] & 0xff) == 0); + expect((array[5] & 0xff) == 0); + expect((array[6] & 0xff) == 0); + expect((array[7] & 0xff) == 231); + + expect(getLong(array, 0) == 231); + } + + java.nio.ByteBuffer buffer = java.nio.ByteBuffer.allocate(8); + buffer.putLong(231); + buffer.flip(); + expect(buffer.getLong() == 231); + + expect(unsignedShiftRight32(231) == 0); + + { int[] x = new int[] { 1701899151 }; + int[] z = new int[x.length * 2]; + final long LONG_MASK = 0xffffffffL; + + int lastProductLowWord = 0; + for (int j=0, i=0; j>> 33); + z[i++] = (int) (product >>> 1); + lastProductLowWord = (int) product; + } + + expect(z[0] == 337192406); + expect(z[1] == -437261072); + } + + { long b = 1; + expect((b << 64) == 1); } + + { long b = 0xFFFFFFFFFFFFFFFFL; + expect((b >>> -1) == 1); } + + { long b = 0x10000000000L; + expect((b >> -63) == 0x8000000000L); } + + { long b = 1; int s = 64; + expect((b << s) == 1); } + + { long b = 0xFFFFFFFFFFFFFFFFL; int s = -1; + expect((b >>> s) == 1); } + + { long b = 0x10000000000L; int s = -63; + expect((b >> s) == 0x8000000000L); } + + { long b = 0xBEL; + expect((b & 0xFF) == 0xBEL); } + + { long b = 0xBEL; + expect((b >>> 0) == 0xBEL); } + + { long b = 0xBEL; + expect((b >> 0) == 0xBEL); } + + { long b = 0xBEL; + expect((b << 0) == 0xBEL); } + + { long b = 0xBEL; + expect(((b >>> 0) & 0xFF) == 0xBEL); } + + { long b = 0xBEL; int x = 0xFF; + expect((b & x) == 0xBEL); } + + { long b = 0xBEL; int x = 0; + expect((b >>> x) == 0xBEL); } + + { long b = 0xBEL; int x = 0; + expect((b >> x) == 0xBEL); } + + { long b = 0xBEL; int x = 0; + expect((b << x) == 0xBEL); } + + { long b = 0xBEL; int x = 0; int y = 0xFF; + expect(((b >>> x) & y) == 0xBEL); } + + { long b = 0xFFFFFFFFFFFFFFFFL; int s = 20; + expect((b >>> -s) == 0xFFFFF); + } + } + +} diff --git a/sgx-jvm/avian/test/MemoryRamp.java b/sgx-jvm/avian/test/MemoryRamp.java new file mode 100644 index 0000000000..2023a5483b --- /dev/null +++ b/sgx-jvm/avian/test/MemoryRamp.java @@ -0,0 +1,66 @@ +/** + * Demonstrates slow multithreaded memory access. + */ +public class MemoryRamp implements Runnable { + + private static final int ARRAY_SIZE = 10 * 1024 * 1024; //10 MB in byte + private static final boolean ACCESS_ARRAY = true; + + @Override + public void run() { + mem(); + } + + public static void main(String[] args) { + // get timing for single thread + long singleTime = mem(); + System.out.println("Single thread: " + singleTime + "ms"); + + // run the same method with different thread numbers + for (int threadCount : new int[] {2, 3, 4}) { + long time = memMulti(threadCount); + double timeFactor = 1.0 * singleTime * threadCount / time; + System.out.println(threadCount + " threads: " + time + "ms (" + timeFactor + "x)"); + } + } + + /** + * Creates and accesses a ARRAY_SIZE big byte[]. + * @return time to create and access array in milliseconds + */ + private static long mem() { + long start = System.currentTimeMillis(); + final byte[] array = new byte[ARRAY_SIZE]; + if (ACCESS_ARRAY) { + for (int i = 0; i < array.length; i++) { + //array[i] = (byte) 170; //write + byte x = array[i]; //read + } + } + return System.currentTimeMillis() - start; + } + + /** + * Starts multiple threads and runs mem() in each one. + * @return total time for all threads + */ + private static long memMulti(int numOfThreads) { + Thread[] threads = new Thread[numOfThreads]; + long start = System.currentTimeMillis(); + + for (int i = 0; i < threads.length; i++) { + threads[i] = new Thread(new MemoryRamp(), "mem-"); + threads[i].start(); + } + + try { + for (Thread thread : threads) { + thread.join(); + } + } + catch (InterruptedException iex) { + throw new RuntimeException(iex); + } + return System.currentTimeMillis() - start; + } +} diff --git a/sgx-jvm/avian/test/MessageFormatTest.java b/sgx-jvm/avian/test/MessageFormatTest.java new file mode 100644 index 0000000000..63914ee44e --- /dev/null +++ b/sgx-jvm/avian/test/MessageFormatTest.java @@ -0,0 +1,24 @@ +import java.text.MessageFormat; + +public class MessageFormatTest { + + private static void assertEquals(Object a, Object b) { + if(!a.equals(b)) { + throw new RuntimeException("[" + a + "] != [" + b + "]"); + } + } + + public static void main(String[] args) { + assertEquals("Hi there", MessageFormat.format("Hi there", "a")); + assertEquals("Hi there", MessageFormat.format("Hi {0}here", "t")); + assertEquals("Hi a!a!a", MessageFormat.format("Hi {0}!{0}!{0}", "a")); + assertEquals("Hi There", MessageFormat.format("{1} {0}", "There", "Hi")); + assertEquals("6 There 4", MessageFormat.format("{1} {2} {0}", 4, 6, "There")); + assertEquals("Zero and {0} aren't the same", MessageFormat.format("{0} and '{0}' aren''t the same","Zero")); + assertEquals("There are six grapes", MessageFormat.format("There are {0} grapes", "six")); + assertEquals("3 + 2 = 5", MessageFormat.format("{2} + {1} = {0}", 5, 2, 3)); + assertEquals("again and again and again", MessageFormat.format("{0} and {0} and {0}", "again")); + assertEquals("Joe's age is 30, not {0}", MessageFormat.format("Joe''s age is {0}, not '{0}'", 30)); + } + +} \ No newline at end of file diff --git a/sgx-jvm/avian/test/Misc.java b/sgx-jvm/avian/test/Misc.java new file mode 100644 index 0000000000..19195277aa --- /dev/null +++ b/sgx-jvm/avian/test/Misc.java @@ -0,0 +1,367 @@ +import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; + +public class Misc { + private static class μClass { + public int μField; + + public void μMethod(int i) { + μField = i; + } + } + + private interface Bar { + public int baz(); + } + + private static abstract class Bim implements Bar { } + + private static class Baz extends Bim { + public int baz() { + return 42; + } + } + + private static class Static { + static { + staticRan = true; + } + + public static void run() { } + } + + private static boolean staticRan; + + private static int alpha; + private static int beta; + private static byte byte1, byte2, byte3; + + private static volatile int volatileStatic; + + private static volatile long volatileStaticLong; + + private final int NonStaticConstant = 42; + + private int gamma; + private int pajama; + private boolean boolean1; + private boolean boolean2; + private long time; + private volatile int volatileMember; + + public Misc() { + expect(! boolean1); + expect(! boolean2); + + time = 0xffffffffffffffffL; + + expect(! boolean1); + expect(! boolean2); + } + + private String foo(String s) { + return s; + } + + public String bar(String s) { + return s; + } + + private static String baz(String s) { + return s; + } + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private synchronized byte sync() { + byte[] array = new byte[123]; + return array[42]; + } + + private static synchronized byte syncStatic(boolean throw_) { + byte[] array = new byte[123]; + if (throw_) { + throw new RuntimeException(); + } else { + return array[42]; + } + } + + public String toString() { + return super.toString(); + } + + private static int zap() { + return 42; + } + + private static int zip() { + return 5 + zap(); + } + + private static int zup() { + return zap() + 5; + } + + private static class Foo { + public int a; + public int b; + public int c; + public int[] array; + } + + private static int bar(int a, int b, int c) { + return a + b + c; + } + + private static Object gimmeNull() { + return null; + } + + private static Object queryDefault(Object default_) { + Object o = gimmeNull(); + return (o == null ? default_ : o); + } + + private static class Zam { + public void bim() { } + } + + private static class Zim { + public Object zum() { + return null; + } + } + + private static Zim zim = new Zim(); + + private static void zam() { + Zam z; + while ((z = (Zam) zim.zum()) != null) { + z.bim(); + } + } + + private static synchronized void testStaticNotify() { + Misc.class.notify(); + } + + public static void main(String[] args) throws Exception { + zam(); + + Bim bim = new Baz(); + expect(bim.baz() == 42); + + expect(queryDefault(new Object()) != null); + + { Foo foo = new Foo(); + int x = foo.a + foo.b + foo.c; + bar(foo.a, foo.b, foo.c); + } + + byte2 = 0; + expect(byte2 == 0); + + boolean v = Boolean.valueOf("true"); + + ClassLoader.getSystemClassLoader().toString(); + + testStaticNotify(); + + { Misc m = new Misc(); + m.toString(); + + expect(m.NonStaticConstant == 42); + + expect(m.time == 0xffffffffffffffffL); + long t = m.time; + expect(t == 0xffffffffffffffffL); + + String s = "hello"; + m.foo(s); + m.bar(s); + baz(s); + + m.sync(); + syncStatic(false); + try { + syncStatic(true); + } catch (RuntimeException e) { + e.printStackTrace(); + } + + int d = alpha; + beta = 42; + alpha = 43; + volatileStatic = 55; + volatileStaticLong = 9L; + int e = beta; + int f = alpha; + m.volatileMember = 23; + m.gamma = 44; + m.volatileMember = 27; + + expect(beta == 42); + expect(alpha == 43); + expect(m.gamma == 44); + expect(volatileStatic == 55); + expect(volatileStaticLong == 9L); + expect(m.volatileMember == 27); + } + + expect(zip() == 47); + expect(zup() == 47); + + { + Object a = new Object(); + Object b = new Object(); + expect(a != b); + + Object c = a; + Object d = b; + expect(c != d); + + c = (c == a) ? b : a; + d = (d == a) ? b : a; + + expect(c != d); + } + + { Foo foo = new Foo(); + foo.array = new int[3]; + foo.a = (foo.a + 1) % foo.array.length; + } + + { boolean foo = false; + boolean iconic = false; + do { + zap(); + iconic = foo ? true : false; + } while (foo); + zap(); + } + + { int x = 0; + if (x == 0) { + x = 1; + do { + int y = x; + x = 1; + } while (x != 1); + } + } + + System.out.println(new java.util.Date().toString()); + + System.out.println('x'); + System.out.println(true); + System.out.println(42); + System.out.println(123456789012345L); + System.out.println(75.62); + System.out.println(75.62d); + System.out.println(new char[] { 'h', 'i' }); + + expect(! (((Object) new int[0]) instanceof Object[])); + + { μClass μInstance = new μClass(); + μInstance.μMethod(8933); + expect(μInstance.μField == 8933); + } + + expect(new int[0] instanceof Cloneable); + expect(new int[0] instanceof java.io.Serializable); + + expect(new Object[0] instanceof Cloneable); + expect(new Object[0] instanceof java.io.Serializable); + + expect((Baz.class.getModifiers() & java.lang.reflect.Modifier.STATIC) + != 0); + + expect((Protected.class.getModifiers() & java.lang.reflect.Modifier.PUBLIC) + == 0); + + try { + int count = 0; + boolean test = false, extraDir = false; + ClassLoader loader = Misc.class.getClassLoader(); + Enumeration resources = loader.getResources("multi-classpath-test.txt"); + while (resources.hasMoreElements()) { + ++count; + String url = resources.nextElement().toString(); + if (url.contains("extra-dir")) { + extraDir = true; + } else if (url.contains("test")) { + test = true; + } + } + // This test is only relevant if multi-classpath-test.txt + // actually exists in somewhere under the classpath from which + // Misc.class was loaded. Since we run this test from an + // AOT-compiled boot image as well as straight from the + // filesystem, and the boot image does not contain + // multi-classpath-test.txt, we'll skip the test if it's not + // present. + if (count != 0) { + expect(count == 2); + expect(test); + expect(extraDir); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + + // as of this writing, we don't support URLs on Android, since it + // pulls in a third-party library we don't include: + if (! "http://www.android.com/".equals + (System.getProperty("java.vendor.url"))) + { + expect(new URL("http://oss.readytalk.com") + .getHost().equals("oss.readytalk.com")); + } + + expect(java.util.Arrays.equals + (new byte[] { 0, 0, 0, 0 }, + java.net.InetAddress.getByName("0.0.0.0").getAddress())); + + expect(! staticRan); + Static.run(); + expect(staticRan); + + expect(System.getProperty("java.class.path").equals + (System.getProperties().getProperty("java.class.path"))); + + expect(System.getProperty("path.separator").equals + (System.getProperties().getProperty("path.separator"))); + + expect(System.getProperty("user.dir").equals + (System.getProperties().getProperty("user.dir"))); + + expect(System.getProperty("java.io.tmpdir").equals + (System.getProperties().getProperty("java.io.tmpdir"))); + + System.setProperty("buzzy.buzzy.bim.bam", "dippy dopey flim flam"); + + expect(System.getProperty("buzzy.buzzy.bim.bam").equals + (System.getProperties().getProperty("buzzy.buzzy.bim.bam"))); + + expect(System.getProperty("buzzy.buzzy.bim.bam").equals + ("dippy dopey flim flam")); + + System.getProperties().put("buzzy.buzzy.bim.bam", "yippy yappy yin yang"); + + expect(System.getProperty("buzzy.buzzy.bim.bam").equals + (System.getProperties().getProperty("buzzy.buzzy.bim.bam"))); + + expect(System.getProperty("buzzy.buzzy.bim.bam").equals + ("yippy yappy yin yang")); + + // just test that it's there; don't care what it returns: + Runtime.getRuntime().totalMemory(); + Runtime.getRuntime().maxMemory(); + } + + protected class Protected { } +} diff --git a/sgx-jvm/avian/test/NullPointer.java b/sgx-jvm/avian/test/NullPointer.java new file mode 100644 index 0000000000..8d3abdc6fa --- /dev/null +++ b/sgx-jvm/avian/test/NullPointer.java @@ -0,0 +1,132 @@ +public class NullPointer { + private int x; + private Object y; + + private static void throw_(Object o) { + o.toString(); + } + + private static void throwAndCatch(Object o) { + try { + o.toString(); + throw new RuntimeException(); + } catch (NullPointerException e) { + e.printStackTrace(); + } + } + + public static void main(String[] args) { + try { + ((Object) null).getClass(); + } catch (Exception e) { + e.printStackTrace(); + } + + try { + throw_(null); + throw new RuntimeException(); + } catch (NullPointerException e) { + e.printStackTrace(); + } + + throwAndCatch(null); + + // invokeinterface + try { + ((Runnable) null).run(); + throw new RuntimeException(); + } catch (NullPointerException e) { + e.printStackTrace(); + } + + // invokevirtual + try { + ((Object) null).toString(); + throw new RuntimeException(); + } catch (NullPointerException e) { + e.printStackTrace(); + } + + // arraylength + try { + int a = ((byte[]) null).length; + throw new RuntimeException(); + } catch (NullPointerException e) { + e.printStackTrace(); + } + + // iaload + try { + int a = ((byte[]) null)[42]; + throw new RuntimeException(); + } catch (NullPointerException e) { + e.printStackTrace(); + } + + // aaload + try { + Object a = ((Object[]) null)[42]; + throw new RuntimeException(); + } catch (NullPointerException e) { + e.printStackTrace(); + } + + // getfield (int) + try { + int a = ((NullPointer) null).x; + throw new RuntimeException(); + } catch (NullPointerException e) { + e.printStackTrace(); + } + + // getfield (Object) + try { + Object a = ((NullPointer) null).y; + throw new RuntimeException(); + } catch (NullPointerException e) { + e.printStackTrace(); + } + + // iastore + try { + ((byte[]) null)[42] = 42; + throw new RuntimeException(); + } catch (NullPointerException e) { + e.printStackTrace(); + } + + // aastore + try { + ((Object[]) null)[42] = null; + throw new RuntimeException(); + } catch (NullPointerException e) { + e.printStackTrace(); + } + + // putfield (int) + try { + ((NullPointer) null).x = 42; + throw new RuntimeException(); + } catch (NullPointerException e) { + e.printStackTrace(); + } + + // putfield (Object) + try { + ((NullPointer) null).y = null; + throw new RuntimeException(); + } catch (NullPointerException e) { + e.printStackTrace(); + } + + // monitorenter + try { + synchronized ((Object) null) { + int a = 42; + } + throw new RuntimeException(); + } catch (NullPointerException e) { + e.printStackTrace(); + } + } +} diff --git a/sgx-jvm/avian/test/Observe.java b/sgx-jvm/avian/test/Observe.java new file mode 100644 index 0000000000..75dc0063a8 --- /dev/null +++ b/sgx-jvm/avian/test/Observe.java @@ -0,0 +1,88 @@ +import java.util.Observer; +import java.util.Observable; + +public class Observe { + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static class MyObservable extends Observable { + private String value; + + public MyObservable(String value) { + this.value = value; + } + + public void set(String newValue) { + if(!value.equals(newValue)) { + value = newValue; + setChanged(); + notifyObservers(value); + } + } + } + + private static class MyObserver implements Observer { + private int count = 0; + private Observable expectedObs; + private Object expectedValue = null; + private boolean expected = false; + + public MyObserver(Observable expectedObs) { + this.expectedObs = expectedObs; + } + + public void update(Observable obs, Object value) { + expect(expectedObs == expectedObs); + expect(expected); + expect(value == expectedValue); + expectNothing(); + } + + public void noUpdate() { + expect(!expected); + } + + public void expect(Object value) { + expected = true; + expectedValue = value; + } + + public void expectNothing() { + expected = false; + } + + } + + public static void main(String[] args) { + MyObservable obs = new MyObservable("test"); + MyObserver o = new MyObserver(obs); + MyObserver o2 = new MyObserver(obs); + + obs.set("a"); + + obs.addObserver(o); + o.expect("b"); + obs.set("b"); + o.noUpdate(); + + obs.addObserver(o2); + o.expect("c"); + o2.expect("c"); + obs.set("c"); + o.noUpdate(); + o2.noUpdate(); + + obs.deleteObserver(o); + o.expectNothing(); + o2.expect("d"); + obs.set("d"); + o2.noUpdate(); + + obs.deleteObserver(o2); + o.expectNothing(); + o2.expectNothing(); + obs.set("e"); + + } +} \ No newline at end of file diff --git a/sgx-jvm/avian/test/OutOfMemory.java b/sgx-jvm/avian/test/OutOfMemory.java new file mode 100644 index 0000000000..fbcb614fdf --- /dev/null +++ b/sgx-jvm/avian/test/OutOfMemory.java @@ -0,0 +1,62 @@ +public class OutOfMemory { + // assume a 128MB heap size: + private static final int Padding = 120 * 1024 * 1024; + + private static class Node { + Object value; + Node next; + } + + private static void bigObjects() { + Object[] root = null; + while (true) { + Object[] x = new Object[1024 * 1024]; + x[0] = root; + root = x; + } + } + + private static void littleObjects() { + byte[] padding = new byte[Padding]; + Node root = null; + while (true) { + Node x = new Node(); + x.next = root; + root = x; + } + } + + private static void bigAndLittleObjects() { + byte[] padding = new byte[Padding]; + Node root = null; + while (true) { + Node x = new Node(); + x.value = new Object[1024 * 1024]; + x.next = root; + root = x; + } + } + + public static void main(String[] args) { + try { + bigObjects(); + throw new RuntimeException(); + } catch (OutOfMemoryError e) { + e.printStackTrace(); + } + + try { + littleObjects(); + throw new RuntimeException(); + } catch (OutOfMemoryError e) { + e.printStackTrace(); + } + + try { + bigAndLittleObjects(); + throw new RuntimeException(); + } catch (OutOfMemoryError e) { + e.printStackTrace(); + } + } +} diff --git a/sgx-jvm/avian/test/Processes.java b/sgx-jvm/avian/test/Processes.java new file mode 100644 index 0000000000..372513f0c1 --- /dev/null +++ b/sgx-jvm/avian/test/Processes.java @@ -0,0 +1,31 @@ +import java.io.IOException; + +public class Processes { + public static void main(String[] args) { + long start = System.currentTimeMillis(); + try { + final Process p = Runtime.getRuntime().exec("sleep 10"); + new Thread() { + public void run() { + try { + Thread.sleep(100); + } catch(InterruptedException e) { + // ignore + } + p.destroy(); + } + }.start(); + try { + p.waitFor(); + } catch(InterruptedException e) { + // ignore + } + long stop = System.currentTimeMillis(); + if(stop - start > 5000) { + throw new RuntimeException("test failed; we didn't kill the process..."); + } + } catch(IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/sgx-jvm/avian/test/Proxies.java b/sgx-jvm/avian/test/Proxies.java new file mode 100644 index 0000000000..e52a0e7d68 --- /dev/null +++ b/sgx-jvm/avian/test/Proxies.java @@ -0,0 +1,42 @@ +import java.lang.reflect.Proxy; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; + +public class Proxies { + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + public static void main(String[] args) { + Foo foo = (Foo) Proxy.newProxyInstance + (Proxies.class.getClassLoader(), new Class[] { Foo.class }, + new InvocationHandler() { + public Object invoke(Object proxy, Method method, Object[] arguments) + { + if (method.getName().equals("bar")) { + return "bam"; + } else if (method.getName().equals("baz")) { + return ((Integer) arguments[0]) + 1; + } else if (method.getName().equals("bim")) { + return ((Long) arguments[0]) - 1L; + } else if (method.getName().equals("boom")) { + return ((String) arguments[0]).substring(1); + } else { + throw new IllegalArgumentException(); + } + } + }); + + expect(foo.bar().equals("bam")); + expect(foo.baz(42) == 43); + expect(foo.bim(42L) == 41L); + expect(foo.boom("hello").equals("ello")); + } + + private interface Foo { + public String bar(); + public int baz(int v); + public long bim(long v); + public String boom(String s); + } +} diff --git a/sgx-jvm/avian/test/QueueHelper.java b/sgx-jvm/avian/test/QueueHelper.java new file mode 100644 index 0000000000..3c9cc4c6fb --- /dev/null +++ b/sgx-jvm/avian/test/QueueHelper.java @@ -0,0 +1,149 @@ +import java.util.LinkedList; +import java.util.NoSuchElementException; +import java.util.Queue; + +public class QueueHelper { + private static void verify(boolean val) { + if (! val) { + throw new RuntimeException(); + } + } + + public static void main(String args[]) { + // prevents unit test failure + } + + public static void sizeTest(Queue q) { + verify(q.size() == 0); + + q.add(new Object()); + verify(q.size() == 1); + } + + public static void isEmptyTest(Queue q) { + verify(q.isEmpty()); + + q.add(new Object()); + verify(! q.isEmpty()); + } + + public static void addTest(Queue q) { + Object testObject = new Object(); + q.add(testObject); + + verify(q.size() == 1); + verify(q.peek() == testObject); + } + + public static void addAllTest(Queue q) { + LinkedList toAdd = new LinkedList(); + toAdd.add(new Object()); + toAdd.add(new Object()); + + q.addAll(toAdd); + + verify(q.size() == toAdd.size()); + while (! q.isEmpty()) { + verify(q.remove() == toAdd.remove()); + } + } + + public static void elementTest(Queue q) { + Object testObject = new Object(); + q.add(testObject); + + verify(q.element() == testObject); + } + + public static void elementFail(Queue q) { + try { + q.element(); + throw new RuntimeException("Exception should have thrown"); + } catch (NoSuchElementException e) { + // expected + } + } + + public static void removeTest(Queue q) { + Object testObject = new Object(); + q.add(testObject); + + verify(q.remove() == testObject); + } + + public static void removeEmptyFail(Queue q) { + try { + q.remove(); + throw new RuntimeException("Exception should have thrown"); + } catch (NoSuchElementException e) { + // expected + } + } + + public static void containsTest(Queue q) { + Object testObject = new Object(); + + verify(! q.contains(testObject)); + + q.add(testObject); + verify(q.contains(testObject)); + } + + public static void containsAllTest(Queue q) { + Object testObject = new Object(); + q.add(testObject); + + LinkedList testList = new LinkedList(); + testList.add(testObject); + testList.add(new Object()); + + verify(! q.containsAll(testList)); + + q.addAll(testList); + verify(q.containsAll(testList)); + } + + public static void removeObjectTest(Queue q) { + Object testObject = new Object(); + + verify(! q.remove(testObject)); + + q.add(testObject); + verify(q.remove(testObject)); + } + + public static void removeAllTest(Queue q) { + Object testObject = new Object(); + q.add(testObject); + + LinkedList testList = new LinkedList(); + testList.add(testObject); + testList.add(new Object()); + + verify(q.removeAll(testList)); + + q.addAll(testList); + verify(q.removeAll(testList)); + } + + public static void clearTest(Queue q) { + q.add(new Object()); + + q.clear(); + + verify(q.isEmpty()); + } + + public static void toArrayTest(Queue q) { + if (q.toArray().length != 0) { + throw new RuntimeException(); + } + + Object testObject = new Object(); + q.add(testObject); + + Object[] result = q.toArray(); + verify(result.length == 1); + verify(result[0] == testObject); + } +} diff --git a/sgx-jvm/avian/test/References.java b/sgx-jvm/avian/test/References.java new file mode 100644 index 0000000000..5557e5db1a --- /dev/null +++ b/sgx-jvm/avian/test/References.java @@ -0,0 +1,72 @@ +import java.lang.ref.ReferenceQueue; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.lang.ref.PhantomReference; +import java.util.WeakHashMap; + +public class References { + public static void main(String[] args) { + Object a = new Object(); + Object b = new Object(); + Object c = new Object(); + Object d = new Object(); + Object e = new Object(); + Object f = new Object(); + + ReferenceQueue q = new ReferenceQueue(); + + Reference ar = new WeakReference(a); + Reference br = new WeakReference(b, q); + Reference cr = new WeakReference(c, q); + Reference dr = new PhantomReference(d, q); + Reference er = new MyReference(e, q, "foo"); + + WeakHashMap map = new WeakHashMap(); + map.put(new Key("foo"), f); + + a = b = c = d = e = cr = null; + + System.out.println("a: " + ar.get()); + System.out.println("b: " + br.get()); + System.out.println("d: " + dr.get()); + System.out.println("e: " + er.get()); + System.out.println("f: " + map.get(new Key("foo"))); + + System.gc(); + + System.out.println("a: " + ar.get()); + System.out.println("b: " + br.get()); + System.out.println("d: " + dr.get()); + System.out.println("e: " + er.get()); + System.out.println("f: " + map.get(new Key("foo"))); + + for (Reference r = q.poll(); r != null; r = q.poll()) { + System.out.println("polled: " + r.get()); + } + } + + private static class MyReference extends WeakReference { + private final Object foo; + + public MyReference(Object target, ReferenceQueue queue, Object foo) { + super(target, queue); + this.foo = foo; + } + } + + private static class Key { + private final String name; + + public Key(String name) { + this.name = name; + } + + public int hashCode() { + return name.hashCode(); + } + + public boolean equals(Object o) { + return o instanceof Key && ((Key) o).name.equals(name); + } + } +} diff --git a/sgx-jvm/avian/test/Reflection.java b/sgx-jvm/avian/test/Reflection.java new file mode 100644 index 0000000000..39dc5c85a8 --- /dev/null +++ b/sgx-jvm/avian/test/Reflection.java @@ -0,0 +1,337 @@ +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.InvocationTargetException; + +public class Reflection { + public static boolean booleanMethod() { + return true; + } + + public static byte byteMethod() { + return 1; + } + + public static char charMethod() { + return '2'; + } + + public static short shortMethod() { + return 3; + } + + public static int intMethod() { + return 4; + } + + public static float floatMethod() { + return 5.0f; + } + + public static long longMethod() { + return 6; + } + + public static double doubleMethod() { + return 7.0; + } + + public static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static class Hello { + private class World { } + } + + private static void innerClasses() throws Exception { + Class c = Reflection.class; + Class[] inner = c.getDeclaredClasses(); + expect(2 == inner.length); + expect(Hello.class == inner[0] + || Hello.class == inner[1]); + } + + private int egads; + + private static void annotations() throws Exception { + Field egads = Reflection.class.getDeclaredField("egads"); + expect(egads.getAnnotation(Deprecated.class) == null); + } + + private Integer[] array; + + private Integer integer; + + public static Hello>.World> pinky; + + private static void genericType() throws Exception { + Field field = Reflection.class.getDeclaredField("egads"); + expect(field.getGenericType() == Integer.TYPE); + + field = Reflection.class.getField("pinky"); + expect("Reflection$Hello$World".equals(field.getType().getName())); + expect(field.getGenericType() instanceof ParameterizedType); + ParameterizedType type = (ParameterizedType) field.getGenericType(); + + expect(type.getRawType() instanceof Class); + Class clazz = (Class) type.getRawType(); + expect("Reflection$Hello$World".equals(clazz.getName())); + + expect(type.getOwnerType() instanceof ParameterizedType); + ParameterizedType owner = (ParameterizedType) type.getOwnerType(); + clazz = (Class) owner.getRawType(); + expect(clazz == Hello.class); + + Type[] args = type.getActualTypeArguments(); + expect(1 == args.length); + expect(args[0] instanceof ParameterizedType); + + ParameterizedType arg = (ParameterizedType) args[0]; + expect(arg.getRawType() instanceof Class); + clazz = (Class) arg.getRawType(); + expect("Reflection$Hello".equals(clazz.getName())); + + args = arg.getActualTypeArguments(); + expect(1 == args.length); + expect(args[0] == String.class); + } + + public static void throwOOME() { + throw new OutOfMemoryError(); + } + + public static void classType() throws Exception { + // Class types + expect(!Reflection.class.isAnonymousClass()); + expect(!Reflection.class.isLocalClass()); + expect(!Reflection.class.isMemberClass()); + + expect(Reflection.Hello.class.isMemberClass()); + + Cloneable anonymousLocal = new Cloneable() {}; + expect(anonymousLocal.getClass().isAnonymousClass()); + + class NamedLocal {} + expect(NamedLocal.class.isLocalClass()); + } + + public static void main(String[] args) throws Exception { + innerClasses(); + annotations(); + genericType(); + classType(); + + Class system = Class.forName("java.lang.System"); + Field out = system.getDeclaredField("out"); + Class output = Class.forName("java.io.PrintStream"); + Method println = output.getDeclaredMethod("println", String.class); + + println.invoke(out.get(null), "Hello, World!"); + + expect((Boolean) Reflection.class.getMethod("booleanMethod").invoke(null)); + + expect(1 == (Byte) Reflection.class.getMethod("byteMethod").invoke(null)); + + expect('2' == (Character) Reflection.class.getMethod + ("charMethod").invoke(null)); + + expect(3 == (Short) Reflection.class.getMethod + ("shortMethod").invoke(null)); + + expect(4 == (Integer) Reflection.class.getMethod + ("intMethod").invoke(null)); + + expect(5.0 == (Float) Reflection.class.getMethod + ("floatMethod").invoke(null)); + + expect(6 == (Long) Reflection.class.getMethod + ("longMethod").invoke(null)); + + expect(7.0 == (Double) Reflection.class.getMethod + ("doubleMethod").invoke(null)); + + { Class[][] array = new Class[][] { { Class.class } }; + expect("[Ljava.lang.Class;".equals(array[0].getClass().getName())); + expect(Class[].class == array[0].getClass()); + expect(array.getClass().getComponentType() == array[0].getClass()); + } + + { Reflection r = new Reflection(); + expect(r.egads == 0); + + Reflection.class.getDeclaredField("egads").set(r, (Integer)42); + expect(((Integer)Reflection.class.getDeclaredField("egads").get(r)) == 42); + + Reflection.class.getDeclaredField("egads").setInt(r, 43); + expect(Reflection.class.getDeclaredField("egads").getInt(r) == 43); + + Integer[] array = new Integer[0]; + Reflection.class.getDeclaredField("array").set(r, array); + expect(Reflection.class.getDeclaredField("array").get(r) == array); + + try { + Reflection.class.getDeclaredField("array").set(r, new Object()); + expect(false); + } catch (IllegalArgumentException e) { + // cool + } + + Integer integer = 45; + Reflection.class.getDeclaredField("integer").set(r, integer); + expect(Reflection.class.getDeclaredField("integer").get(r) == integer); + + try { + Reflection.class.getDeclaredField("integer").set(r, new Object()); + expect(false); + } catch (IllegalArgumentException e) { + // cool + } + + try { + Reflection.class.getDeclaredField("integer").set + (new Object(), integer); + expect(false); + } catch (IllegalArgumentException e) { + // cool + } + + try { + Reflection.class.getDeclaredField("integer").get(new Object()); + expect(false); + } catch (IllegalArgumentException e) { + // cool + } + } + + try { + Foo.class.getMethod("foo").invoke(null); + expect(false); + } catch (ExceptionInInitializerError e) { + expect(e.getCause() instanceof MyException); + } + + try { + Foo.class.getConstructor().newInstance(); + expect(false); + } catch (NoClassDefFoundError e) { + // cool + } + + try { + Foo.class.getField("foo").get(null); + expect(false); + } catch (NoClassDefFoundError e) { + // cool + } + + try { + Foo.class.getField("foo").set(null, (Integer)42); + expect(false); + } catch (NoClassDefFoundError e) { + // cool + } + + try { + Foo.class.getField("foo").set(null, new Object()); + expect(false); + } catch (IllegalArgumentException e) { + // cool + } catch (NoClassDefFoundError e) { + // cool + } + + { Method m = Reflection.class.getMethod("throwOOME"); + try { + m.invoke(null); + } catch(Throwable t) { + expect(t.getClass() == InvocationTargetException.class); + } + } + + expect((Foo.class.getMethod("toString").getModifiers() + & Modifier.PUBLIC) != 0); + + expect(avian.TestReflection.get(Baz.class.getField("foo"), new Baz()) + .equals(42)); + expect((Baz.class.getModifiers() & Modifier.PUBLIC) == 0); + + expect(B.class.getDeclaredMethods().length == 0); + + new Runnable() { + public void run() { + expect(getClass().getDeclaringClass() == null); + } + }.run(); + + expect(avian.testing.annotations.Test.class.getPackage().getName().equals + ("avian.testing.annotations")); + + expect(Baz.class.getField("foo").getAnnotation(Ann.class) == null); + expect(Baz.class.getField("foo").getAnnotations().length == 0); + + expect(new Runnable() { public void run() { } }.getClass() + .getEnclosingClass().equals(Reflection.class)); + + expect(new Runnable() { public void run() { } }.getClass() + .getEnclosingMethod().equals + (Reflection.class.getMethod + ("main", new Class[] { String[].class }))); + + Slithy.class.getMethod("tove", Gybe.class); + + try { + Slithy.class.getMethod("tove", Bandersnatch.class); + expect(false); + } catch (NoSuchMethodException e) { + // cool + } + + expect(C.class.getInterfaces().length == 1); + expect(C.class.getInterfaces()[0].equals(B.class)); + } + + protected static class Baz { + public int foo = 42; + } +} + +class Bandersnatch { } + +class Gybe extends Bandersnatch { } + +class Slithy { + public static void tove(Gybe gybe) { + // ignore + } +} + +class Foo { + static { + if (true) throw new MyException(); + } + + public Foo() { } + + public static int foo; + + public static void foo() { + // ignore + } +} + +class MyException extends RuntimeException { } + +interface A { + void foo(); +} + +interface B extends A { } + +class C implements B { + public void foo() { } +} + +@interface Ann { } diff --git a/sgx-jvm/avian/test/Regex.java b/sgx-jvm/avian/test/Regex.java new file mode 100644 index 0000000000..1409c67e3e --- /dev/null +++ b/sgx-jvm/avian/test/Regex.java @@ -0,0 +1,102 @@ +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Regex { + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static Matcher getMatcher(String regex, String string) { + return Pattern.compile(regex).matcher(string); + } + + private static void expectMatch(String regex, String string) { + expect(getMatcher(regex, string).matches()); + } + + private static void expectNoMatch(String regex, String string) { + expect(!getMatcher(regex, string).matches()); + } + + private static void expectGroups(String regex, String string, + String... groups) { + Matcher matcher = getMatcher(regex, string); + expect(matcher.matches()); + expect(matcher.groupCount() == groups.length); + for (int i = 1; i <= groups.length; ++i) { + if (groups[i - 1] == null) { + expect(matcher.group(i) == null); + } else { + expect(groups[i - 1].equals(matcher.group(i))); + } + } + } + + private static void expectFind(String regex, String string, + String... matches) + { + Matcher matcher = getMatcher(regex, string); + int i = 0; + while (i < matches.length) { + expect(matcher.find()); + expect(matches[i++].equals(matcher.group())); + } + expect(!matcher.find()); + } + + private static void expectSplit(String regex, String string, + String... list) + { + String[] array = Pattern.compile(regex).split(string); + expect(array.length == list.length); + for (int i = 0; i < list.length; ++ i) { + expect(list[i].equals(array[i])); + } + } + + public static void main(String[] args) { + expectMatch("a(bb)?a", "abba"); + expectNoMatch("a(bb)?a", "abbba"); + expectNoMatch("a(bb)?a", "abbaa"); + expectGroups("a(a*?)(a?)(a??)(a+)(a*)a", "aaaaaa", "", "a", "", "aaa", ""); + expectMatch("...", "abc"); + expectNoMatch(".", "\n"); + expectGroups("a(bb)*a", "abbbba", "bb"); + expectGroups("a(bb)?(bb)+a", "abba", null, "bb"); + expectFind(" +", "Hello , world! ", " ", " ", " "); + expectMatch("[0-9A-Fa-f]+", "08ef"); + expectNoMatch("[0-9A-Fa-f]+", "08@ef"); + expectGroups("(?:a)", "a"); + expectGroups("a|(b|c)", "a", (String)null); + expectGroups("a|(b|c)", "c", "c"); + expectGroups("(?=a)a", "a"); + expectGroups(".*(o)(?<=[A-Z][a-z]{1,4})", "Hello", "o"); + expectNoMatch("(?!a).", "a"); + expectMatch("[\\d]", "0"); + expectMatch("\\0777", "?7"); + expectMatch("\\a", "\007"); + expectMatch("\\\\", "\\"); + expectMatch("\\x4A", "J"); + expectMatch("\\x61", "a"); + expectMatch("\\078", "\0078"); + expectSplit("(?<=\\w)(?=\\W)|(?<=\\W)(?=\\w)", "a + b * x", + "a", " + ", "b", " * ", "x"); + expectMatch("[0-9[def]]", "f"); + expectNoMatch("[a-z&&[^d-f]]", "f"); + expectSplit("^H", "Hello\nHobbes!", "", "ello\nHobbes!"); + expectSplit("o.*?$", "Hello\r\nHobbes!", "Hello\r\nH"); + try { + expectSplit("\\b", "a+ b + c\nd", "", "a", "+ ", "b", " + ", "c", "\n", "d"); + } catch (RuntimeException e) { + // Java 8 changed the semantics of split, so if we're on 8, the + // above will fail and this will succeed: + expectSplit("\\b", "a+ b + c\nd", "a", "+ ", "b", " + ", "c", "\n", "d"); + } + expectSplit("\\B", "Hi Cal!", "H", "i C", "a", "l!"); + expectMatch("a{2,5}", "aaaa"); + expectGroups("a??(a{2,5}?)", "aaaa", "aaaa"); + expectGroups("a??(a{3}?)", "aaaa", "aaa"); + expectNoMatch("a(a{3}?)", "aaaaa"); + expectMatch("a(a{3,}?)", "aaaaa"); + } +} diff --git a/sgx-jvm/avian/test/Serialize.java b/sgx-jvm/avian/test/Serialize.java new file mode 100644 index 0000000000..95a2f279d2 --- /dev/null +++ b/sgx-jvm/avian/test/Serialize.java @@ -0,0 +1,231 @@ +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Properties; + +public class Serialize implements Serializable { + public static final long serialVersionUID = 1l; + public int dummy = 0x12345678; + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static void expectEqual(boolean a, boolean b) { + expect(a == b); + } + + private static void expectEqual(int a, int b) { + expect(a == b); + } + + private static void expectEqual(String a, String b) { + expect(a.equals(b)); + } + + private static String pad(long number, int length) { + return pad(Long.toHexString(number), length, '0'); + } + + private static String pad(String s, int length, char padChar) { + length -= s.length(); + if (length <= 0) { + return s; + } + StringBuilder builder = new StringBuilder(); + while (length-- > 0) { + builder.append(padChar); + } + return builder.append(s).toString(); + } + + protected static void hexdump(byte[] a) { + StringBuilder builder = new StringBuilder(); + System.err.print(pad(0, 8) + " "); + for (int i = 0; i < a.length; i++) { + String hex = Integer.toHexString(a[i] & 0xff); + System.err.print(" " + (hex.length() == 1 ? "0" : "") + hex); + builder.append(a[i] < 0x20 || a[i] > 0x7f ? '.' : (char)a[i]); + if ((i & 0xf) == 0x7) { + System.err.print(" "); + } else if ((i & 0xf) == 0xf) { + System.err.println(" |" + builder + "|"); + builder.setLength(0); + System.err.print(pad(i + 1, 8) + " "); + } + } + for (int i = a.length & 0xf; i < 0x10; i++) { + System.err.print(" "); + if ((i & 0xf) == 0x7) { + System.err.print(" "); + } + } + System.err.println(" |" + builder + "|"); + } + + private static void expectEqual(byte[] a, int[] b) { + expect(a.length == b.length); + + for (int i = 0; i < a.length; ++i) { + expect(a[i] == (byte)b[i]); + } + } + + private static class MyMap implements Serializable { + private transient Properties properties = new Properties(); + + public final static long serialVersionUID = 0x0cc1f63e2d256ae6l; + + public int size() { + return properties.size(); + } + + public void put(String key, String value) { + properties.put(key, value); + } + + public String get(String key) { + return properties.getProperty(key); + } + + private void writeObject(ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + out.writeInt(size()); + for (Object key : properties.keySet()) { + out.writeObject(key); + out.writeObject(properties.get(key)); + } + } + + private void readObject(ObjectInputStream in) throws IOException { + try { + in.defaultReadObject(); + } catch (Exception e) { + // OpenJDK's defaultReadObject() can throw a ClassNotFoundException + throw new IOException(e); + } + properties = new Properties(); + int size = in.readInt(); + for (int i = 0; i < size; i++) try { + properties.put(in.readObject(), in.readObject()); + } catch (ClassNotFoundException e) { + throw new IOException(e); + } + } + } + + public static void main(String[] args) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ObjectOutputStream out2 = new ObjectOutputStream(out); + out2.writeBoolean(true); + out2.flush(); + out2.writeByte(17); + out2.flush(); + out2.writeInt(0xcafebabe); + out2.flush(); + out2.writeUTF("Max & Möritz"); + out2.flush(); + out2.writeChar('É›'); + out2.flush(); + out2.writeObject(new Serialize()); + out2.close(); + byte[] array = out.toByteArray(); + expectEqual(array, new int[] { + // magic + 0xac, 0xed, + // version + 0x00, 0x05, + // blockdata, length + 0x77, 0x1, + // true + 1, + // blockdata, length + 0x77, 0x1, + // (byte)17 + 17, + // blockdata, length + 0x77, 0x4, + // 0xcafebabe + 0xca, 0xfe, 0xba, 0xbe, + // blockdata, length + 0x77, 0xf, + // "Max & Möritz" + 0x0, 0xd, 'M', 'a', 'x', ' ', '&', ' ', 'M', 0xc3, 0xb6, 'r', 'i', 't', 'z', + // blockdata, length + 0x77, 0x2, + // 'ö' + 0x02, 0x5b, + // object + 0x73, + // class desc, "Serialize" + 0x72, 0, 9, 'S', 'e', 'r', 'i', 'a', 'l', 'i', 'z', 'e', + // serialVersionUID + 0, 0, 0, 0, 0, 0, 0, 1, + // flags (SC_SERIALIZABLE) + 2, + // field count + 0x0, 0x1, + // int dummy + 'I', 0x0, 0x5, 'd', 'u', 'm', 'm', 'y', + // class annotation + 0x78, + // super class desc + 0x70, + // classdata[] + 0x12, 0x34, 0x56, 0x78 + }); + ByteArrayInputStream in = new ByteArrayInputStream(array); + ObjectInputStream in2 = new ObjectInputStream(in); + expectEqual(true, in2.readBoolean()); + expectEqual(17, in2.readByte()); + expectEqual(0xcafebabe, in2.readInt()); + expectEqual("Max & Möritz", in2.readUTF()); + expectEqual('É›', in2.readChar()); + Serialize unserialized = (Serialize) in2.readObject(); + expectEqual(0x12345678, unserialized.dummy); + in2.close(); + + out.reset(); + out2 = new ObjectOutputStream(out); + MyMap map = new MyMap(); + map.put("key", "value"); + out2.writeObject(map); + out2.close(); + array = out.toByteArray(); + expectEqual(array, new int[] { + // magic + 0xac, 0xed, + // version + 0x00, 0x05, + // object + 0x73, + // class desc "Serialize$MyMap" + 0x72, 0, 15, 'S', 'e', 'r', 'i', 'a', 'l', 'i', 'z', 'e', '$', + 'M', 'y', 'M', 'a', 'p', + // serial version UID: 0x0cc1f64e2d266ae6 + 0x0c, 0xc1, 0xf6, 0x3e, 0x2d, 0x25, 0x6a, 0xe6, + // flags: SC_SERIALIZABLE | SC_WRITE_METHOD + 0x03, + // no (non-transient) fields + 0, 0, + // class annotation + 0x78, + // super class desc + 0x70, + // custom TreeMap data written by TreeMap#writeObject + 0x77, 4, 0x00 , 0x00, 0x00, 0x01, // (int)1 (== map.size()) + 0x74, 0, 3, 'k', 'e', 'y', // "key" + 0x74, 0, 5, 'v', 'a', 'l', 'u', 'e', // "value" + // end block data + 0x78 + }); + in = new ByteArrayInputStream(array); + in2 = new ObjectInputStream(in); + map = (MyMap)in2.readObject(); + in2.close(); + expectEqual(1, map.size()); + expectEqual("value", (String)map.get("key")); + } +} diff --git a/sgx-jvm/avian/test/Simple.java b/sgx-jvm/avian/test/Simple.java new file mode 100644 index 0000000000..ce0c470e95 --- /dev/null +++ b/sgx-jvm/avian/test/Simple.java @@ -0,0 +1,11 @@ +public class Simple { + public static int size(long v, int radix) { + int size = 0; + for (long n = v; n != 0; n /= radix) ++size; + return size; + } + + public static void main(String[] args) { + size(42, 10); + } +} diff --git a/sgx-jvm/avian/test/Sockets.java b/sgx-jvm/avian/test/Sockets.java new file mode 100644 index 0000000000..9bce0e036e --- /dev/null +++ b/sgx-jvm/avian/test/Sockets.java @@ -0,0 +1,33 @@ +import java.net.SocketAddress; +import java.net.InetSocketAddress; +import java.nio.channels.SocketChannel; +import java.io.IOException; + +public class Sockets { + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + public static void testFailedBind() throws Exception { + final String Hostname = "localhost"; + final int Port = 22046; // hopefully this port is unused + final SocketAddress Address = new InetSocketAddress(Hostname, Port); + final byte[] Message = "hello, world!".getBytes(); + + SocketChannel out = SocketChannel.open(); + try { + try { + out.connect(Address); + expect(false); + } catch(IOException e) { + // We're good. This previously triggered a vm assert, rather than an exception + } + } finally { + out.close(); + } + } + + public static void main(String[] args) throws Exception { + testFailedBind(); + } +} diff --git a/sgx-jvm/avian/test/StackOverflow.java b/sgx-jvm/avian/test/StackOverflow.java new file mode 100644 index 0000000000..e25e3819eb --- /dev/null +++ b/sgx-jvm/avian/test/StackOverflow.java @@ -0,0 +1,41 @@ +public class StackOverflow { + private static int add(int[] numbers, int offset, int length) { + if (length == 0) { + return 0; + } else { + return numbers[offset] + add(numbers, offset + 1, length - 1); + } + } + private static int add(int ... numbers) { + return add(numbers, 0, numbers.length); + } + + private static int test1() { + add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + return test1() + 1; + } + + private static int test2() { + return test3() + 1; + } + + private static int test3() { + return test2() + 1; + } + + public static void main(String[] args) { + try { + test1(); + throw new RuntimeException(); + } catch (StackOverflowError e) { + e.printStackTrace(); + } + + try { + test2(); + throw new RuntimeException(); + } catch (StackOverflowError e) { + e.printStackTrace(); + } + } +} diff --git a/sgx-jvm/avian/test/StringBuilderTest.java b/sgx-jvm/avian/test/StringBuilderTest.java new file mode 100644 index 0000000000..3060b4f975 --- /dev/null +++ b/sgx-jvm/avian/test/StringBuilderTest.java @@ -0,0 +1,65 @@ + +public class StringBuilderTest { + private static final int iterations = 1000; + + public static void main(String[] args) { + verifyAppendStrLength(); + verifyAppendCharLength(); + verifySubstring(); + } + + private static void verify(String srcStr, int iterations, String result) { + int expectedLength = srcStr.length() * iterations; + if (result.length() != expectedLength) { + throw new IllegalStateException("Incorrect length: " + result.length() + " vs " + expectedLength); + } + } + + private static void verify(String expected, String actual) { + if (! expected.equals(actual)) { + throw new IllegalStateException("Strings don't match, expected: " + expected + ", actual: " + actual); + } + } + + private static void verifyAppendStrLength() { + String fooStr = "foobar"; + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < iterations; i++) { + sb.append(fooStr); + } + String result = sb.toString(); + + verify(fooStr, iterations, result); + } + + private static void verifyAppendCharLength() { + int iterations = 5000; + String fooStr = "foobar"; + char[] fooChars = new char[fooStr.length()]; + fooStr.getChars(0, fooStr.length(), fooChars, 0); + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < iterations; i++) { + for (int j = 0; j < fooChars.length; j++) { + sb.append(fooChars[j]); + } + } + String result = sb.toString(); + + verify(fooStr, iterations, result); + } + + private static void verifySubstring() { + String fooStr = "foobar"; + StringBuilder sb = new StringBuilder(); + sb.append(fooStr); + sb.append(fooStr); + + String beginingSubString = sb.substring(0, fooStr.length()); + verify(fooStr, beginingSubString); + + String endSubString = sb.substring(fooStr.length()); + verify(fooStr, endSubString); + } +} diff --git a/sgx-jvm/avian/test/Strings.java b/sgx-jvm/avian/test/Strings.java new file mode 100644 index 0000000000..df3ca334ff --- /dev/null +++ b/sgx-jvm/avian/test/Strings.java @@ -0,0 +1,231 @@ +public class Strings { + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static boolean equal(Object a, Object b) { + return a == b || (a != null && a.equals(b)); + } + + private static boolean arraysEqual(byte[] a, byte[] b) { + if (a.length != b.length) { + return false; + } + + for (int i = 0; i < a.length; ++i) { + if (a[i] != b[i]) { + return false; + } + } + + return true; + } + + private static byte[] append(byte[] a, byte[] b) { + byte[] c = new byte[a.length + b.length]; + for (int i = 0; i < a.length; ++i) { + c[i] = a[i]; + } + for (int i = 0; i < b.length; ++i) { + c[i + a.length] = b[i]; + } + return c; + } + + private static boolean arraysEqual(Object[] a, Object[] b) { + if (a.length != b.length) { + return false; + } + + for (int i = 0; i < a.length; ++i) { + if (! equal(a[i], b[i])) { + return false; + } + } + + return true; + } + + private static void testDecode(final boolean prematureEOS) throws Exception { + java.io.Reader r = new java.io.InputStreamReader + (new java.io.InputStream() { + int state = 0; + + public int read() { + throw new UnsupportedOperationException(); + } + + public int read(byte[] b, int offset, int length) { + if (length == 0) return 0; + + switch (state) { + case 0: + b[offset] = (byte) 0xc2; + state = 1; + return 1; + + case 1: + b[offset] = (byte) 0xae; + state = 2; + return 1; + + case 2: + b[offset] = (byte) 0xea; + state = 3; + return 1; + + case 3: + b[offset] = (byte) 0xba; + state = prematureEOS ? 5 : 4; + return 1; + + case 4: + b[offset] = (byte) 0xaf; + state = 5; + return 1; + + case 5: + return -1; + + default: + throw new RuntimeException(); + } + } + }, "UTF-8"); + + char[] buffer = new char[2]; + int offset = 0; + while (offset < buffer.length) { + int c = r.read(buffer, offset, buffer.length - offset); + if (c == -1) break; + offset += c; + } + + expect(new String(buffer, 0, offset).equals + (prematureEOS ? "\u00ae\ufffd" : "\u00ae\uaeaf")); + } + + public static void testTrivialPattern() throws Exception { + expect("?7".matches("\\0777")); + expect("\007".matches("\\a")); + expect("\\".matches("\\\\")); + expect("J".matches("\\x4A")); + expect("a".matches("\\x61")); + expect("\0078".matches("\\078")); + } + + public static void main(String[] args) throws Exception { + expect(new String(new byte[] { 99, 111, 109, 46, 101, 99, 111, 118, 97, + 116, 101, 46, 110, 97, 116, 46, 98, 117, + 115, 46, 83, 121, 109, 98, 111, 108 }) + .equals("com.ecovate.nat.bus.Symbol")); + + final String months = "Jan\u00aeFeb\u00aeMar\u00ae"; + expect(months.split("\u00ae").length == 3); + expect(months.replaceAll("\u00ae", ".").equals("Jan.Feb.Mar.")); + + // Java 8 changed the semantics of String.split relative to + // previous versions, therefore we accept multiple possible + // results: + expect(arraysEqual + ("xyz".split("", 0), new String[] { "", "x", "y", "z" }) + || arraysEqual + ("xyz".split("", 0), new String[] { "x", "y", "z" })); + expect(arraysEqual + ("xyz".split("", 1), new String[] { "xyz" })); + expect(arraysEqual + ("xyz".split("", 2), new String[] { "", "xyz" }) + || arraysEqual + ("xyz".split("", 2), new String[] { "x", "yz" })); + expect(arraysEqual + ("xyz".split("", 3), new String[] { "", "x", "yz" }) + || arraysEqual + ("xyz".split("", 3), new String[] { "x", "y", "z" })); + expect(arraysEqual + ("xyz".split("", 4), new String[] { "", "x", "y", "z" }) + || arraysEqual + ("xyz".split("", 4), new String[] { "x", "y", "z", "" })); + expect(arraysEqual + ("xyz".split("", 5), new String[] { "", "x", "y", "z", "" }) + || arraysEqual + ("xyz".split("", 5), new String[] { "x", "y", "z", "" })); + expect(arraysEqual + ("xyz".split("", 6), new String[] { "", "x", "y", "z", "" }) + || arraysEqual + ("xyz".split("", 6), new String[] { "x", "y", "z", "" })); + expect(arraysEqual + ("xyz".split("", -1), new String[] { "", "x", "y", "z", "" }) + || arraysEqual + ("xyz".split("", -1), new String[] { "x", "y", "z", "" })); + + expect(arraysEqual("".split("xyz", 0), new String[] { "" })); + expect(arraysEqual("".split("xyz", 1), new String[] { "" })); + expect(arraysEqual("".split("xyz", -1), new String[] { "" })); + + expect(arraysEqual("".split("", 0), new String[] { "" })); + expect(arraysEqual("".split("", 1), new String[] { "" })); + expect(arraysEqual("".split("", -1), new String[] { "" })); + + expect("foo_foofoo__foo".replaceAll("_", "__") + .equals("foo__foofoo____foo")); + + expect("foo_foofoo__foo".replaceFirst("_", "__") + .equals("foo__foofoo__foo")); + + expect("stereomime".matches("stereomime")); + expect(! "stereomime".matches("stereomim")); + expect(! "stereomime".matches("tereomime")); + expect(! "stereomime".matches("sterEomime")); + + StringBuilder sb = new StringBuilder(); + sb.append('$'); + sb.append('2'); + expect(sb.substring(1).equals("2")); + + expect(Character.forDigit(Character.digit('0', 10), 10) == '0'); + expect(Character.forDigit(Character.digit('9', 10), 10) == '9'); + expect(Character.forDigit(Character.digit('b', 16), 16) == 'b'); + expect(Character.forDigit(Character.digit('f', 16), 16) == 'f'); + expect(Character.forDigit(Character.digit('z', 36), 36) == 'z'); + + testDecode(false); + testDecode(true); + + expect + (java.text.MessageFormat.format + ("{0} enjoy {1} {2}. do {4}? {4} do?", + "I", "grape", "nuts", "foobar", + new Object() { public String toString() { return "you"; } }) + .equals("I enjoy grape nuts. do you? you do?")); + + { java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream(); + java.io.PrintStream pout = new java.io.PrintStream(bout, true, "UTF-8"); + String s = "I ♥ grape nuts"; + System.out.println(s); + pout.println(s); + + expect + (arraysEqual + (bout.toByteArray(), + (s + System.getProperty("line.separator")).getBytes("UTF-8"))); + + expect + (arraysEqual + (bout.toByteArray(), append + (new byte[] { 73, 32, -30, -103, -91, 32, 103, 114, 97, 112, 101, + 32, 110, 117, 116, 115 }, + System.getProperty("line.separator").getBytes("UTF-8")))); + } + + expect("abc".lastIndexOf('b', 100) == 1); + + testTrivialPattern(); + + { String s = "hello, world!"; + java.nio.CharBuffer buffer = java.nio.CharBuffer.allocate(s.length()); + new java.io.InputStreamReader + (new java.io.ByteArrayInputStream(s.getBytes())).read(buffer); + expect(s.equals(new String(buffer.array()))); + } + } +} diff --git a/sgx-jvm/avian/test/Subroutine.java b/sgx-jvm/avian/test/Subroutine.java new file mode 100644 index 0000000000..e03686a9f5 --- /dev/null +++ b/sgx-jvm/avian/test/Subroutine.java @@ -0,0 +1,347 @@ +import avian.Stream; +import avian.ConstantPool; +import avian.ConstantPool.PoolEntry; +import avian.Assembler; +import avian.Assembler.FieldData; +import avian.Assembler.MethodData; + +import java.util.ArrayList; +import java.util.List; +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.IOException; + +public class Subroutine { + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static void stackMap(Object x) { + while (true) { + try { + try { + System.gc(); + } catch (DummyException e) { + // ignore + } finally { + x.toString(); + } + break; + } catch (DummyException e) { + // ignore + } + } + } + + private static byte[] makeTestCode(List pool) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Stream.write2(out, 1); // max stack + Stream.write2(out, 1); // max locals + Stream.write4(out, 0); // length (we'll set the real value later) + + // 0: + Stream.write1(out, Assembler.ldc_w); + Stream.write2(out, ConstantPool.addString(pool, "foo") + 1); + + // 3: + Stream.write1(out, Assembler.astore_0); + + // 4: + Stream.write1(out, Assembler.invokestatic); + Stream.write2(out, ConstantPool.addMethodRef + (pool, "java/lang/System", "gc", "()V") + 1); + + // 7: + Stream.write1(out, Assembler.goto_); + Stream.write2(out, 9); // 16 + + // 10: + Stream.write1(out, Assembler.astore_0); + + // 11: + Stream.write1(out, Assembler.invokestatic); + Stream.write2(out, ConstantPool.addMethodRef + (pool, "java/lang/System", "gc", "()V") + 1); + + // 14: + Stream.write1(out, Assembler.ret); + Stream.write1(out, 0); + + // 16: + Stream.write1(out, Assembler.jsr); + Stream.write2(out, -6); // 10 + + // 19: + Stream.write1(out, Assembler.invokestatic); + Stream.write2(out, ConstantPool.addMethodRef + (pool, "java/lang/System", "gc", "()V") + 1); + + // 22: + Stream.write1(out, Assembler.return_); + + Stream.write2(out, 0); // exception handler table length + Stream.write2(out, 0); // attribute count + + byte[] result = out.toByteArray(); + Stream.set4(result, 4, result.length - 12); + + return result; + } + + private static Class makeTestClass() throws IOException { + List pool = new ArrayList(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + String name = "$SubroutineTest$"; + + Assembler.writeClass + (out, pool, ConstantPool.addClass(pool, name), + ConstantPool.addClass(pool, "java/lang/Object"), + new int[0], new FieldData[0], new MethodData[] + { new MethodData(Assembler.ACC_STATIC | Assembler.ACC_PUBLIC, + ConstantPool.addUtf8(pool, "test"), + ConstantPool.addUtf8(pool, "()V"), + makeTestCode(pool)) }); + + return new MyClassLoader(Subroutine.class.getClassLoader()) + .defineClass(name, out.toByteArray()); + } + + // These tests are intended to cover the jsr and ret instructions. + // However, recent Sun javac versions avoid generating these + // instructions by default, so we must compile this class using + // -source 1.2 -target 1.1 -XDjsrlimit=0. + // + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4381996 + // + + private static void test(boolean throw_, boolean predicate) { + int x = 42; + int y = 99; + int a = 0; + try { + try { + int z = x + y; + if (throw_) throw new DummyException(); + if (predicate) { + return; + } + Integer.valueOf(z).toString(); + } finally { + a = x + y; + System.gc(); + } + expect(a == x + y); + } catch (DummyException e) { + e.printStackTrace(); + } + } + + private static Object test2(int path) { + try { + try { + switch (path) { + case 1: + return new Object(); + + case 2: { + int a = 42; + return Integer.valueOf(a); + } + + case 3: + throw new DummyException(); + } + } finally { + System.gc(); + } + return null; + } catch (DummyException e) { + e.printStackTrace(); + return null; + } + } + + private static Object test3(int path1, int path2, int path3) { + try { + try { + switch (path1) { + case 1: + return new Object(); + + case 2: { + int a = 42; + return Integer.valueOf(a); + } + + case 3: + throw new DummyException(); + } + } finally { + try { + switch (path2) { + case 1: + return new Object(); + + case 2: { + int a = 42; + return Integer.valueOf(a); + } + + case 3: + throw new DummyException(); + } + } finally { + try { + switch (path3) { + case 1: + return new Object(); + + case 2: { + int a = 42; + return Integer.valueOf(a); + } + + case 3: + throw new DummyException(); + } + } finally { + System.gc(); + } + } + } + return null; + } catch (DummyException e) { + e.printStackTrace(); + return null; + } + } + + private static long test4(int path) { + try { + try { + switch (path) { + case 1: + return 0xFABFABFABFL; + + case 2: { + int a = 42; + return 52L; + } + + case 3: + throw new DummyException(); + } + } finally { + System.gc(); + } + return 0L; + } catch (DummyException e) { + e.printStackTrace(); + return 0L; + } + } + + private boolean test5(boolean predicate) { + try { + if (predicate) { + return false; + } + } finally { + synchronized (this) { + notifyAll(); + } + } + return true; + } + + private static int test6(boolean predicate) { + try { + if (predicate) { + return -2; + } + } finally { + return new Throwable().getStackTrace()[0].getLineNumber(); + } + } + + public static void main(String[] args) throws Exception { + test(false, false); + test(false, true); + test(true, false); + + String.valueOf(test2(1)); + String.valueOf(test2(2)); + String.valueOf(test2(3)); + + String.valueOf(test3(1, 1, 1)); + String.valueOf(test3(2, 1, 1)); + String.valueOf(test3(3, 1, 1)); + + String.valueOf(test3(1, 2, 1)); + String.valueOf(test3(2, 2, 1)); + String.valueOf(test3(3, 2, 1)); + + String.valueOf(test3(1, 3, 1)); + String.valueOf(test3(2, 3, 1)); + String.valueOf(test3(3, 3, 1)); + + String.valueOf(test3(1, 1, 2)); + String.valueOf(test3(2, 1, 2)); + String.valueOf(test3(3, 1, 2)); + + String.valueOf(test3(1, 2, 2)); + String.valueOf(test3(2, 2, 2)); + String.valueOf(test3(3, 2, 2)); + + String.valueOf(test3(1, 3, 2)); + String.valueOf(test3(2, 3, 2)); + String.valueOf(test3(3, 3, 2)); + + String.valueOf(test3(1, 1, 3)); + String.valueOf(test3(2, 1, 3)); + String.valueOf(test3(3, 1, 3)); + + String.valueOf(test3(1, 2, 3)); + String.valueOf(test3(2, 2, 3)); + String.valueOf(test3(3, 2, 3)); + + String.valueOf(test3(1, 3, 3)); + String.valueOf(test3(2, 3, 3)); + String.valueOf(test3(3, 3, 3)); + + String.valueOf(test4(1)); + String.valueOf(test4(2)); + String.valueOf(test4(3)); + + expect(test4(1) == 0xFABFABFABFL); + + new Subroutine().test5(true); + new Subroutine().test5(false); + + makeTestClass().getMethod("test", new Class[0]).invoke + (null, new Object[0]); + + stackMap(new Object()); + + { + int f = test6(false); + int t = test6(true); + System.out.println("line: " + f); + expect(f > 0); + expect(f == t); + } + + } + + private static class DummyException extends RuntimeException { } + + private static class MyClassLoader extends ClassLoader { + public MyClassLoader(ClassLoader parent) { + super(parent); + } + + public Class defineClass(String name, byte[] bytes) { + return super.defineClass(name, bytes, 0, bytes.length); + } + } +} diff --git a/sgx-jvm/avian/test/Switch.java b/sgx-jvm/avian/test/Switch.java new file mode 100644 index 0000000000..bb1c0a459f --- /dev/null +++ b/sgx-jvm/avian/test/Switch.java @@ -0,0 +1,66 @@ +public class Switch { + private static int table(int k) { + switch (k) { + case 0: + return 0; + case 1: + return 1; + case 2: + return 2; + case 9: + return 9; + case 10: + return 10; + case 11: + return 11; + case 12: + return 8; + case -5: + return 5; + default: + return 7; + } + } + + private static int lookup(int k) { + switch (k) { + case 0: + return 0; + case 45: + return 45; + case 46: + return 46; + case 47: + return -47; + case 200: + return 200; + case 244: + return 244; + case 245: + return 245; + default: + return 91; + } + } + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + public static void main(String[] args) { + expect(table(0) == 0); + expect(table(9) == 9); + expect(table(10) == 10); + expect(table(11) == 11); + expect(table(12) == 8); + expect(table(-5) == 5); + expect(table(-13) == 7); + + expect(lookup(0) == 0); + expect(lookup(45) == 45); + expect(lookup(46) == 46); + expect(lookup(47) == -47); + expect(lookup(245) == 245); + expect(lookup(246) == 91); + } +} diff --git a/sgx-jvm/avian/test/Threads.java b/sgx-jvm/avian/test/Threads.java new file mode 100644 index 0000000000..5f4860bd7b --- /dev/null +++ b/sgx-jvm/avian/test/Threads.java @@ -0,0 +1,94 @@ +public class Threads implements Runnable { + private static boolean success = false; + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + public static void main(String[] args) throws Exception { + ((Thread.UncaughtExceptionHandler) Thread.currentThread().getThreadGroup()) + .uncaughtException(Thread.currentThread(), new Exception()); + + { Threads test = new Threads(); + Thread thread = new Thread(test); + + synchronized (test) { + thread.start(); + test.wait(); + } + } + + { Thread thread = new Thread() { + public void run() { + while (true) { + System.out.print("."); + try { + sleep(1000); + } catch (Exception e) { + System.out.println("thread interrupted? " + interrupted()); + break; + } + } + } + }; + thread.start(); + + System.out.println("\nAbout to interrupt..."); + thread.interrupt(); + System.out.println("\nInterrupted!"); + } + + { Thread thread = new Thread() { + @Override + public void run() { + // do nothing + } + }; + + thread.start(); + thread.join(); + } + + System.out.println("finished; success? " + success); + + if (! success) { + System.exit(-1); + } + } + + public void run() { + int i = 0; + try { + expect(! Thread.holdsLock(this)); + synchronized (this) { + expect(Thread.holdsLock(this)); + + System.out.println("I'm running in a separate thread!"); + + Thread.yield(); // just to prove Thread.yield exists and is callable + + final int arrayCount = 16; + final int arraySize = 4; + System.out.println("Allocating and discarding " + arrayCount + + " arrays of " + arraySize + "MB each"); + for (; i < arrayCount; ++i) { + byte[] array = new byte[arraySize * 1024 * 1024]; + } + + long nap = 500; + System.out.println("sleeping for " + nap + " milliseconds"); + Thread.sleep(nap); + notifyAll(); + } + success = true; + } catch (Throwable e) { + System.err.println("caught something in second thread after " + i + + " iterations"); + e.printStackTrace(); + } finally { + synchronized (this) { + notifyAll(); + } + } + } +} diff --git a/sgx-jvm/avian/test/TimeUnitConversions.java b/sgx-jvm/avian/test/TimeUnitConversions.java new file mode 100644 index 0000000000..b681f60076 --- /dev/null +++ b/sgx-jvm/avian/test/TimeUnitConversions.java @@ -0,0 +1,117 @@ +import java.util.concurrent.TimeUnit; + +public class TimeUnitConversions { + private static void expect(long v1, long v2) { + if (v1 != v2) { + throw new RuntimeException(v1 + " != " + v2); + } + } + + private static void toNanoConversionTest() { + long expectedValue = 1; + expect(TimeUnit.NANOSECONDS.convert(1, TimeUnit.NANOSECONDS), expectedValue); + expectedValue *= 1000; + expect(TimeUnit.NANOSECONDS.convert(1, TimeUnit.MICROSECONDS), expectedValue); + expectedValue *= 1000; + expect(TimeUnit.NANOSECONDS.convert(1, TimeUnit.MILLISECONDS), expectedValue); + expectedValue *= 1000; + expect(TimeUnit.NANOSECONDS.convert(1, TimeUnit.SECONDS), expectedValue); + expectedValue *= 60; + expect(TimeUnit.NANOSECONDS.convert(1, TimeUnit.MINUTES), expectedValue); + expectedValue *= 60; + expect(TimeUnit.NANOSECONDS.convert(1, TimeUnit.HOURS), expectedValue); + expectedValue *= 24; + expect(TimeUnit.NANOSECONDS.convert(1, TimeUnit.DAYS), expectedValue); + } + + private static void toMicroConversionTest() { + long expectedValue = 1; + expect(TimeUnit.MICROSECONDS.convert(1000, TimeUnit.NANOSECONDS), expectedValue); + expect(TimeUnit.MICROSECONDS.convert(1, TimeUnit.MICROSECONDS), expectedValue); + expectedValue *= 1000; + expect(TimeUnit.MICROSECONDS.convert(1, TimeUnit.MILLISECONDS), expectedValue); + expectedValue *= 1000; + expect(TimeUnit.MICROSECONDS.convert(1, TimeUnit.SECONDS), expectedValue); + expectedValue *= 60; + expect(TimeUnit.MICROSECONDS.convert(1, TimeUnit.MINUTES), expectedValue); + expectedValue *= 60; + expect(TimeUnit.MICROSECONDS.convert(1, TimeUnit.HOURS), expectedValue); + expectedValue *= 24; + expect(TimeUnit.MICROSECONDS.convert(1, TimeUnit.DAYS), expectedValue); + } + + private static void toMilliConversionTest() { + long expectedValue = 1; + expect(TimeUnit.MILLISECONDS.convert(1000L * 1000, TimeUnit.NANOSECONDS), expectedValue); + expect(TimeUnit.MILLISECONDS.convert(1000, TimeUnit.MICROSECONDS), expectedValue); + expect(TimeUnit.MILLISECONDS.convert(1, TimeUnit.MILLISECONDS), expectedValue); + expectedValue *= 1000; + expect(TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS), expectedValue); + expectedValue *= 60; + expect(TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES), expectedValue); + expectedValue *= 60; + expect(TimeUnit.MILLISECONDS.convert(1, TimeUnit.HOURS), expectedValue); + expectedValue *= 24; + expect(TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS), expectedValue); + } + + private static void toSecondConversionTest() { + long expectedValue = 1; + expect(TimeUnit.SECONDS.convert(1000L * 1000 * 1000, TimeUnit.NANOSECONDS), expectedValue); + expect(TimeUnit.SECONDS.convert(1000L * 1000, TimeUnit.MICROSECONDS), expectedValue); + expect(TimeUnit.SECONDS.convert(1000, TimeUnit.MILLISECONDS), expectedValue); + expect(TimeUnit.SECONDS.convert(1, TimeUnit.SECONDS), expectedValue); + expectedValue *= 60; + expect(TimeUnit.SECONDS.convert(1, TimeUnit.MINUTES), expectedValue); + expectedValue *= 60; + expect(TimeUnit.SECONDS.convert(1, TimeUnit.HOURS), expectedValue); + expectedValue *= 24; + expect(TimeUnit.SECONDS.convert(1, TimeUnit.DAYS), expectedValue); + } + + private static void toMinuteConversionTest() { + long expectedValue = 1; + expect(TimeUnit.MINUTES.convert(1000L * 1000 * 1000 * 60, TimeUnit.NANOSECONDS), expectedValue); + expect(TimeUnit.MINUTES.convert(1000L * 1000 * 60, TimeUnit.MICROSECONDS), expectedValue); + expect(TimeUnit.MINUTES.convert(1000L * 60, TimeUnit.MILLISECONDS), expectedValue); + expect(TimeUnit.MINUTES.convert(60, TimeUnit.SECONDS), expectedValue); + expect(TimeUnit.MINUTES.convert(1, TimeUnit.MINUTES), expectedValue); + expectedValue *= 60; + expect(TimeUnit.MINUTES.convert(1, TimeUnit.HOURS), expectedValue); + expectedValue *= 24; + expect(TimeUnit.MINUTES.convert(1, TimeUnit.DAYS), expectedValue); + } + + private static void toHourConversionTest() { + long expectedValue = 1; + expect(TimeUnit.HOURS.convert(1000L * 1000 * 1000 * 60 * 60, TimeUnit.NANOSECONDS), expectedValue); + expect(TimeUnit.HOURS.convert(1000L * 1000 * 60 * 60, TimeUnit.MICROSECONDS), expectedValue); + expect(TimeUnit.HOURS.convert(1000L * 60 * 60, TimeUnit.MILLISECONDS), expectedValue); + expect(TimeUnit.HOURS.convert(60L * 60, TimeUnit.SECONDS), expectedValue); + expect(TimeUnit.HOURS.convert(60, TimeUnit.MINUTES), expectedValue); + expect(TimeUnit.HOURS.convert(1, TimeUnit.HOURS), expectedValue); + expectedValue *= 24; + expect(TimeUnit.HOURS.convert(1, TimeUnit.DAYS), expectedValue); + } + + private static void toDayConversionTest() { + long expectedValue = 1; + expect(TimeUnit.DAYS.convert(1000L * 1000 * 1000 * 60 * 60 * 24, TimeUnit.NANOSECONDS), expectedValue); + expect(TimeUnit.DAYS.convert(1000L * 1000 * 60 * 60 * 24, TimeUnit.MICROSECONDS), expectedValue); + expect(TimeUnit.DAYS.convert(1000L * 60 * 60 * 24, TimeUnit.MILLISECONDS), expectedValue); + expect(TimeUnit.DAYS.convert(60L * 60 * 24, TimeUnit.SECONDS), expectedValue); + expect(TimeUnit.DAYS.convert(60L * 24, TimeUnit.MINUTES), expectedValue); + expect(TimeUnit.DAYS.convert(24, TimeUnit.HOURS), expectedValue); + expect(TimeUnit.DAYS.convert(1, TimeUnit.DAYS), expectedValue); + } + + public static void main(String[] args) { + toNanoConversionTest(); + toMicroConversionTest(); + toMilliConversionTest(); + toSecondConversionTest(); + toMinuteConversionTest(); + toHourConversionTest(); + toDayConversionTest(); + } +} diff --git a/sgx-jvm/avian/test/Trace.java b/sgx-jvm/avian/test/Trace.java new file mode 100644 index 0000000000..2b453f4cf9 --- /dev/null +++ b/sgx-jvm/avian/test/Trace.java @@ -0,0 +1,113 @@ +public class Trace implements Runnable { + private volatile boolean alive = true; + + private static void throwSomething() { + throw new RuntimeException(); + } + + private void bar(Object o) { + o.toString(); + } + + private void foo() { + { long a = 42; + long b = 25; + long c = a / b; + } + + try { + long a = 42; + long b = 0; + long c = a / b; + } catch (Exception e) { } + + try { + throw new Exception(); + } catch (Exception e) { } + + try { + throwSomething(); + } catch (Exception e) { } + + try { + Trace.class.getMethod("bar", Object.class).invoke(this, this); + } catch (Exception e) { } + } + + private static void dummy() { + byte[] a = new byte[10]; + byte[] b = new byte[10]; + System.arraycopy(a, 0, b, 0, 10); + } + + private static void tail1(int a, int b, int c, int d, int e, int f) { + dummy(); + } + + private static void tail2() { + tail1(1, 2, 3, 4, 5, 6); + tail1(1, 2, 3, 4, 5, 6); + } + + private static void test(Trace trace) { + tail1(1, 2, 3, 4, 5, 6); + tail2(); + trace.foo(); + } + + public void run() { + synchronized (this) { + notifyAll(); + } + + try { + for (int i = 0; i < 10000; ++i) { + test(this); + + if (i % 100 == 0) { + System.out.print("r"); + System.out.flush(); + synchronized (this) { + notifyAll(); + } + } + } + } finally { + synchronized (this) { + alive = false; + notifyAll(); + } + } + } + + public static void main(String[] args) throws Exception { + if ("true".equals(System.getenv("TRAVIS"))) { + // This test fails randomly on Travis-CI, though we've never + // been able to reproduce the failure elsewhere. So we disable + // it if we know we're running on Travis. + return; + } + + Trace trace = new Trace(); + Thread thread = new Thread(trace); + + synchronized (trace) { + thread.start(); + trace.wait(); + + int count = 0; + while (trace.alive) { + thread.getStackTrace(); + ++ count; + + if (count % 100 == 0) { + trace.wait(); + System.out.print("t"); + System.out.flush(); + } + } + + System.out.println("\ngot " + count + " traces"); + } + } +} diff --git a/sgx-jvm/avian/test/Tree.java b/sgx-jvm/avian/test/Tree.java new file mode 100644 index 0000000000..ec7c5cf41f --- /dev/null +++ b/sgx-jvm/avian/test/Tree.java @@ -0,0 +1,133 @@ +import java.util.Comparator; +import java.util.TreeSet; +import java.util.TreeMap; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; +import java.util.Iterator; + +public class Tree { + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static String printList(TreeSet list) { + StringBuilder sb = new StringBuilder(); + + for (Object o : list) { + sb.append(o); + sb.append(", "); + } + sb.setLength(sb.length()-2); + return sb.toString(); + } + + private static String printMap(TreeMap map) { + StringBuilder sb = new StringBuilder(); + + for (Iterator it = map.entrySet().iterator(); it.hasNext();) { + Map.Entry e = it.next(); + sb.append(e.getKey()); + sb.append("="); + sb.append(e.getValue()); + if (it.hasNext()) { + sb.append(", "); + } + } + return sb.toString(); + } + + private static void isEqual(String s1, String s2) { + System.out.println(s1); + expect(s1.equals(s2)); + } + + private static class MyCompare implements Comparator { + public int compare(Integer o1, Integer o2) { + return o1.compareTo(o2); + } + } + + private static void ascendingIterator() { + TreeSet t = new TreeSet(); + t.add(7); + t.add(2); + t.add(9); + t.add(2); + Iterator iter = t.iterator(); + expect(2 == (int)iter.next()); + expect(7 == (int)iter.next()); + iter.remove(); + expect(9 == (int)iter.next()); + expect(!iter.hasNext()); + isEqual(printList(t), "2, 9"); + } + + private static void descendingIterator() { + TreeSet t = new TreeSet(); + t.add(7); + t.add(2); + t.add(9); + t.add(2); + Iterator iter = t.descendingIterator(); + expect(9 == (int)iter.next()); + expect(7 == (int)iter.next()); + iter.remove(); + expect(2 == (int)iter.next()); + expect(!iter.hasNext()); + isEqual(printList(t), "2, 9"); + } + + public static void main(String args[]) { + ascendingIterator(); + descendingIterator(); + TreeSet t1 = new TreeSet(new MyCompare()); + t1.add(5); t1.add(2); t1.add(1); t1.add(8); t1.add(3); + isEqual(printList(t1), "1, 2, 3, 5, 8"); + t1.add(4); + isEqual(printList(t1), "1, 2, 3, 4, 5, 8"); + t1.remove(3); + isEqual(printList(t1), "1, 2, 4, 5, 8"); + TreeSet t2 = new TreeSet(new Comparator() { + public int compare(String s1, String s2) { + return s1.compareTo(s2); + } + }); + t2.add("one"); t2.add("two"); t2.add("three"); t2.add("four"); t2.add("five"); + isEqual(printList(t2), "five, four, one, three, two"); + for (int i=0; i < 1000; i++) { + t2.add(Integer.toString(i)); + } + expect(t2.size() == 1005); + for (int i=0; i < 999; i++) { + t2.remove(Integer.toString(i)); + } + expect(t2.size() == 6); + t2.add("kappa"); + isEqual(printList(t2), "999, five, four, kappa, one, three, two"); + + TreeMap map = new TreeMap + (new Comparator() { + public int compare(String s1, String s2) { + return s1.compareTo(s2); + } + }); + + map.put("q", "Q"); + map.put("a", "A"); + map.put("b", "B"); + map.put("z", "Z"); + map.put("c", "C"); + map.put("y", "Y"); + + isEqual(printMap(map), "a=A, b=B, c=C, q=Q, y=Y, z=Z"); + + Collection list = new ArrayList(); + list.add(7); + list.add(2); + list.add(9); + list.add(2); + + isEqual(printList(new TreeSet(list)), "2, 7, 9"); + } +} diff --git a/sgx-jvm/avian/test/UnsafeTest.java b/sgx-jvm/avian/test/UnsafeTest.java new file mode 100644 index 0000000000..732c281a4a --- /dev/null +++ b/sgx-jvm/avian/test/UnsafeTest.java @@ -0,0 +1,171 @@ +import sun.misc.Unsafe; + +public class UnsafeTest { + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static void unsafeThrow(Unsafe u) { + u.throwException(new Exception()); + } + + private static void unsafeCatch(Unsafe u) { + boolean success = false; + try { + unsafeThrow(u); + } catch(Exception e) { + expect(e.getClass() == Exception.class); + success = true; + } + expect(success); + } + + private static void unsafeMemory(Unsafe u) { + final long size = 64; + long memory = u.allocateMemory(size); + try { + for (int i = 0; i < size; ++i) + u.putByte(memory + i, (byte) 42); + + for (int i = 0; i < size; ++i) + expect(u.getByte(memory + i) == 42); + + for (int i = 0; i < size / 2; ++i) + u.putShort(memory + (i * 2), (short) -12345); + + for (int i = 0; i < size / 2; ++i) + expect(u.getShort(memory + (i * 2)) == -12345); + + for (int i = 0; i < size / 2; ++i) + u.putChar(memory + (i * 2), (char) 23456); + + for (int i = 0; i < size / 2; ++i) + expect(u.getChar(memory + (i * 2)) == 23456); + + for (int i = 0; i < size / 4; ++i) + u.putInt(memory + (i * 4), 0x12345678); + + for (int i = 0; i < size / 4; ++i) + expect(u.getInt(memory + (i * 4)) == 0x12345678); + + for (int i = 0; i < size / 4; ++i) + u.putFloat(memory + (i * 4), 1.2345678F); + + for (int i = 0; i < size / 4; ++i) + expect(u.getFloat(memory + (i * 4)) == 1.2345678F); + + for (int i = 0; i < size / 8; ++i) + u.putLong(memory + (i * 8), 0x1234567890ABCDEFL); + + for (int i = 0; i < size / 8; ++i) + expect(u.getLong(memory + (i * 8)) == 0x1234567890ABCDEFL); + + for (int i = 0; i < size / 8; ++i) + u.putDouble(memory + (i * 8), 1.23456789012345D); + + for (int i = 0; i < size / 8; ++i) + expect(u.getDouble(memory + (i * 8)) == 1.23456789012345D); + + for (int i = 0; i < size / 8; ++i) + u.putAddress(memory + (i * 8), 0x12345678); + + for (int i = 0; i < size / 8; ++i) + expect(u.getAddress(memory + (i * 8)) == 0x12345678); + } finally { + u.freeMemory(memory); + } + } + + private static void unsafeArray(Unsafe u) { + final int offset = u.arrayBaseOffset(long[].class); + final int scale = u.arrayIndexScale(long[].class); + final int size = 64; + final long[] array = new long[size]; + + for (int i = 0; i < size; ++i) + u.putBooleanVolatile(array, offset + (i * scale), i % 2 == 0); + + for (int i = 0; i < size; ++i) + expect(u.getBooleanVolatile(array, offset + (i * scale)) + == (i % 2 == 0)); + + for (int i = 0; i < size; ++i) + u.putByteVolatile(array, offset + (i * scale), (byte) 42); + + for (int i = 0; i < size; ++i) + expect(u.getByteVolatile(array, offset + (i * scale)) == 42); + + for (int i = 0; i < size; ++i) + u.putShortVolatile(array, offset + (i * scale), (short) -12345); + + for (int i = 0; i < size; ++i) + expect(u.getShortVolatile(array, offset + (i * scale)) == -12345); + + for (int i = 0; i < size; ++i) + u.putCharVolatile(array, offset + (i * scale), (char) 23456); + + for (int i = 0; i < size; ++i) + expect(u.getCharVolatile(array, offset + (i * scale)) == 23456); + + for (int i = 0; i < size; ++i) + u.putIntVolatile(array, offset + (i * scale), 0x12345678); + + for (int i = 0; i < size; ++i) + expect(u.getIntVolatile(array, offset + (i * scale)) == 0x12345678); + + for (int i = 0; i < size; ++i) + u.putFloatVolatile(array, offset + (i * scale), 1.2345678F); + + for (int i = 0; i < size; ++i) + expect(u.getFloatVolatile(array, offset + (i * scale)) == 1.2345678F); + + for (int i = 0; i < size; ++i) + u.putLongVolatile(array, offset + (i * scale), 0x1234567890ABCDEFL); + + for (int i = 0; i < size; ++i) + expect(u.getLongVolatile(array, offset + (i * scale)) + == 0x1234567890ABCDEFL); + + for (int i = 0; i < size; ++i) + u.putDoubleVolatile(array, offset + (i * scale), 1.23456789012345D); + + for (int i = 0; i < size; ++i) + expect(u.getDoubleVolatile(array, offset + (i * scale)) + == 1.23456789012345D); + } + + private static class Data { + public long longField; + public double doubleField; + } + + private static void unsafeObject(Unsafe u) throws Exception { + final long longOffset = u.objectFieldOffset + (Data.class.getField("longField")); + + final long doubleOffset = u.objectFieldOffset + (Data.class.getField("doubleField")); + + Data data = new Data(); + + u.putLong(data, longOffset, 0x1234567890ABCDEFL); + + u.putDouble(data, doubleOffset, 1.23456789012345D); + + expect(u.getLong(data, longOffset) == 0x1234567890ABCDEFL); + + expect(u.getDouble(data, doubleOffset) == 1.23456789012345D); + } + + public static void main(String[] args) throws Exception { + System.out.println("method count is " + + Unsafe.class.getDeclaredMethods().length); + + Unsafe u = avian.Machine.getUnsafe(); + + unsafeCatch(u); + unsafeMemory(u); + unsafeArray(u); + unsafeObject(u); + } +} diff --git a/sgx-jvm/avian/test/UrlTest.java b/sgx-jvm/avian/test/UrlTest.java new file mode 100644 index 0000000000..244d1ec45a --- /dev/null +++ b/sgx-jvm/avian/test/UrlTest.java @@ -0,0 +1,41 @@ +import java.net.MalformedURLException; +import java.net.URL; + +public class UrlTest { + private static String query="var1=val1&var2=val2"; + private static String path="/testpath"; + private static String domain="file://www.readytalk.com"; + private static String file=path + "?" + query; + private static URL url; + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static void setupURL() throws MalformedURLException { + StringBuilder builder = new StringBuilder(); + builder.append(domain); + builder.append(file); + url = new URL(builder.toString()); + } + + private static void testGetPath() { + expect(url.getPath().equals(path)); + } + + private static void testGetFile() { + expect(url.getFile().equals(file)); + } + + private static void testGetQuery() { + expect(url.getQuery().equals(query)); + } + + public static void main(String[] args) throws MalformedURLException { + setupURL(); + testGetPath(); + testGetFile(); + testGetQuery(); + } + +} diff --git a/sgx-jvm/avian/test/Zip.java b/sgx-jvm/avian/test/Zip.java new file mode 100644 index 0000000000..36d08b6ca2 --- /dev/null +++ b/sgx-jvm/avian/test/Zip.java @@ -0,0 +1,53 @@ +import java.io.InputStream; +import java.io.File; +import java.util.Enumeration; +import java.util.zip.ZipFile; +import java.util.zip.ZipEntry; + +public class Zip { + + private static String findJar(File directory) { + for (File file: directory.listFiles()) { + if (file.isFile()) { + if (file.getName().endsWith(".jar")) { + System.out.println + ("found " + file.getAbsolutePath() + " length " + file.length()); + + return file.getAbsolutePath(); + } + } else if (file.isDirectory()) { + String result = findJar(file); + if (result != null) { + return result; + } + } + } + return null; + } + + public static void main(String[] args) throws Exception { + ZipFile file = new ZipFile + (findJar(new File(System.getProperty("user.dir")))); + + try { + byte[] buffer = new byte[4096]; + for (Enumeration e = file.entries(); + e.hasMoreElements();) + { + ZipEntry entry = e.nextElement(); + InputStream in = file.getInputStream(entry); + try { + int size = 0; + int c; while ((c = in.read(buffer)) != -1) size += c; + System.out.println + (entry.getName() + " " + entry.getCompressedSize() + " " + size); + } finally { + in.close(); + } + } + } finally { + file.close(); + } + } + +} diff --git a/sgx-jvm/avian/test/ZipOutputStreamTest.java b/sgx-jvm/avian/test/ZipOutputStreamTest.java new file mode 100644 index 0000000000..61a0335ef0 --- /dev/null +++ b/sgx-jvm/avian/test/ZipOutputStreamTest.java @@ -0,0 +1,214 @@ +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.zip.*; + +public class ZipOutputStreamTest +{ + private static final String TEST1 = "test1.txt"; + private static final String TEST2 = "test2.txt"; + private static final String TEST3 = "test3.txt"; + private static final String TEST4 = "test4.txt"; + + private static final String TEST1_CONTENTS = "\"this is a test\""; + private static final String TEST2_CONTENTS = "this is a\nmulti-line test"; + private static final String TEST3_CONTENTS = "74 68 69 73 20 69 73 20 61 20 74 65 73 74"; + private static final String TEST4_CONTENTS = "01110100 01101000 01101001 01110011 00100000 01101001 01110011 00100000 01100001 00100000 01110100 01100101 01110011 01110100"; + + private static final String BYTE_ZIP_PREFIX = "zosByte"; + private static final String ARRAY_ZIP_PREFIX = "zosArray"; + private static final String ARRAY_OFFSET_LENGTH_ZIP_PREFIX = "zosArrayOffsetLength"; + private static final String ZIP_SUFFIX = ".zip"; + + private static final Map FILES_CONTENTS; + static + { + Map m = new HashMap(); + m.put(TEST1, TEST1_CONTENTS); + m.put(TEST2, TEST2_CONTENTS); + m.put(TEST3, TEST3_CONTENTS); + m.put(TEST4, TEST4_CONTENTS); + FILES_CONTENTS = Collections.unmodifiableMap(m); + } + + private static enum WriteStyle { + Byte(ARRAY_ZIP_PREFIX), + Array(ARRAY_ZIP_PREFIX), + ArrayOffsetLength(ARRAY_OFFSET_LENGTH_ZIP_PREFIX); + + public final String prefix; + + private WriteStyle(String prefix) { + this.prefix = prefix; + } + } + private static byte[] buffer = new byte[1024]; + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + public static void main(String[] args) + throws Exception + { + List zipFiles = new ArrayList(2); + + try { + // Test byte-at-a-time write function + File f1 = createZip(WriteStyle.Byte); + zipFiles.add(f1); + verifyContents(f1.getAbsolutePath()); + // Test arraw write function + File f2 = createZip(WriteStyle.Array); + zipFiles.add(f2); + verifyContents(f2.getAbsolutePath()); + // Test arraw write function + File f3 = createZip(WriteStyle.ArrayOffsetLength); + zipFiles.add(f3); + verifyContents(f3.getAbsolutePath()); + } finally { + // Remove the created zip files + cleanUp(zipFiles); + } + } + + private static File createZip(WriteStyle writeStyle) + throws Exception + { + FileOutputStream outputStream = null; + ZipOutputStream zipContents = null; + + try + { + // Create a temporary zip file for this test + String prefix = writeStyle.prefix; + File outputZip = File.createTempFile(prefix, ZIP_SUFFIX); + + System.out.println("Created " + outputZip.getAbsolutePath()); + + // Prepare the streams + outputStream = new FileOutputStream(outputZip); + zipContents = new ZipOutputStream(outputStream); + + // Zip the file contents (convert directly from string to bytes) + long startTime = System.currentTimeMillis(); + for (Map.Entry f : FILES_CONTENTS.entrySet()) + { + String name = f.getKey(); + String contents = f.getValue(); + + System.out.println("Zipping " + name + "..."); + ZipEntry entry = new ZipEntry(name); + zipContents.putNextEntry(entry); + + byte[] bytesToWrite = contents.getBytes(); + + switch (writeStyle) { + case Byte: { + // Use the 1-parameter write method; takes a single byte + for (int i = 0; i < bytesToWrite.length; i++) + { + zipContents.write(bytesToWrite[i]); + } + } break; + + case Array: { + // Use 3-parameter write method; takes a buffer, offset, and length + zipContents.write(bytesToWrite); + } break; + + case ArrayOffsetLength: { + // Use 3-parameter write method; takes a buffer, offset, and length + zipContents.write(bytesToWrite, 0 , bytesToWrite.length); + } break; + + default: throw new RuntimeException("unexpected write style: " + writeStyle); + } + + // Done with this file + zipContents.closeEntry(); + System.out.println("Done"); + } + + // All files have been written + long endTime = System.currentTimeMillis(); + System.out.println("Finished " + outputZip.getName() + " in " + ((endTime - startTime) / 1000.0) + " seconds"); + return outputZip; + } + finally + { + if (zipContents != null) + zipContents.close(); + if (outputStream != null) + outputStream.close(); + } + } + + private static void verifyContents(String zipName) + throws Exception + { + System.out.println("Verify " + zipName); + ZipFile zf = null; + BufferedReader reader = null; + int numFilesInZip = 0; + + try + { + String line; + String contents; + + // Get the contents of each file in the zip + zf = new ZipFile(zipName); + for (Enumeration e = zf.entries(); e.hasMoreElements();) + { + ZipEntry entry = e.nextElement(); + reader = new BufferedReader(new InputStreamReader(zf.getInputStream(entry))); + contents = ""; + numFilesInZip += 1; + + while ((line = reader.readLine()) != null) + { + if (contents.length() > 0) + { + contents += "\n"; + } + contents += line; + } + reader.close(); + + // Assert that this file's contents are correct + expect(contents.equals(FILES_CONTENTS.get(entry.getName()))); + } + zf.close(); + + // Assert that the zip contained the correct number of files + expect(numFilesInZip == FILES_CONTENTS.size()); + } + finally + { + if (zf != null) + zf.close(); + if (reader != null) + reader.close(); + } + } + + private static void cleanUp(List zipFiles) + throws Exception + { + for (File f : zipFiles) + { + if (f.exists()) + { + f.delete(); + } + } + } +} diff --git a/sgx-jvm/avian/test/avian/TestReflection.java b/sgx-jvm/avian/test/avian/TestReflection.java new file mode 100644 index 0000000000..7b17dff52b --- /dev/null +++ b/sgx-jvm/avian/test/avian/TestReflection.java @@ -0,0 +1,9 @@ +package avian; + +import java.lang.reflect.Field; + +public class TestReflection { + public static Object get(Field field, Object target) throws Exception { + return field.get(target); + } +} diff --git a/sgx-jvm/avian/test/avian/testing/Asserts.java b/sgx-jvm/avian/test/avian/testing/Asserts.java new file mode 100644 index 0000000000..ed0ce5ec95 --- /dev/null +++ b/sgx-jvm/avian/test/avian/testing/Asserts.java @@ -0,0 +1,67 @@ +package avian.testing; + +import java.util.Collection; + +public class Asserts { + + public static void assertEquals(byte first, byte second) { + if(first != second) { + throw new RuntimeException(first+" is not equals to: "+second); + } + } + + public static void assertEquals(short first, short second) { + if(first != second) { + throw new RuntimeException(first+" is not equals to: "+second); + } + } + + public static void assertEquals(int first, int second) { + if(first != second) { + throw new RuntimeException(first+" is not equals to: "+second); + } + } + + public static void assertEquals(long first, long second) { + if(first != second) { + throw new RuntimeException(first+" is not equals to: "+second); + } + } + + public static void assertEquals(float first, float second) { + if(first != second) { + throw new RuntimeException(first+" is not equals to: "+second); + } + } + + public static void assertEquals(double first, double second) { + if(first != second) { + throw new RuntimeException(first+" is not equals to: "+second); + } + } + + + + + + public static void assertEquals(Object first, Object second) { + if(first == null && second == null) { + return; + } + if(!first.equals(second)) { + throw new RuntimeException(first+" is not equals to: "+second); + } + } + + public static void assertTrue(boolean flag) { + if (!flag) { + throw new RuntimeException("Error: "+flag+" is not True"); + } + } + + public static void assertContains(Enum element, Collection collection) { + if (!collection.contains(element)) { + throw new RuntimeException("expected " + element + " in the collection:"+collection); + } + } +} diff --git a/sgx-jvm/avian/test/avian/testing/annotations/Color.java b/sgx-jvm/avian/test/avian/testing/annotations/Color.java new file mode 100644 index 0000000000..f054b0420a --- /dev/null +++ b/sgx-jvm/avian/test/avian/testing/annotations/Color.java @@ -0,0 +1,5 @@ +package avian.testing.annotations; + +public enum Color { + Red, Yellow, Blue +} \ No newline at end of file diff --git a/sgx-jvm/avian/test/avian/testing/annotations/Test.java b/sgx-jvm/avian/test/avian/testing/annotations/Test.java new file mode 100644 index 0000000000..24b8229480 --- /dev/null +++ b/sgx-jvm/avian/test/avian/testing/annotations/Test.java @@ -0,0 +1,9 @@ +package avian.testing.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Test { + public String value() default "Hello, world!"; +} diff --git a/sgx-jvm/avian/test/avian/testing/annotations/TestComplex.java b/sgx-jvm/avian/test/avian/testing/annotations/TestComplex.java new file mode 100644 index 0000000000..4e271cc9d9 --- /dev/null +++ b/sgx-jvm/avian/test/avian/testing/annotations/TestComplex.java @@ -0,0 +1,14 @@ +package avian.testing.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface TestComplex { + public Test[] arrayValue(); + public Class classValue(); + public String stringValue(); + public char charValue(); + public double doubleValue(); +} + diff --git a/sgx-jvm/avian/test/avian/testing/annotations/TestEnum.java b/sgx-jvm/avian/test/avian/testing/annotations/TestEnum.java new file mode 100644 index 0000000000..f8a252fd54 --- /dev/null +++ b/sgx-jvm/avian/test/avian/testing/annotations/TestEnum.java @@ -0,0 +1,8 @@ +package avian.testing.annotations; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface TestEnum { + public Color value(); +} \ No newline at end of file diff --git a/sgx-jvm/avian/test/avian/testing/annotations/TestInteger.java b/sgx-jvm/avian/test/avian/testing/annotations/TestInteger.java new file mode 100644 index 0000000000..b7ec78d0bc --- /dev/null +++ b/sgx-jvm/avian/test/avian/testing/annotations/TestInteger.java @@ -0,0 +1,8 @@ +package avian.testing.annotations; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface TestInteger { + public int value(); +} \ No newline at end of file diff --git a/sgx-jvm/avian/test/ci.sh b/sgx-jvm/avian/test/ci.sh new file mode 100755 index 0000000000..a82e95c3eb --- /dev/null +++ b/sgx-jvm/avian/test/ci.sh @@ -0,0 +1,109 @@ +#!/usr/bin/env bash + +set -eo pipefail + +root_dir=$(pwd) + +flags="use-werror=true ${@}" + +is-mac() { + if [[ $(uname -s) == "Darwin" || ${TRAVIS_OS_NAME} == "osx" ]]; then + return 0 + fi + return 1 +} + +install-deps() { + if is-mac; then + echo "------ Installing dependencies for Mac ------" + else + echo "------ Installing dependencies for Linux ------" + sudo apt-get update -qq + sudo apt-get install -y libc6-dev-i386 mingw-w64 gcc-mingw-w64-x86-64 g++-mingw-w64-i686 binutils-mingw-w64-x86-64 lib32z1-dev zlib1g-dev g++-mingw-w64-x86-64 + fi +} + +run() { + echo '===============================================' + if [ ! $(pwd) = ${root_dir} ]; then + printf "cd $(pwd); " + fi + echo "${@}" + echo '===============================================' + "${@}" +} + +run_cmake() { + mkdir -p cmake-build + rm -rf cmake-build/* + cd cmake-build + run cmake ${@} .. + run make -j4 check + cd .. +} + +publish() { + local platforms="${1}" + local arches="${2}" + + local platform + for platform in ${platforms}; do + local arch + for arch in ${arches}; do + echo "------ Publishing ${platform}-${arch} ------" + ./gradlew artifactoryPublish -Pplatform=${platform} -Parch=${arch} + done + done +} + +has_flag() { + local arg=${1} + + local f + for f in ${flags}; do + local key=$(echo $f | awk -F '=' '{print $1}') + if [ ${key} = ${arg} ]; then + return 0 + fi + done + return 1 +} + +### START ### + +install-deps + +if [[ "${1}" == "PUBLISH" ]]; then + if is-mac; then + publish "macosx" "i386 x86_64" + elif [[ $(uname -s) == "Linux" ]]; then + publish "linux windows" "i386 x86_64" + fi +else + if [[ $(uname -o) != "Cygwin" ]]; then + run_cmake -DCMAKE_BUILD_TYPE=Debug + fi + + make_target=test + + if ! has_flag arch; then + run make ${flags} jdk-test + fi + + run make ${flags} ${make_target} + run make ${flags} mode=debug ${make_target} + run make ${flags} process=interpret ${make_target} + + if has_flag openjdk-src || ! has_flag openjdk; then + run make ${flags} mode=debug bootimage=true ${make_target} + run make ${flags} bootimage=true ${make_target} + run make ${flags} bootimage=true bootimage-test=true ${make_target} + fi + + if ! has_flag openjdk && ! has_flag android && ! has_flag arch; then + run make ${flags} openjdk=$JAVA_HOME ${make_target} + fi + + run make ${flags} tails=true continuations=true heapdump=true ${make_target} + run make ${flags} codegen-targets=all +fi diff --git a/sgx-jvm/avian/test/extra/ComposableContinuations.java b/sgx-jvm/avian/test/extra/ComposableContinuations.java new file mode 100644 index 0000000000..d33d39693b --- /dev/null +++ b/sgx-jvm/avian/test/extra/ComposableContinuations.java @@ -0,0 +1,93 @@ +package extra; + +import static avian.Continuations.shift; +import static avian.Cell.cons; +import static avian.Cell.equal; + +import avian.Cell; +import avian.Function; +import avian.Continuations; + +import java.util.concurrent.Callable; + +public class ComposableContinuations { + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + public static void main(String[] args) throws Exception { + expect(2 * Continuations.reset(new Callable() { + public Integer call() throws Exception { + return 1 + shift + (new Function,Integer>() { + public Integer call(Function continuation) + throws Exception + { + return continuation.call(5); + } + }); + } + }) == 12); + + expect(1 + Continuations.reset(new Callable() { + public Integer call() throws Exception { + return 2 * shift + (new Function,Integer>() { + public Integer call(Function continuation) + throws Exception + { + return continuation.call(continuation.call(4)); + } + }); + } + }) == 17); + + expect + (equal + (Continuations.,Cell>reset + (new Callable>() { + public Cell call() throws Exception { + shift(new Function,Cell>, + Cell>() + { + public Cell call + (Function,Cell> continuation) + throws Exception + { + return cons(1, continuation.call(null)); + } + }); + + shift(new Function,Cell>, + Cell>() + { + public Cell call + (Function,Cell> continuation) + throws Exception + { + return cons(2, continuation.call(null)); + } + }); + + return null; + } + }), cons(1, cons(2, null)))); + + expect + (equal + (Continuations.reset + (new Callable() { + public String call() throws Exception { + return new String + (shift(new Function,Integer>() { + public Integer call(Function continuation) + throws Exception + { + return Integer.parseInt + (continuation.call(new byte[] { 0x34, 0x32 })); + } + }), "UTF-8"); + } + }), 42)); + } +} diff --git a/sgx-jvm/avian/test/extra/Continuations.java b/sgx-jvm/avian/test/extra/Continuations.java new file mode 100644 index 0000000000..1186c43a4a --- /dev/null +++ b/sgx-jvm/avian/test/extra/Continuations.java @@ -0,0 +1,58 @@ +package extra; + +import static avian.Continuations.callWithCurrentContinuation; + +import avian.Function; +import avian.Callback; + +public class Continuations { + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + public static void main(String[] args) throws Exception { + expect + (callWithCurrentContinuation + (new Function,Integer>() { + public Integer call(Callback continuation) { + continuation.handleResult(42); + throw new AssertionError(); + } + }) == 42); + + expect + (callWithCurrentContinuation + (new Function,Integer>() { + public Integer call(Callback continuation) { + return 43; + } + }) == 43); + + try { + callWithCurrentContinuation(new Function,Integer>() { + public Integer call(Callback continuation) { + continuation.handleException(new MyException()); + throw new AssertionError(); + } + }); + throw new AssertionError(); + } catch (MyException e) { + e.printStackTrace(); + } + + try { + callWithCurrentContinuation(new Function,Integer>() { + public Integer call(Callback continuation) + throws MyException + { + throw new MyException(); + } + }); + throw new AssertionError(); + } catch (MyException e) { + e.printStackTrace(); + } + } + + private static class MyException extends Exception { } +} diff --git a/sgx-jvm/avian/test/extra/Coroutines.java b/sgx-jvm/avian/test/extra/Coroutines.java new file mode 100644 index 0000000000..3afd2dce73 --- /dev/null +++ b/sgx-jvm/avian/test/extra/Coroutines.java @@ -0,0 +1,93 @@ +package extra; + +import static avian.Continuations.callWithCurrentContinuation; + +import avian.Function; +import avian.Callback; + +public class Coroutines { + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static void produce(Consumer consumer) throws Exception { + System.out.println("produce \"a\""); + consumer.consume('a'); + + System.out.println("produce \"b\""); + consumer.consume('b'); + + System.out.println("produce \"c\""); + consumer.consume('c'); + } + + private static void consume(Producer producer) throws Exception { + char v = producer.produce(); + System.out.println("consume \"" + v + "\""); + expect(v == 'a'); + + v = producer.produce(); + System.out.println("consume \"" + v + "\""); + expect(v == 'b'); + + v = producer.produce(); + System.out.println("consume \"" + v + "\""); + expect(v == 'c'); + } + + public static void main(String[] args) throws Exception { + final CoroutineState state = new CoroutineState(); + + final Consumer consumer = new Consumer() { + public void consume(final Character c) throws Exception { + callWithCurrentContinuation(new Function,Object>() { + public Object call(Callback continuation) { + state.produceNext = continuation; + + state.consumeNext.handleResult(c); + + throw new AssertionError(); + } + }); + } + }; + + final Producer producer = new Producer() { + final Function,Character> receiver + = new Function,Character>() { + public Character call(Callback continuation) + throws Exception + { + state.consumeNext = continuation; + + if (state.produceNext == null) { + Coroutines.produce(consumer); + } else { + state.produceNext.handleResult(null); + } + + throw new AssertionError(); + } + }; + + public Character produce() throws Exception { + return callWithCurrentContinuation(receiver); + } + }; + + consume(producer); + } + + private static class CoroutineState { + public Callback produceNext; + public Callback consumeNext; + } + + private interface Producer { + public T produce() throws Exception; + } + + private interface Consumer { + public void consume(T value) throws Exception; + } +} diff --git a/sgx-jvm/avian/test/extra/DumpStats.java b/sgx-jvm/avian/test/extra/DumpStats.java new file mode 100644 index 0000000000..73b9170bed --- /dev/null +++ b/sgx-jvm/avian/test/extra/DumpStats.java @@ -0,0 +1,159 @@ +package extra; + +import java.io.PrintStream; +import java.io.InputStream; +import java.io.FileInputStream; +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.EOFException; +import java.util.Map; +import java.util.HashMap; +import java.util.Comparator; +import java.util.Arrays; + +/** + * This is a simple utility to generate and print statistics from a + * heap dump generated by Avian's heapdump.cpp. The output is a list + * of classes (identified by number in the case of anonymous, + * VM-internal classes), each followed by (1) the total memory + * footprint of all instances of the class in machine words, and (2) + * the number of instances. The output is ordered by instance memory + * footprint. + */ +public class DumpStats { + private static final int Root = 0; + private static final int Size = 1; + private static final int ClassName = 2; + private static final int Push = 3; + private static final int Pop = 4; + + private static int readInt(InputStream in) throws IOException { + int b1 = in.read(); + int b2 = in.read(); + int b3 = in.read(); + int b4 = in.read(); + if (b4 == -1) throw new EOFException(); + return (int) ((b1 << 24) | (b2 << 16) | (b3 << 8) | (b4)); + } + + private static String readString(InputStream in) throws IOException { + int count = readInt(in); + byte[] b = new byte[count]; + int offset = 0; + int c; + while ((c = in.read(b, offset, b.length - offset)) != -1 + && offset < b.length) + { + offset += c; + } + if (offset != b.length) throw new EOFException(); + return new String(b); + } + + private static Record record(Map map, int key) { + Record r = map.get(key); + if (r == null) { + map.put(key, r = new Record(key)); + } + return r; + } + + private static Map read(InputStream in) + throws IOException + { + boolean done = false; + boolean popped = false; + int size = 0; + int last = 0; + Map map = new HashMap(); + + while (! done) { + int flag = in.read(); + switch (flag) { + case Root: { + last = readInt(in); + popped = false; + } break; + + case ClassName: { + record(map, last).name = readString(in); + } break; + + case Push: { + last = readInt(in); + if (! popped) { + Record r = record(map, last); + r.footprint += size; + ++ r.count; + } + popped = false; + } break; + + case Pop: { + popped = true; + } break; + + case Size: { + size = readInt(in); + } break; + + case -1: + done = true; + break; + + default: + throw new RuntimeException("bad flag: " + flag); + } + } + + return map; + } + + private static void usageAndExit() { + System.err.println("usage: java DumpStats "); + } + + public static void main(String[] args) throws Exception { + if (args.length != 2) { + usageAndExit(); + } + + Map map = read + (new BufferedInputStream(new FileInputStream(args[0]))); + + Record[] array = map.values().toArray(new Record[map.size()]); + Arrays.sort(array, new Comparator() { + public int compare(Record a, Record b) { + return b.footprint - a.footprint; + } + }); + + int wordSize = Integer.parseInt(args[1]); + + int footprint = 0; + int count = 0; + for (Record r: array) { + if (r.name == null) { + r.name = String.valueOf(r.key); + } + System.out.println + (r.name + ": " + (r.footprint * wordSize) + " " + r.count); + footprint += r.footprint; + count += r.count; + } + + System.out.println(); + System.out.println("total: " + (footprint * wordSize) + " " + count); + } + + private static class Record { + public final int key; + public String name; + public int footprint; + public int count; + + public Record(int key) { + this.key = key; + } + } +} diff --git a/sgx-jvm/avian/test/extra/DynamicWind.java b/sgx-jvm/avian/test/extra/DynamicWind.java new file mode 100644 index 0000000000..bbc305842c --- /dev/null +++ b/sgx-jvm/avian/test/extra/DynamicWind.java @@ -0,0 +1,338 @@ +package extra; + +import static avian.Continuations.callWithCurrentContinuation; +import static avian.Continuations.dynamicWind; + +import avian.Function; +import avian.Callback; + +import java.util.concurrent.Callable; + +public class DynamicWind { + private int before; + private int task; + private int after; + private int continuationCount; + private Callback continuationReference; + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private void unwindTest(final Callable unwind) throws Exception { + System.out.println("unwindTest enter"); + + try { + expect(dynamicWind(new Runnable() { + public void run() { + System.out.println("unwindTest before"); + + expect(before == 0); + expect(task == 0); + expect(after == 0); + + before = 1; + } + }, new Callable() { + public Integer call() throws Exception { + System.out.println("unwindTest thunk"); + + expect(before == 1); + expect(task == 0); + expect(after == 0); + + task = 1; + + return unwind.call(); + } + }, + new Runnable() { + public void run() { + System.out.println("unwindTest after"); + + expect(before == 1); + expect(task == 1); + expect(after == 0); + + after = 1; + } + }) == 42); + } catch (MyException e) { + e.printStackTrace(); + } + + System.out.println("unwindTest expect"); + + expect(before == 1); + expect(task == 1); + expect(after == 1); + + System.out.println("unwindTest exit"); + } + + private void normalUnwind() throws Exception { + unwindTest(new Callable() { + public Integer call() { + return 42; + } + }); + } + + private void exceptionUnwind() throws Exception { + unwindTest(new Callable() { + public Integer call() throws Exception { + throw new MyException(); + } + }); + } + + private void continuationUnwindTest + (final Function,Integer> receiver) + throws Exception + { + System.out.println("continuationUnwindTest enter"); + + try { + expect + (callWithCurrentContinuation + (new Function,Integer>() { + public Integer call(final Callback continuation) + throws Exception + { + unwindTest(new Callable() { + public Integer call() throws Exception { + return receiver.call(continuation); + } + }); + throw new AssertionError(); + } + }) == 42); + } catch (MyException e) { + e.printStackTrace(); + } + + System.out.println("continuationUnwindTest expect"); + + expect(before == 1); + expect(task == 1); + expect(after == 1); + + System.out.println("continuationUnwindTest exit"); + } + + private void continuationResultUnwind() throws Exception { + continuationUnwindTest(new Function,Integer>() { + public Integer call(final Callback continuation) { + continuation.handleResult(42); + throw new AssertionError(); + } + }); + } + + private void continuationExceptionUnwind() throws Exception { + continuationUnwindTest(new Function,Integer>() { + public Integer call(final Callback continuation) { + continuation.handleException(new MyException()); + throw new AssertionError(); + } + }); + } + + private void rewindTest(final Callable unwind, Runnable rewind) + throws Exception + { + System.out.println("rewindTest enter"); + + int value; + try { + value = dynamicWind(new Runnable() { + public void run() { + System.out.println("rewindTest before"); + + expect(before == continuationCount); + expect(task == continuationCount); + expect(after == continuationCount); + + ++ before; + } + }, new Callable() { + public Integer call() throws Exception { + System.out.println("rewindTest thunk"); + + expect(before == 1); + expect(task == 0); + expect(after == 0); + + task = 1; + + return callWithCurrentContinuation + (new Function,Integer>() { + public Integer call(final Callback continuation) + throws Exception + { + continuationReference = continuation; + return unwind.call(); + } + }); + } + }, new Runnable() { + public void run() { + System.out.println("rewindTest after"); + + expect(before == continuationCount + 1); + expect(task == 1); + expect(after == continuationCount); + + ++ after; + } + }); + } catch (MyException e) { + value = e.value; + } + + System.out.println("rewindTest expect"); + + expect(value == continuationCount); + + if (value == 0) { + System.out.println("rewindTest expect 0"); + + expect(before == 1); + expect(task == 1); + expect(after == 1); + + continuationCount = 1; + rewind.run(); + throw new AssertionError(); + } else { + System.out.println("rewindTest expect 1"); + + expect(value == 1); + expect(before == 2); + expect(task == 1); + expect(after == 2); + } + + System.out.println("rewindTest exit"); + } + + private void continuationResultRewind() throws Exception { + rewindTest(new Callable() { + public Integer call() { + return 0; + } + }, new Runnable() { + public void run() { + continuationReference.handleResult(1); + } + }); + } + + private void continuationExceptionRewind() throws Exception { + rewindTest(new Callable() { + public Integer call() throws Exception { + throw new MyException(0); + } + }, new Runnable() { + public void run() { + continuationReference.handleException(new MyException(1)); + } + }); + } + + private void continuationResultUnwindAndRewind() throws Exception { + rewindTest(new Callable() { + public Integer call() { + return 0; + } + }, new Runnable() { + public void run() { + try { + new DynamicWind().unwindTest(new Callable() { + public Integer call() { + continuationReference.handleResult(1); + throw new AssertionError(); + } + }); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } + + private void continuationExceptionUnwindAndRewind() throws Exception { + rewindTest(new Callable() { + public Integer call() throws Exception { + throw new MyException(0); + } + }, new Runnable() { + public void run() { + try { + new DynamicWind().unwindTest(new Callable() { + public Integer call() { + continuationReference.handleException(new MyException(1)); + throw new AssertionError(); + } + }); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } + + private void continuationResultUnwindAndRewindWithShared() throws Exception { + unwindTest(new Callable() { + public Integer call() throws Exception { + new DynamicWind().continuationResultUnwindAndRewind(); + return 42; + } + }); + } + + private void continuationExceptionUnwindAndRewindWithShared() + throws Exception + { + unwindTest(new Callable() { + public Integer call() throws Exception { + new DynamicWind().continuationExceptionUnwindAndRewind(); + return 42; + } + }); + } + + public static void main(String[] args) throws Exception { + new DynamicWind().normalUnwind(); + + new DynamicWind().exceptionUnwind(); + + new DynamicWind().continuationResultUnwind(); + + new DynamicWind().continuationExceptionUnwind(); + + new DynamicWind().continuationResultRewind(); + + new DynamicWind().continuationExceptionRewind(); + + new DynamicWind().continuationResultUnwindAndRewind(); + + new DynamicWind().continuationExceptionUnwindAndRewind(); + + new DynamicWind().continuationResultUnwindAndRewindWithShared(); + + new DynamicWind().continuationExceptionUnwindAndRewindWithShared(); + } + + private static class MyException extends Exception { + public final int value; + + public MyException() { + this(0); + } + + public MyException(int value) { + this.value = value; + } + } +} diff --git a/sgx-jvm/avian/test/extra/Memory.java b/sgx-jvm/avian/test/extra/Memory.java new file mode 100644 index 0000000000..b2e8cfbfea --- /dev/null +++ b/sgx-jvm/avian/test/extra/Memory.java @@ -0,0 +1,172 @@ +package extra; + +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.TreeSet; + +public class Memory { + private static final int ITERATION_COUNT=1; + + private static class Item { + private static int instanceCount=0; + private final int index; + private final int val; + public Item(int i) { val = i; index = instanceCount++; } + public int value() { return val; } + public int index() { return index; } + } + + private static void traceFunc(String s) { + if (false) { + System.out.println(s); + } + } + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static int runningSum(Item[] items) { + int sum=0; + for (Item item : items) { + sum += item.value(); + } + return sum; + } + + private static int runningSum(Collection items) { + int sum=0; + for (Item item : items) { + sum += item.value(); + } + return sum; + } + + private static final void testArray() { + traceFunc("testArray()"); + Item[] items = new Item[1750]; + + for (int iter=0; iter < ITERATION_COUNT; iter++) { + for (int i=0; i < 1000; i++) { + items[i] = new Item(1); + } + for (int i=0; i < 500; i++) { + items[i+1000] = new Item(4); + } + for (int i=0; i < 250; i++) { + items[i+1500] = new Item(9); + } + expect(runningSum(items) == (1000*1 + 500*4 + 250*9)); + Item[] zeroItems = new Item[300]; + for (int i=0; i < 300; i++) { + zeroItems[i] = new Item(0); + } + System.arraycopy(zeroItems, 0, items, 900, zeroItems.length); + for (int i=0; i < 10000; i++) { + items[0] = new Item(1); + } + expect(runningSum(items) == (900*1 + 300*4 + 250*9)); + for (int i=0; i < 300; i++) { + zeroItems[i] = new Item((i+900) < 1000 ? 1 : 4); + } + for (int i=0; i < 10000; i++) { + items[0] = new Item(1); + } + expect(runningSum(items) == (900*1 + 300*4 + 250*9)); + System.arraycopy(zeroItems, 0, items, 900, zeroItems.length); + expect(runningSum(items) == (1000*1 + 500*4 + 250*9)); + for (int i=0; i < 1750; i++) { + items[i] = null; + } + } + } + + private static final void testHashMap() { + traceFunc("testHashMap()"); + HashMap items = new HashMap(); + for (int iter=0; iter < ITERATION_COUNT; iter++) { + for (int i=0; i < 1000; i++) { + items.put(i, new Item(1)); + } + for (int i=0; i < 500; i++) { + items.put(i+1000, new Item(4)); + } + for (int i=0; i < 250; i++) { + items.put(i+1500, new Item(9)); + } + expect(runningSum(items.values()) == (1000*1 + 500*4 + 250*9)); + for (int i = 900; i < 1200; i++) { + items.remove(i); + } + expect(runningSum(items.values()) == (900*1 + 300*4 + 250*9)); + for (int i = 900; i < 1200; i++) { + items.put(i, new Item(i < 1000 ? 1 : 4)); + } + expect(runningSum(items.values()) == (1000*1 + 500*4 + 250*9)); + items.clear(); + } + } + + private static final void testLinkedList() { + traceFunc("testLinkedList()"); + LinkedList items = new LinkedList(); + for (int iter=0; iter < ITERATION_COUNT; iter++) { + for (int i=0; i < 1000; i++) { + items.add(new Item(1)); + } + for (int i=0; i < 500; i++) { + items.add(new Item(4)); + } + for (int i=0; i < 250; i++) { + items.add(new Item(9)); + } + expect(runningSum(items) == (1000*1 + 500*4 + 250*9)); + for (int i = 1199; i >= 900; i--) { + items.remove(i); + } + expect(runningSum(items) == (900*1 + 300*4 + 250*9)); + for (int i = 900; i < 1200; i++) { + items.add(new Item(i < 1000 ? 1 : 4)); + } + expect(runningSum(items) == (1000*1 + 500*4 + 250*9)); + items.clear(); + } + } + + private static final void testTreeSet() { + traceFunc("testTreeSet()"); + TreeSet items = new TreeSet(new Comparator() { + public int compare(Item i1, Item i2) { + int r = i1.value() - i2.value(); + if (r == 0) { + return i1.index() - i2.index(); + } + return r; + } + }); + for (int iter=0; iter < ITERATION_COUNT; iter++) { + for (int i=0; i < 1000; i++) { + items.add(new Item(1)); + } + for (int i=0; i < 500; i++) { + items.add(new Item(4)); + } + for (int i=0; i < 250; i++) { + items.add(new Item(9)); + } + expect(runningSum(items) == (1000*1 + 500*4 + 250*9)); + items.clear(); + } + } + + public static void main(String args[]) { + for (int i=0; i < 10; i++) { + testArray(); + testHashMap(); + testLinkedList(); + testTreeSet(); + } + } +} diff --git a/sgx-jvm/avian/test/extra/PrintDump.java b/sgx-jvm/avian/test/extra/PrintDump.java new file mode 100644 index 0000000000..8f43f419c9 --- /dev/null +++ b/sgx-jvm/avian/test/extra/PrintDump.java @@ -0,0 +1,101 @@ +package extra; + +import java.io.PrintStream; +import java.io.InputStream; +import java.io.FileInputStream; +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.EOFException; + +/** + * This is a simple utility to print the contents of a heap dump + * generated by Avian's heapdump.cpp in a human-readable format. + */ +public class PrintDump { + private static final int Root = 0; + private static final int Size = 1; + private static final int ClassName = 2; + private static final int Push = 3; + private static final int Pop = 4; + + private static void indent(PrintStream out, int level) { + for (; level > 0; --level) out.print(" "); + } + + private static int readInt(InputStream in) throws IOException { + int b1 = in.read(); + int b2 = in.read(); + int b3 = in.read(); + int b4 = in.read(); + if (b4 == -1) throw new EOFException(); + return (int) ((b1 << 24) | (b2 << 16) | (b3 << 8) | (b4)); + } + + private static String readString(InputStream in) throws IOException { + int count = readInt(in); + byte[] b = new byte[count]; + int offset = 0; + int c; + while ((c = in.read(b, offset, b.length - offset)) != -1 + && offset < b.length) + { + offset += c; + } + if (offset != b.length) throw new EOFException(); + return new String(b); + } + + private static void pipe(InputStream in, PrintStream out) + throws IOException + { + boolean done = false; + boolean popped = false; + int level = 0; + while (! done) { + int flag = in.read(); + switch (flag) { + case Root: { + out.print("\nroot " + readInt(in)); + popped = false; + } break; + + case ClassName: { + out.print(" class " + readString(in)); + } break; + + case Push: { + ++ level; + out.println(); + indent(out, level); + if (! popped) { + out.print("first "); + } + out.print("child " + readInt(in)); + popped = false; + } break; + + case Pop: { + -- level; + popped = true; + } break; + + case Size: { + out.print(" size " + readInt(in)); + } break; + + case -1: + out.println(); + out.flush(); + done = true; + break; + + default: + throw new RuntimeException("bad flag: " + flag); + } + } + } + + public static void main(String[] args) throws Exception { + pipe(new BufferedInputStream(new FileInputStream(args[0])), System.out); + } +} diff --git a/sgx-jvm/avian/test/extra/QueryDump.java b/sgx-jvm/avian/test/extra/QueryDump.java new file mode 100644 index 0000000000..156cd64d36 --- /dev/null +++ b/sgx-jvm/avian/test/extra/QueryDump.java @@ -0,0 +1,358 @@ +package extra; + +import java.io.PrintStream; +import java.io.InputStream; +import java.io.FileInputStream; +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.EOFException; +import java.util.Set; +import java.util.HashSet; +import java.util.Map; +import java.util.HashMap; +import java.util.Comparator; +import java.util.Arrays; +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; + +public class QueryDump { + private static final int Root = 0; + private static final int Size = 1; + private static final int ClassName = 2; + private static final int Push = 3; + private static final int Pop = 4; + + private static int readInt(InputStream in) throws IOException { + int b1 = in.read(); + int b2 = in.read(); + int b3 = in.read(); + int b4 = in.read(); + if (b4 == -1) throw new EOFException(); + return (int) ((b1 << 24) | (b2 << 16) | (b3 << 8) | (b4)); + } + + private static String readString(InputStream in) throws IOException { + int count = readInt(in); + byte[] b = new byte[count]; + int offset = 0; + int c; + while ((c = in.read(b, offset, b.length - offset)) != -1 + && offset < b.length) + { + offset += c; + } + if (offset != b.length) throw new EOFException(); + return new String(b); + } + + private static Record record(Map map, int key) { + Record r = map.get(key); + if (r == null) { + map.put(key, r = new Record(key)); + } + return r; + } + + private static void push(List stack, T value) { + stack.add(value); + } + + private static T pop(List stack) { + return stack.remove(stack.size() - 1); + } + + private static T peek(List stack, int offset) { + return stack.get(stack.size() - 1 - offset); + } + + private static T peek(List stack) { + return peek(stack, 0); + } + + private static Set nodes(Record record) { + if (record.nodes == null) { + record.nodes = new HashSet(2); + } + return record.nodes; + } + + private static void query(Map nodes, Record[] query, + List stack, int index) + { + Node node = nodes.get(peek(stack, index).key); + if (node != null) { + int base = node.index(); + for (int i = base + 1; i < query.length; ++i) { + int peek = index + i - base; + if (peek < stack.size()) { + Instance instance = peek(stack, peek); + if (query[i] == instance.record) { + TreeNode next = (TreeNode) nodes.get(instance); + if (next == null) { + nodes.put(instance.key, next = new TreeNode(instance, i)); + } + next.children.add(node); + node = next; + } else { + return; + } + } else { + return; + } + } + + if (index + query.length - base < stack.size()) { + nodes(peek(stack, index + query.length - base).record).add(node); + } + } + } + + private static void query(Map nodes, Record[] query, + List stack) + { + if (stack.size() > 1) { + Instance instance = peek(stack, 1); + if (instance != null && instance.record == query[0]) { + Node node = nodes.get(instance.key); + if (node == null) { + nodes.put(instance.key, new LeafNode(instance)); + query(nodes, query, stack, 1); + } + return; + } + } + + query(nodes, query, stack, 0); + } + + private static Map read(InputStream in, + String[] queryClasses) + throws IOException + { + boolean done = false; + boolean popped = false; + Map records = new HashMap(); + Map nodes = new HashMap(); + List stack = new ArrayList(); + Record[] query = new Record[queryClasses.length]; + + Record roots = new Record(-1, ""); + records.put(roots.key, roots); + + while (! done) { + int flag = in.read(); + switch (flag) { + case Root: { + stack.clear(); + push(stack, new Instance(readInt(in))); + + query(nodes, query, stack); + + popped = false; + // System.out.println("root " + last); + } break; + + case ClassName: { + String name = readString(in); + Record r = record(records, peek(stack).key); + r.name = name; + + for (int i = 0; i < queryClasses.length; ++i) { + if (queryClasses[i].equals(name)) { + query[i] = r; + } + } + + query(nodes, query, stack); + } break; + + case Push: { + int key = readInt(in); + + if (! popped) { + peek(stack).record = record(records, key); + } + + push(stack, new Instance(key)); + + query(nodes, query, stack); + + popped = false; + } break; + + case Pop: { + pop(stack); + + popped = true; + } break; + + case Size: { + peek(stack).size = readInt(in); + } break; + + case -1: + done = true; + break; + + default: + throw new RuntimeException("bad flag: " + flag); + } + } + + return records; + } + + private static String[] copy(String[] array, int offset, int length) { + String[] copy = new String[length]; + if (length > 0) { + System.arraycopy(array, offset, copy, 0, length); + } + + return copy; + } + + private static void visitLeaves(Set nodes, LeafVisitor visitor) { + for (Node n: nodes) { + n.visitLeaves(visitor); + } + } + + private static void usageAndExit() { + System.err.println("usage: java QueryDump " + + " ..."); + } + + public static void main(String[] args) throws Exception { + if (args.length < 3) { + usageAndExit(); + } + + Map map = read + (new BufferedInputStream(new FileInputStream(args[0])), + copy(args, 2, args.length - 2)); + + for (Iterator it = map.values().iterator(); it.hasNext();) { + final Record r = it.next(); + if (r.nodes == null) { + it.remove(); + } else { + visitLeaves(r.nodes, new LeafVisitor() { + private Set set = new HashSet(); + + public void visit(LeafNode node) { + if (! set.contains(node.instance)) { + r.footprint += node.instance.size; + ++ r.count; + } + set.add(node.instance); + } + }); + } + } + + Record[] array = map.values().toArray(new Record[map.size()]); + Arrays.sort(array, new Comparator() { + public int compare(Record a, Record b) { + return b.footprint - a.footprint; + } + }); + + int wordSize = Integer.parseInt(args[1]); + + int footprint = 0; + int count = 0; + for (Record r: array) { + if (r.name == null) { + r.name = String.valueOf(r.key); + } + System.out.println + (r.name + ": " + (r.footprint * wordSize) + " " + r.count); + footprint += r.footprint; + count += r.count; + } + + System.out.println(); + System.out.println("total: " + (footprint * wordSize) + " " + count); + } + + private static class Record { + public final int key; + public String name; + public int footprint; + public int count; + public Set nodes; + + public Record(int key) { + this(key, null); + } + + public Record(int key, String name) { + this.key = key; + this.name = name; + } + + public String toString() { + return name; + } + } + + private static class Instance { + public final int key; + public int size; + public Record record; + + public Instance(int key) { + this.key = key; + } + + public String toString() { + return "[" + key + " " + record + "]"; + } + } + + public interface Node { + public void visitLeaves(LeafVisitor visitor); + public int index(); + } + + public static class LeafNode implements Node { + public final Instance instance; + + public LeafNode(Instance instance) { + this.instance = instance; + } + + public void visitLeaves(LeafVisitor visitor) { + visitor.visit(this); + } + + public int index() { + return 0; + } + } + + public static class TreeNode implements Node { + public final Instance instance; + public final int index; + + public final Set children = new HashSet(2); + + public TreeNode(Instance instance, int index) { + this.instance = instance; + this.index = index; + } + + public void visitLeaves(LeafVisitor visitor) { + QueryDump.visitLeaves(children, visitor); + } + + public int index() { + return index; + } + } + + public interface LeafVisitor { + public void visit(LeafNode node); + } +} diff --git a/sgx-jvm/avian/test/extra/RuntimeExec.java b/sgx-jvm/avian/test/extra/RuntimeExec.java new file mode 100644 index 0000000000..23adbb9105 --- /dev/null +++ b/sgx-jvm/avian/test/extra/RuntimeExec.java @@ -0,0 +1,59 @@ +package extra; + +import java.lang.Runtime; +import java.lang.Process; + +public class RuntimeExec { + public static void main(String[] args) throws java.io.IOException, java.lang.InterruptedException { + Runtime runtime = Runtime.getRuntime(); + String ieStr = null; + String charmapStr = null; + String[] firefox = new String[2]; + + if(System.getProperty("os.name").equals("windows")){ + System.out.println("Executing internet explorer"); + ieStr = "\"c:\\program files\\internet explorer\\iexplore.exe\" http://www.google.com"; + } else { + System.out.println("Executing Firefox using string"); + ieStr = "firefox http://www.google.com"; + } + Process ie = runtime.exec(ieStr); + + if(System.getProperty("os.name").equals("windows")){ + System.out.println("Executing firefox"); + firefox[0] = "c:\\program files\\mozilla firefox\\firefox.exe"; + firefox[1] = "http://www.google.com"; + } else { + System.out.println("Executing Firefox using array"); + firefox[0] = "firefox"; + firefox[1] = "http://www.google.com"; + } + Process ff = runtime.exec(firefox); + + boolean ffSuccess = false; + boolean ieSuccess = false; + while(!(ieSuccess && ffSuccess)){ + if(!ffSuccess){ + try{ + System.out.println("Exit value from string exec: " + ff.exitValue()); + ffSuccess = true; + } catch(IllegalThreadStateException e) {} + } + if(!ieSuccess){ + try{ + System.out.println("Exit value from array exec: " + ie.exitValue()); + ieSuccess = true; + } catch(IllegalThreadStateException e) {} + } + } + if(System.getProperty("os.name").equals("windows")){ + System.out.println("Executing and waiting for charmap"); + charmapStr = "c:\\windows\\system32\\charmap.exe"; + } else { + System.out.println("Executing and waiting for firefox"); + charmapStr = "firefox http://www.google.com"; + } + Process cm = runtime.exec(charmapStr); + System.out.println("Exit value: " + cm.waitFor()); + } +} diff --git a/sgx-jvm/avian/test/extra/SendFile.java b/sgx-jvm/avian/test/extra/SendFile.java new file mode 100644 index 0000000000..da77a33a21 --- /dev/null +++ b/sgx-jvm/avian/test/extra/SendFile.java @@ -0,0 +1,73 @@ +package extra; + +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.io.IOException; +import java.nio.channels.Selector; +import java.nio.channels.SelectionKey; +import java.nio.channels.SocketChannel; +import java.io.OutputStream; +import java.io.FileInputStream; + +public class SendFile { + private static class SocketOutputStream extends OutputStream { + private final SocketChannel channel; + private final Selector selector; + public SocketOutputStream(String host, int port) throws Exception { + channel = SocketChannel.open(); + channel.connect(new InetSocketAddress(host, port)); + channel.configureBlocking(false); + selector = Selector.open(); + channel.register(selector, SelectionKey.OP_WRITE, null); + } + + public void close() throws IOException { + channel.close(); + } + + public void write(int c) { + throw new RuntimeException("Do not use!"); + } + public void write(byte[] buffer, int offset, int length) + throws IOException { + ByteBuffer buf = ByteBuffer.wrap(buffer); + buf.position(offset); + buf.limit(offset+length); + while (buf.hasRemaining()) { + selector.select(10000); + for (SelectionKey key : selector.selectedKeys()) { + if (key.isWritable() && (key.channel() == channel)) { + channel.write(buf); + } + } + } + } + } + + public static void sendFile(String file, String host, int port) + throws Exception { + System.out.println("Sending " + file); + OutputStream os = new SocketOutputStream(host, port); + FileInputStream is = new FileInputStream(file); + byte[] buf = new byte[16384]; + int count=-1; + while ((count = is.read(buf)) >= 0) { + os.write(buf, 0, count); + } + is.close(); + os.close(); + } + + public static void main(String args[]) { + if (args.length != 2) { + System.out.println("Usage: SendFile file host"); + } else { + try { + sendFile(args[0], args[1], 8988); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + } +} + diff --git a/sgx-jvm/avian/test/extra/SendServer.java b/sgx-jvm/avian/test/extra/SendServer.java new file mode 100644 index 0000000000..5f60c962c1 --- /dev/null +++ b/sgx-jvm/avian/test/extra/SendServer.java @@ -0,0 +1,92 @@ +package extra; + +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; + +public class SendServer { + private static char cIndex = 'A'; + private static ByteBuffer inBuf = ByteBuffer.allocate(8192); + + private static void dumpByteBuffer(char note, ByteBuffer buf) { + System.out.println(note + ": Buffer position: " + buf.position() + " limit: " + + buf.limit() + " capacity: " + buf.capacity() + " remaining: " + + buf.remaining()); + } + + private static class Connection { + private final char myIndex; + private final java.io.FileOutputStream fos; + + public Connection() throws Exception { + myIndex = cIndex++; + fos = new java.io.FileOutputStream("dump." + myIndex); + } + + public void handleRead(SocketChannel channel) throws Exception { + int count = -1; + while ((count = channel.read(inBuf)) > 0) { + System.out.println(myIndex + ": read " + count); + } + inBuf.flip(); + fos.write(inBuf.array(), inBuf.arrayOffset()+inBuf.position(), inBuf.remaining()); + inBuf.position(inBuf.limit()); + if (count < 0) { + System.out.println(myIndex + ": Closing channel"); + fos.close(); + channel.close(); + } +// dumpByteBuffer(myIndex, inBuf); + inBuf.compact(); + } + } + + public void runMainLoop() throws Exception { + boolean keepRunning = true; + int port = 8988; + ServerSocketChannel serverChannel = ServerSocketChannel.open(); + try { + serverChannel.configureBlocking(false); + serverChannel.socket().bind(new InetSocketAddress("0.0.0.0", port)); + Selector selector = Selector.open(); + serverChannel.register(selector, SelectionKey.OP_ACCEPT, null); + while (keepRunning) { + System.out.println("Running main loop"); + selector.select(10000); + for (SelectionKey key : selector.selectedKeys()) { + if (key.isAcceptable()) { + System.out.println("Accepting new connection"); + SocketChannel c = ((ServerSocketChannel) key.channel()).accept(); + if (c != null) { + c.configureBlocking(false); + c.register(selector, SelectionKey.OP_READ, new Connection()); + } + } else { + SocketChannel c = (SocketChannel) key.channel(); + if (c.isOpen() && key.isReadable()) { + Connection connection = (Connection)key.attachment(); + connection.handleRead(c); + } + } + } + selector.selectedKeys().clear(); + } + } finally { + serverChannel.close(); + } + } + + public static void main(String args[]) { + try { + System.out.println("Starting server"); + if (args.length > 0) { + new SendServer().runMainLoop(); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } +} diff --git a/sgx-jvm/avian/test/extra/Sockets.java b/sgx-jvm/avian/test/extra/Sockets.java new file mode 100644 index 0000000000..75cd278982 --- /dev/null +++ b/sgx-jvm/avian/test/extra/Sockets.java @@ -0,0 +1,41 @@ +package extra; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.Socket; +import java.net.UnknownHostException; + +public class Sockets { + + /** + * @param args + * @throws IOException + * @throws UnknownHostException + */ + public static void main(String[] args) throws UnknownHostException, + IOException { + System.out.print("Requesting... " + args[0] + "\n"); + Socket sock = new Socket(args[0], 80); + try { + BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream())); + String request = "GET /?gws_rd=cr HTTP/1.1\r\n" + + "Host: " + args[0] + "\r\n" + "Accept: */*\r\n" + + "User-Agent: Java\r\n" + "Connection: close\r\n" + "\r\n"; + bw.write(request); + bw.flush(); + + BufferedReader br = new BufferedReader(new InputStreamReader(sock.getInputStream())); + String read = null; + while ((read = br.readLine()) != null) { + System.out.println(read); + } + bw.close(); + } finally { + sock.close(); + } + } + +} \ No newline at end of file diff --git a/sgx-jvm/avian/test/extra/Tails.java b/sgx-jvm/avian/test/extra/Tails.java new file mode 100644 index 0000000000..a99655531a --- /dev/null +++ b/sgx-jvm/avian/test/extra/Tails.java @@ -0,0 +1,49 @@ +package extra; + +public class Tails { + private static final int Limit = 1000000; + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static int staticMethod(Interface i, int n) { + if (n < Limit) { + return i.interfaceMethod(n + 1); + } else { + return leafMethod(n); + } + } + + private static int leafMethod(int n) { + expect(new Throwable().getStackTrace().length == 2); + + return n; + } + + public static void main(String[] args) { + expect(staticMethod(new Foo(), 0) == Limit); + } + + private interface Interface { + public int interfaceMethod(int n); + } + + private static class Foo implements Interface { + public int interfaceMethod(int n) { + if (n < Limit) { + return virtualMethod(n + 1, 1, 2, 3, 4, 5); + } else { + return leafMethod(n); + } + } + + public int virtualMethod(int n, int a, int b, int c, int d, int e) { + if (n < Limit) { + return staticMethod(this, n + 1); + } else { + return leafMethod(n); + } + } + } +} diff --git a/sgx-jvm/avian/test/jni.cpp b/sgx-jvm/avian/test/jni.cpp new file mode 100644 index 0000000000..18e418ab92 --- /dev/null +++ b/sgx-jvm/avian/test/jni.cpp @@ -0,0 +1,228 @@ +#include +#include "jni-util.h" + +extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) +{ + JNIEnv* e; + if (vm->GetEnv(reinterpret_cast(&e), JNI_VERSION_1_6) != JNI_OK) { + return -1; + } + + jclass c = e->FindClass("JNI"); + if (c == 0) { + return -1; + } + + e->SetStaticBooleanField( + c, e->GetStaticFieldID(c, "onLoadCalled", "Z"), true); + + return JNI_VERSION_1_6; +} + +extern "C" JNIEXPORT jdouble JNICALL Java_JNI_addDoubles(JNIEnv*, + jclass, + jdouble a1, + jdouble a2, + jdouble a3, + jdouble a4, + jdouble a5, + jdouble a6, + jdouble a7, + jdouble a8, + jdouble a9, + jdouble a10, + jdouble a11, + jdouble a12, + jdouble a13, + jdouble a14, + jdouble a15, + jdouble a16, + jdouble a17, + jdouble a18, + jdouble a19, + jdouble a20) +{ + return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12 + a13 + + a14 + a15 + a16 + a17 + a18 + a19 + a20; +} + +extern "C" JNIEXPORT jfloat JNICALL Java_JNI_addFloats(JNIEnv*, + jclass, + jfloat a1, + jfloat a2, + jfloat a3, + jfloat a4, + jfloat a5, + jfloat a6, + jfloat a7, + jfloat a8, + jfloat a9, + jfloat a10, + jfloat a11, + jfloat a12, + jfloat a13, + jfloat a14, + jfloat a15, + jfloat a16, + jfloat a17, + jfloat a18, + jfloat a19, + jfloat a20) +{ + return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12 + a13 + + a14 + a15 + a16 + a17 + a18 + a19 + a20; +} + +extern "C" JNIEXPORT jdouble JNICALL Java_JNI_addMix(JNIEnv*, + jclass, + jfloat a1, + jdouble a2, + jfloat a3, + jdouble a4, + jfloat a5, + jfloat a6, + jfloat a7, + jfloat a8, + jfloat a9, + jfloat a10, + jfloat a11, + jfloat a12, + jfloat a13, + jfloat a14, + jfloat a15, + jdouble a16, + jfloat a17, + jfloat a18, + jfloat a19, + jfloat a20) +{ + return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12 + a13 + + a14 + a15 + a16 + a17 + a18 + a19 + a20; +} + +extern "C" JNIEXPORT jint JNICALL + Java_JNI_addStackBoundary2(JNIEnv*, jclass, jobject, jobject, jobject, + jint i1, jint i2) +{ + return i1 + i2; +} + +extern "C" JNIEXPORT jint JNICALL + Java_JNI_addStackBoundary3(JNIEnv*, jclass, jobject, jobject, jobject, + jint i1, jint i2, jint i3) +{ + return i1 + i2 + i3; +} + +extern "C" JNIEXPORT jint JNICALL + Java_JNI_addStackBoundary4(JNIEnv*, jclass, jobject, jobject, jobject, + jint i1, jint i2, jint i3, jint i4) +{ + return i1 + i2 + i3 + i4; +} + +extern "C" JNIEXPORT jint JNICALL + Java_JNI_addStackBoundary5(JNIEnv*, jclass, jobject, jobject, jobject, + jint i1, jint i2, jint i3, jint i4, jint i5) +{ + return i1 + i2 + i3 + i4 + i5; +} + +extern "C" JNIEXPORT jint JNICALL + Java_JNI_addStackBoundary6(JNIEnv*, jclass, jobject, jobject, jobject, + jint i1, jint i2, jint i3, jint i4, jint i5, jint i6) +{ + return i1 + i2 + i3 + i4 + i5 + i6; +} + +extern "C" JNIEXPORT jfloat JNICALL + Java_JNI_doEcho__F(JNIEnv* e, jclass c, jfloat f) +{ + jvalue value; + value.f = f; + jvalue array[] = {value}; + return e->CallStaticFloatMethodA( + c, e->GetStaticMethodID(c, "echo", "(F)F"), array); +} + +extern "C" JNIEXPORT jdouble JNICALL + Java_JNI_doEcho__D(JNIEnv* e, jclass c, jdouble f) +{ + jvalue value; + value.d = f; + jvalue array[] = {value}; + return e->CallStaticDoubleMethodA( + c, e->GetStaticMethodID(c, "echo", "(D)D"), array); +} + +extern "C" JNIEXPORT jlong JNICALL + Java_JNI_fromReflectedMethod(JNIEnv* e, jclass, jobject method) +{ + return reinterpret_cast(e->FromReflectedMethod(method)); +} + +extern "C" JNIEXPORT jobject JNICALL + Java_JNI_toReflectedMethod(JNIEnv* e, + jclass, + jclass c, + jlong id, + jboolean isStatic) +{ + return e->ToReflectedMethod(c, reinterpret_cast(id), isStatic); +} + +extern "C" JNIEXPORT jint JNICALL + Java_JNI_callStaticIntMethod(JNIEnv* e, jclass, jclass c, jlong id) +{ + return e->CallStaticIntMethod(c, reinterpret_cast(id)); +} + +extern "C" JNIEXPORT jobject JNICALL + Java_JNI_newObject(JNIEnv* e, jclass, jclass c, jlong id) +{ + return e->NewObject(c, reinterpret_cast(id)); +} + +extern "C" JNIEXPORT jlong JNICALL + Java_JNI_fromReflectedField(JNIEnv* e, jclass, jobject field) +{ + return reinterpret_cast(e->FromReflectedField(field)); +} + +extern "C" JNIEXPORT jobject JNICALL + Java_JNI_toReflectedField(JNIEnv* e, + jclass, + jclass c, + jlong id, + jboolean isStatic) +{ + return e->ToReflectedField(c, reinterpret_cast(id), isStatic); +} + +extern "C" JNIEXPORT jint JNICALL + Java_JNI_getStaticIntField(JNIEnv* e, jclass, jclass c, jlong id) +{ + return e->GetStaticIntField(c, reinterpret_cast(id)); +} + +extern "C" JNIEXPORT jobject JNICALL + Java_JNI_testLocalRef(JNIEnv* e, jclass, jobject o) +{ + return e->NewLocalRef(o); +} + +extern "C" JNIEXPORT jobject JNICALL + Java_Buffers_allocateNative(JNIEnv* e, jclass, jint capacity) +{ + void* p = allocate(e, capacity); + if (p == 0) + return 0; + + return e->NewDirectByteBuffer(p, capacity); +} + +extern "C" JNIEXPORT void JNICALL + Java_Buffers_freeNative(JNIEnv* e, jclass, jobject b) +{ + free(e->GetDirectBufferAddress(b)); +} diff --git a/sgx-jvm/avian/test/test.sh b/sgx-jvm/avian/test/test.sh new file mode 100644 index 0000000000..0bd3db7d68 --- /dev/null +++ b/sgx-jvm/avian/test/test.sh @@ -0,0 +1,60 @@ +#!/bin/sh + +vg="nice valgrind --leak-check=full --num-callers=32 \ +--freelist-vol=100000000 --error-exitcode=1" + +ld_path=${1}; shift +unit_tester=${1}; shift +vm=${1}; shift +mode=${1}; shift +flags=${1}; shift +tests=${@} + +log=log.txt + +if [ -n "${ld_path}" ]; then + export ${ld_path} +fi + +echo -n "" >${log} + +printf "%20s------- Unit tests -------\n" "" +${unit_tester} 2>>${log} +if [ "${?}" != "0" ]; then + trouble=1 + echo "unit tests failed!" +fi + +echo + +printf "%20s------- Java tests -------\n" "" +for test in ${tests}; do + printf "%32s: " "${test}" + + case ${mode} in + debug|debug-fast|fast|small ) + ${vm} ${flags} ${test} >>${log} 2>&1;; + + stress* ) + ${vg} ${vm} ${flags} ${test} \ + >>${log} 2>&1;; + + * ) + echo "unknown mode: ${mode}" >&2 + exit 1;; + esac + + if [ "${?}" = "0" ]; then + echo "success" + else + echo "fail" + trouble=1 + fi +done + +echo + +if [ -n "${trouble}" ]; then + printf "see ${log} for output\n" + exit -1 +fi diff --git a/sgx-jvm/avian/unittest/CMakeLists.txt b/sgx-jvm/avian/unittest/CMakeLists.txt new file mode 100644 index 0000000000..d810e3ffba --- /dev/null +++ b/sgx-jvm/avian/unittest/CMakeLists.txt @@ -0,0 +1,23 @@ + +include_directories (${CMAKE_CURRENT_SOURCE_DIR}) + +add_executable (avian_unittest + test-harness.cpp + + codegen/assembler-test.cpp + codegen/registers-test.cpp + + util/arg-parser-test.cpp +) + +target_link_libraries (avian_unittest + avian_codegen + avian_codegen_x86 + avian_system + avian_heap + avian_util + ${PLATFORM_LIBS} +) + +add_test(NAME avian_unittest COMMAND avian_unittest) +add_dependencies(check avian_unittest) diff --git a/sgx-jvm/avian/unittest/codegen/assembler-test.cpp b/sgx-jvm/avian/unittest/codegen/assembler-test.cpp new file mode 100644 index 0000000000..614135949a --- /dev/null +++ b/sgx-jvm/avian/unittest/codegen/assembler-test.cpp @@ -0,0 +1,84 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include + +#include "avian/common.h" +#include +#include +#include "avian/target.h" + +#include +#include +#include +#include + +#include "test-harness.h" + +using namespace avian::codegen; +using namespace vm; + +class BasicEnv { + public: + System* s; + Heap* heap; + Architecture* arch; + + BasicEnv() + : s(makeSystem()), + heap(makeHeap(s, 32 * 1024)), + arch(makeArchitectureNative(s, true)) + { + arch->acquire(); + } + + ~BasicEnv() + { + arch->release(); + s->dispose(); + } +}; + +class Asm { + public: + Zone zone; + Assembler* a; + + Asm(BasicEnv& env) + : zone(env.heap, 8192), a(env.arch->makeAssembler(env.heap, &zone)) + { + } + + ~Asm() + { + a->dispose(); + } +}; + +TEST(BasicAssembler) +{ + BasicEnv env; + Asm a(env); +} + +TEST(ArchitecturePlan) +{ + BasicEnv env; + + for (int op = (int)lir::Call; op < (int)lir::AlignedJump; op++) { + bool thunk; + OperandMask mask; + env.arch->plan( + (lir::UnaryOperation)op, vm::TargetBytesPerWord, mask, &thunk); + assertFalse(thunk); + assertNotEqual(static_cast(0), mask.typeMask); + assertNotEqual(static_cast(0), (uint64_t)mask.lowRegisterMask); + } +} diff --git a/sgx-jvm/avian/unittest/codegen/registers-test.cpp b/sgx-jvm/avian/unittest/codegen/registers-test.cpp new file mode 100644 index 0000000000..4aa982c940 --- /dev/null +++ b/sgx-jvm/avian/unittest/codegen/registers-test.cpp @@ -0,0 +1,47 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include + +#include + +#include "test-harness.h" + +using namespace avian::codegen; +using namespace vm; + +TEST(RegisterIterator) +{ + BoundedRegisterMask regs(0x55); + assertEqual(0, regs.start); + assertEqual(7, regs.limit); + + for(int i = 0; i < 64; i++) { + assertEqual(i, BoundedRegisterMask(static_cast(1) << i).start); + assertEqual(i + 1, BoundedRegisterMask(static_cast(1) << i).limit); + } + + auto it = regs.begin(); + auto end = regs.end(); + + assertTrue(it != end); + assertEqual(6, (*it).index()); + ++it; + assertTrue(it != end); + assertEqual(4, (*it).index()); + ++it; + assertTrue(it != end); + assertEqual(2, (*it).index()); + ++it; + assertTrue(it != end); + assertEqual(0, (*it).index()); + ++it; + assertFalse(it != end); +} diff --git a/sgx-jvm/avian/unittest/test-harness.cpp b/sgx-jvm/avian/unittest/test-harness.cpp new file mode 100644 index 0000000000..2a8d4c2986 --- /dev/null +++ b/sgx-jvm/avian/unittest/test-harness.cpp @@ -0,0 +1,53 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include + +#include "test-harness.h" + +// since we aren't linking against libstdc++, we must implement this +// ourselves: +extern "C" void __cxa_pure_virtual(void) +{ + abort(); +} + +Test* Test::first = 0; +Test** Test::last = &first; + +Test::Test(const char* name) : next(0), failures(0), runs(0), name(name) +{ + *last = this; + last = &next; +} + +bool Test::runAll() +{ + int failures = 0; + for (Test* t = Test::first; t; t = t->next) { + printf("%32s: ", t->name); + t->run(); + failures += t->failures; + if (t->failures > 0) { + printf("failure\n"); + } else { + printf("success\n"); + } + } + return failures == 0; +} + +int main(int argc UNUSED, char** argv UNUSED) +{ + if (Test::runAll()) { + return 0; + } + return 1; +} diff --git a/sgx-jvm/avian/unittest/test-harness.h b/sgx-jvm/avian/unittest/test-harness.h new file mode 100644 index 0000000000..10b3f0259a --- /dev/null +++ b/sgx-jvm/avian/unittest/test-harness.h @@ -0,0 +1,119 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef TEST_HARNESS_H +#define TEST_HARNESS_H + +#include "avian/common.h" +#include + +class Test { + private: + Test* next; + static Test* first; + static Test** last; + + friend int main(int argc, char** argv); + + void print(uint64_t value) + { + fprintf(stderr, "%p", reinterpret_cast(value)); + } + + void print(uint32_t value) + { + fprintf(stderr, "%p", reinterpret_cast(value)); + } + + void print(uint8_t value) + { + print(static_cast(value)); + } + + void print(bool value) + { + fprintf(stderr, "%s", value ? "true" : "false"); + } + + int failures; + int runs; + + protected: + template + void assertEqual(T expected, T actual) + { + if (expected != actual) { + fprintf(stderr, "assertion failure, expected: "); + print(expected); + fprintf(stderr, ", actual: "); + print(actual); + fprintf(stderr, "\n"); + failures++; + } + runs++; + } + + void assertEqual(const char* expected, const char* actual) + { + if ((expected == 0 && actual != 0) || (expected != 0 && actual == 0) + || strcmp(expected, actual) != 0) { + fprintf(stderr, + "assertion failure, expected: \"%s\", actual: \"%s\"\n", + expected, + actual); + failures++; + } + runs++; + } + + template + void assertNotEqual(T expected, T actual) + { + if (expected == actual) { + fprintf(stderr, "assertion failure, expected: not "); + print(expected); + fprintf(stderr, ", actual: "); + print(actual); + fprintf(stderr, "\n"); + failures++; + } + runs++; + } + + void assertTrue(bool value) + { + assertEqual(true, value); + } + + void assertFalse(bool value) + { + assertEqual(false, value); + } + + public: + const char* const name; + Test(const char* name); + + virtual void run() = 0; + + static bool runAll(); +}; + +#define TEST(name) \ + class name##TestClass : public Test { \ + public: \ + name##TestClass() : Test(#name) \ + { \ + } \ + virtual void run(); \ + } name##TestInstance; \ + void name##TestClass::run() + +#endif // TEST_HARNESS_H diff --git a/sgx-jvm/avian/unittest/util/arg-parser-test.cpp b/sgx-jvm/avian/unittest/util/arg-parser-test.cpp new file mode 100644 index 0000000000..11fdb5e929 --- /dev/null +++ b/sgx-jvm/avian/unittest/util/arg-parser-test.cpp @@ -0,0 +1,49 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include + +#include "avian/common.h" + +#include + +#include "test-harness.h" + +using namespace avian::util; + +TEST(ArgParser) +{ + { + ArgParser parser; + Arg arg1(parser, false, "arg1", ""); + Arg required2(parser, true, "required2", ""); + const char* args[] + = {"myExecutable", "-arg1", "myValue1", "-required2", "myRequired2", 0}; + assertTrue(parser.parse(sizeof(args) / sizeof(char*) - 1, args)); + assertEqual("myValue1", arg1.value); + assertEqual("myRequired2", required2.value); + } + + { + ArgParser parser; + Arg arg1(parser, false, "arg1", ""); + Arg required2(parser, true, "required2", ""); + const char* args[] = {"myExecutable", "-arg1", "myValue1", "-required2", 0}; + assertFalse(parser.parse(sizeof(args) / sizeof(char*) - 1, args)); + } + + { + ArgParser parser; + Arg arg1(parser, false, "arg1", ""); + Arg required2(parser, true, "required2", ""); + const char* args[] = {"myExecutable", "-arg1", "myValue1", 0}; + assertFalse(parser.parse(sizeof(args) / sizeof(char*) - 1, args)); + } +} diff --git a/sgx-jvm/avian/valgrind.supp b/sgx-jvm/avian/valgrind.supp new file mode 100644 index 0000000000..89e9ef784b --- /dev/null +++ b/sgx-jvm/avian/valgrind.supp @@ -0,0 +1,50 @@ + +{ + + Memcheck:Cond + obj:/lib/ld-2.3.6.so +} + +{ + + Memcheck:Cond + obj:/lib/ld-2.6.so +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.3.6.so +} + +{ + + Memcheck:Addr8 + obj:/lib/ld-2.3.6.so +} + +# { +# +# Memcheck:Param +# write(buf) +# obj:* +# } + +# { +# +# Memcheck:Param +# writev(vector[...]) +# obj:* +# } + +# { +# +# Memcheck:Cond +# obj:* +# } + +# { +# +# Memcheck:Value8 +# obj:* +# } diff --git a/sgx-jvm/avian/vm.pro b/sgx-jvm/avian/vm.pro new file mode 100644 index 0000000000..ebc6f7c7e0 --- /dev/null +++ b/sgx-jvm/avian/vm.pro @@ -0,0 +1,135 @@ +# proguard include file (http://proguard.sourceforge.net) + +# we call the values method reflectively in Enum.valueOf(): + +-keepclassmembers public class * extends java.lang.Enum { + public static *** values(); + } + +# the VM depends on the fixed layout of the following classes: + +-keepclassmembers class java.lang.Class { !static ; } +-keepclassmembers class java.lang.ClassLoader { !static ; } +-keepclassmembers class java.lang.String { !static ; } +-keepclassmembers class java.lang.Thread { !static ; } +-keepclassmembers class java.lang.ThreadGroup { !static ; } +-keepclassmembers class java.lang.StackTraceElement { !static ; } +-keepclassmembers class java.lang.Throwable { !static ; } +-keepclassmembers class java.lang.Byte { !static ; } +-keepclassmembers class java.lang.Boolean { !static ; } +-keepclassmembers class java.lang.Short { !static ; } +-keepclassmembers class java.lang.Character { !static ; } +-keepclassmembers class java.lang.Integer { !static ; } +-keepclassmembers class java.lang.Long { !static ; } +-keepclassmembers class java.lang.Float { !static ; } +-keepclassmembers class java.lang.Double { !static ; } +-keepclassmembers class java.lang.ref.Reference { !static ; } +-keepclassmembers class java.lang.ref.ReferenceQueue { !static ; } +-keepclassmembers class java.lang.ref.WeakReference { !static ; } +-keepclassmembers class java.lang.ref.PhantomReference { !static ; } +-keepclassmembers class java.lang.reflect.Field { !static ; } +-keepclassmembers class java.lang.reflect.Method { !static ; } +-keepclassmembers class java.lang.reflect.Constructor { !static ; } +-keepclassmembers class java.lang.reflect.AccessibleObject { !static ; } +-keepclassmembers class sun.reflect.ConstantPool { !static ; } +-keepclassmembers class avian.VMClass { !static ; } +-keepclassmembers class avian.VMMethod { !static ; } +-keepclassmembers class avian.VMField { !static ; } +-keepclassmembers class avian.ClassAddendum { !static ; } +-keepclassmembers class avian.MethodAddendum { !static ; } +-keepclassmembers class avian.FieldAddendum { !static ; } +-keepclassmembers class avian.Continuations$Continuation { !static ; } +-keepclassmembers class avian.Continuations$UnwindResult { !static ; } + +# the VM may throw instances of the following: + +-keep public class avian.IncompatibleContinuationException +-keep public class java.lang.Exception +-keep public class java.lang.RuntimeException +-keep public class java.lang.IllegalStateException +-keep public class java.lang.IllegalArgumentException +-keep public class java.lang.IllegalMonitorStateException +-keep public class java.lang.IllegalThreadStateException +-keep public class java.lang.IndexOutOfBoundsException +-keep public class java.lang.ArrayIndexOutOfBoundsException +-keep public class java.lang.ArrayStoreException +-keep public class java.lang.NegativeArraySizeException +-keep public class java.lang.CloneNotSupportedException +-keep public class java.lang.ClassCastException +-keep public class java.lang.ClassNotFoundException +-keep public class java.lang.NullPointerException +-keep public class java.lang.ArithmeticException +-keep public class java.lang.InterruptedException +-keep public class java.lang.StackOverflowError +-keep public class java.lang.NoSuchFieldError +-keep public class java.lang.NoSuchMethodError +-keep public class java.lang.AbstractMethodError +-keep public class java.lang.UnsatisfiedLinkError +-keep public class java.lang.ExceptionInInitializerError +-keep public class java.lang.OutOfMemoryError +-keep public class java.lang.IncompatibleClassChangeError +-keep public class java.lang.reflect.InvocationTargetException +-keep public class java.io.IOException +-keep public class java.io.FileNotFoundException +-keep public class java.net.SocketException +-keep public class java.net.UnknownHostException +-keep public class java.util.Locale + +# ClassLoader.getSystemClassloader() depends on the existence of this class: + +-keep class avian.SystemClassLoader + +# the VM references these classes by name, so protect them from obfuscation: + +-keepnames public class java.lang.** +-keepnames public class avian.** + +# Don't optimize calls to ResourceBundle +-keep,allowshrinking,allowobfuscation public class java.util.ResourceBundle { + public static java.util.ResourceBundle getBundle(...); +} + +# musn't obfuscate native method names: + +-keepclasseswithmembernames class * { + native ; + } + +# Thread.run is called by name in the VM + +-keepclassmembers class java.lang.Thread { + private static void run(java.lang.Thread); + public void run(); + } + +# when continuations are enabled, the VM may call these methods by name: + +-keepclassmembers class avian.Continuations { + *** wind(...); + *** rewind(...); + } + +-keepclassmembernames class avian.CallbackReceiver { + *** receive(...); + } + +# the above methods include these classes in their signatures: + +-keepnames public class avian.Callback +-keepnames public class java.util.concurrent.Callable + +# Proguard gets confused about clone() and array classes (http://sourceforge.net/tracker/index.php?func=detail&aid=2851344&group_id=54750&atid=474704): + +-keepclassmembers class java.lang.Object { + protected java.lang.Object clone(); + } + +# called by name in the VM: + +-keepclassmembers class java.lang.ClassLoader { + public java.lang.Class loadClass(java.lang.String); + } + + -keepclassmembers class avian.Classes { + public java.security.ProtectionDomain getProtectionDomain(avian.VMClass); + }