From c855224d141799959540a8d9e3f36d9269ddff1a Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Mon, 17 Jan 2011 09:36:03 -0700 Subject: [PATCH 01/14] fix VM abort when ClassLoader.defineClass is used in bootimage build When loading a class which extends another class that contained a field of primitive array type using defineClass in a bootimage=true build, the VM was unable to find the primitive array class, and makeArrayClass refused to create one since it should already have existed. The problem was that the bootimage=true build uses an empty Machine::BootstrapClassMap, and resolveArrayClass expected to find the primitive array classes there. The fix is to check the Machine::BootLoader map if we can't find it in Machine::BootstrapClassMap. --- src/machine.cpp | 11 ++++++++++- test/DefineClass.java | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/machine.cpp b/src/machine.cpp index 1fade5edbe..14ad1632c7 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -1835,7 +1835,16 @@ resolveArrayClass(Thread* t, object loader, object spec, bool throw_) return c; } else { - return makeArrayClass(t, loader, spec, throw_); + PROTECT(t, loader); + PROTECT(t, spec); + + c = findLoadedClass(t, root(t, Machine::BootLoader), spec); + + if (c) { + return c; + } else { + return makeArrayClass(t, loader, spec, throw_); + } } } diff --git a/test/DefineClass.java b/test/DefineClass.java index 0b72cd5b95..ad98028b91 100644 --- a/test/DefineClass.java +++ b/test/DefineClass.java @@ -72,6 +72,7 @@ public class DefineClass { public abstract static class Base { public int foo; + public int[] array; public void bar() { } From 4a9ff5506017e99029f25c114f30b22b07a19737 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Mon, 17 Jan 2011 09:48:34 -0700 Subject: [PATCH 02/14] File.length should return 0 for non-existent files --- classpath/java-io.cpp | 2 +- test/FileOutput.java | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/classpath/java-io.cpp b/classpath/java-io.cpp index 740d5199c6..3c4ec00544 100644 --- a/classpath/java-io.cpp +++ b/classpath/java-io.cpp @@ -331,7 +331,7 @@ Java_java_io_File_length(JNIEnv* e, jclass, jstring path) } } - return -1; + return 0; } extern "C" JNIEXPORT void JNICALL diff --git a/test/FileOutput.java b/test/FileOutput.java index fa2cc0965e..6fffc0f8a7 100644 --- a/test/FileOutput.java +++ b/test/FileOutput.java @@ -4,6 +4,10 @@ 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); @@ -33,6 +37,8 @@ public class FileOutput { } public static void main(String[] args) throws IOException { + expect(new File("nonexistent-file").length() == 0); + test(false); test(true); } From b4941cddbea9cca8e7763d04ec06fb7a46456834 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Mon, 17 Jan 2011 10:33:43 -0700 Subject: [PATCH 03/14] close FileInputStream before deleting file in FileOutput test Otherwise, Windows won't let us delete it. --- test/FileOutput.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/FileOutput.java b/test/FileOutput.java index 6fffc0f8a7..539b73fc59 100644 --- a/test/FileOutput.java +++ b/test/FileOutput.java @@ -25,6 +25,7 @@ public class FileOutput { 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))) @@ -32,7 +33,7 @@ public class FileOutput { throw new RuntimeException(); } } finally { - new File("test.txt").delete(); + expect(new File("test.txt").delete()); } } From 475b286c7f6c80078b48a403036e35f4669b8fa7 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Mon, 17 Jan 2011 20:23:01 -0700 Subject: [PATCH 04/14] preserve ClassLoader.loadClass from obfuscation/shrinking This method is called by name in the VM. --- vm.pro | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vm.pro b/vm.pro index edfd2e1679..1dccb36b6a 100644 --- a/vm.pro +++ b/vm.pro @@ -116,3 +116,9 @@ -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); + } From c02bfc57a5c853af88a71eb6e4ac691f824092cc Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Tue, 18 Jan 2011 08:35:52 -0700 Subject: [PATCH 05/14] resolve primitive array classes when generating boot image This is necessary to accomodate classes loaded at runtime which refer to primitive array types. Otherwise, they won't be included unless classes in the bootimage refer to them. --- src/bootimage.cpp | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/bootimage.cpp b/src/bootimage.cpp index 252028dd79..13907b6b01 100644 --- a/src/bootimage.cpp +++ b/src/bootimage.cpp @@ -411,6 +411,41 @@ writeBootImage(Thread* t, FILE* out, BootImage* image, uint8_t* code, set(t, type(t, Machine::JdoubleType), ClassName, name); } + // resolve primitive array classes in case they are needed at + // runtime: + { resolveSystemClass + (t, root(t, Machine::BootLoader), makeByteArray(t, "[B"), true); + if (t->exception) return; + + resolveSystemClass + (t, root(t, Machine::BootLoader), makeByteArray(t, "[Z"), true); + if (t->exception) return; + + resolveSystemClass + (t, root(t, Machine::BootLoader), makeByteArray(t, "[S"), true); + if (t->exception) return; + + resolveSystemClass + (t, root(t, Machine::BootLoader), makeByteArray(t, "[C"), true); + if (t->exception) return; + + resolveSystemClass + (t, root(t, Machine::BootLoader), makeByteArray(t, "[I"), true); + if (t->exception) return; + + resolveSystemClass + (t, root(t, Machine::BootLoader), makeByteArray(t, "[J"), true); + if (t->exception) return; + + resolveSystemClass + (t, root(t, Machine::BootLoader), makeByteArray(t, "[F"), true); + if (t->exception) return; + + resolveSystemClass + (t, root(t, Machine::BootLoader), makeByteArray(t, "[D"), true); + if (t->exception) return; + } + collect(t, Heap::MajorCollection); uintptr_t* heap = static_cast From 9c36105b8fa06543897acd8d86de0737149d519a Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Wed, 19 Jan 2011 16:00:18 -0700 Subject: [PATCH 06/14] fix misleading pathnames in bootimage build instructions --- readme.txt | 6 +++--- test/DefineClass.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/readme.txt b/readme.txt index f8b7a51e06..32209046f7 100644 --- a/readme.txt +++ b/readme.txt @@ -462,7 +462,7 @@ Step 2: Create a stage1 directory and extract the contents of the class library jar into it. $ mkdir stage1 - $ (cd stage1 && jar xf ../../build/${platform}-${arch}/classpath.jar) + $ (cd stage1 && jar xf ../../build/linux-i386-bootimage/classpath.jar) Step 3: Build the Java code and add it to stage1. @@ -498,10 +498,10 @@ Step 6: Build the boot image. Step 7: Make an object file out of the boot image. - $ ../build/${platform}-${arch}/binaryToObject \ + $ ../build/linux-i386-bootimage/binaryToObject \ bootimage.bin bootimage-bin.o \ _binary_bootimage_bin_start _binary_bootimage_bin_end \ - ${platform} ${arch} 8 writable executable + linux i386 8 writable executable Step 8: Write a driver which starts the VM and runs the desired main method. Note the bootimageBin function, which will be called by the diff --git a/test/DefineClass.java b/test/DefineClass.java index ad98028b91..4e0d8faef7 100644 --- a/test/DefineClass.java +++ b/test/DefineClass.java @@ -50,7 +50,7 @@ public class DefineClass { } public static void main(String[] args) throws Exception { - testStatic(); + //testStatic(); testDerived(); } From 51c56c1b3c901354a1ae8fad290b00a7436f41c5 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Thu, 20 Jan 2011 08:26:56 -0700 Subject: [PATCH 07/14] add PrintStream.print[ln] overloads for primitives and char arrays --- classpath/java/io/PrintStream.java | 48 ++++++++++++++++++++++++++++++ test/Misc.java | 8 +++++ 2 files changed, 56 insertions(+) diff --git a/classpath/java/io/PrintStream.java b/classpath/java/io/PrintStream.java index 520b676a29..a720fac6a0 100644 --- a/classpath/java/io/PrintStream.java +++ b/classpath/java/io/PrintStream.java @@ -39,10 +39,34 @@ public class PrintStream extends OutputStream { 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 void println(String s) { try { out.write(s.getBytes()); @@ -62,9 +86,33 @@ public class PrintStream extends OutputStream { 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); diff --git a/test/Misc.java b/test/Misc.java index 8a501392c7..7ff2f25378 100644 --- a/test/Misc.java +++ b/test/Misc.java @@ -227,5 +227,13 @@ public class Misc { } 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' }); } } From 2dcc1d525aa1d7dbce05d1322cfc2736bda1198f Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Thu, 20 Jan 2011 08:50:53 -0700 Subject: [PATCH 08/14] add note about slower floating point code for 32-bit x86 bootimage build --- readme.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/readme.txt b/readme.txt index 32209046f7..6bfc750124 100644 --- a/readme.txt +++ b/readme.txt @@ -442,7 +442,10 @@ For boot image builds: 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. + 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. From b57e734ceb051e45c373f7681c437f796d777a41 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Thu, 20 Jan 2011 09:33:50 -0700 Subject: [PATCH 09/14] implement Character.forDigit --- classpath/java/lang/Character.java | 9 +++++++++ test/Strings.java | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/classpath/java/lang/Character.java b/classpath/java/lang/Character.java index f696037561..88025280e8 100644 --- a/classpath/java/lang/Character.java +++ b/classpath/java/lang/Character.java @@ -109,6 +109,15 @@ public final class Character implements Comparable { } } + 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); } diff --git a/test/Strings.java b/test/Strings.java index 6b993bcb4f..d98c1f13f9 100644 --- a/test/Strings.java +++ b/test/Strings.java @@ -71,5 +71,11 @@ public class Strings { 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'); } } From 3a2a46499eb4af32052d75a6b33e3ef9660ae3a9 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Thu, 20 Jan 2011 09:34:46 -0700 Subject: [PATCH 10/14] add Reader.mark/markSupported/reset --- classpath/java/io/Reader.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/classpath/java/io/Reader.java b/classpath/java/io/Reader.java index acbf42ffd2..a09c8a24e9 100644 --- a/classpath/java/io/Reader.java +++ b/classpath/java/io/Reader.java @@ -28,5 +28,17 @@ public abstract class Reader { 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; } From 0ca7ed8c67421fb8b45ed86ae6c2d47f3a8b498a Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Thu, 20 Jan 2011 09:35:34 -0700 Subject: [PATCH 11/14] add Package.getName --- classpath/java/lang/Package.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/classpath/java/lang/Package.java b/classpath/java/lang/Package.java index 507a240a3e..5cd9b9547e 100644 --- a/classpath/java/lang/Package.java +++ b/classpath/java/lang/Package.java @@ -43,4 +43,8 @@ public class Package { this.sealed = sealed; this.loader = loader; } + + public String getName() { + return name; + } } From 3aff7dc861de624dde48bbec953f79b64b5039f0 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Thu, 20 Jan 2011 09:39:16 -0700 Subject: [PATCH 12/14] add TreeSet(Collection) constructor --- classpath/java/util/TreeSet.java | 8 ++++++++ test/Tree.java | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/classpath/java/util/TreeSet.java b/classpath/java/util/TreeSet.java index 5a1d4a8c47..16f291a134 100644 --- a/classpath/java/util/TreeSet.java +++ b/classpath/java/util/TreeSet.java @@ -33,6 +33,14 @@ public class TreeSet extends AbstractSet implements Collection { } }); } + + public TreeSet(Collection collection) { + this(); + + for (T item: collection) { + add(item); + } + } public Iterator iterator() { return new MyIterator(set.first()); diff --git a/test/Tree.java b/test/Tree.java index ded42a27ea..12f1e8fa08 100644 --- a/test/Tree.java +++ b/test/Tree.java @@ -1,6 +1,8 @@ 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; @@ -87,5 +89,13 @@ public class Tree { 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"); } } From 0cc615693258dd688abd5110512de2d551c66373 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 21 Jan 2011 16:11:36 -0700 Subject: [PATCH 13/14] remove redundant code from DefineClass and Zip tests --- test/DefineClass.java | 1 - test/Zip.java | 1 - 2 files changed, 2 deletions(-) diff --git a/test/DefineClass.java b/test/DefineClass.java index 4e0d8faef7..ea3b6362ae 100644 --- a/test/DefineClass.java +++ b/test/DefineClass.java @@ -4,7 +4,6 @@ import java.io.FileInputStream; public class DefineClass { private static File findClass(String name, File directory) { - File[] files = directory.listFiles(); for (File file: directory.listFiles()) { if (file.isFile()) { if (file.getName().equals(name + ".class")) { diff --git a/test/Zip.java b/test/Zip.java index 8862e767c7..9b34444023 100644 --- a/test/Zip.java +++ b/test/Zip.java @@ -7,7 +7,6 @@ import java.util.zip.ZipEntry; public class Zip { private static String findJar(File directory) { - File[] files = directory.listFiles(); for (File file: directory.listFiles()) { if (file.isFile()) { if (file.getName().endsWith(".jar")) { From 220f7760b7c41ed88c7960fc70eedd777fb3501e Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 21 Jan 2011 16:14:21 -0700 Subject: [PATCH 14/14] fix MSVC build regressions --- classpath/java-io.cpp | 6 ++++-- makefile | 3 ++- readme.txt | 2 +- src/finder.cpp | 2 +- src/main.cpp | 8 +++++++- src/process.cpp | 4 ++-- 6 files changed, 17 insertions(+), 8 deletions(-) diff --git a/classpath/java-io.cpp b/classpath/java-io.cpp index 3c4ec00544..abc7684634 100644 --- a/classpath/java-io.cpp +++ b/classpath/java-io.cpp @@ -37,10 +37,12 @@ # define OPEN_MASK O_BINARY # ifdef _MSC_VER -# define S_ISREG(x) ((x) | _S_IFREG) -# define S_ISDIR(x) ((x) | _S_IFDIR) +# 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 # define CREAT _wcreat diff --git a/makefile b/makefile index ef5fb8b04e..5b34b5051b 100644 --- a/makefile +++ b/makefile @@ -353,7 +353,8 @@ ifdef msvc ld = "$(msvc)/BIN/link.exe" mt = "mt.exe" cflags = -nologo -DAVIAN_VERSION=\"$(version)\" -D_JNI_IMPLEMENTATION_ \ - -DUSE_ATOMIC_OPERATIONS \ + -DUSE_ATOMIC_OPERATIONS -DAVIAN_JAVA_HOME=\"$(javahome)\" \ + -DAVIAN_EMBED_PREFIX=\"$(embed-prefix)\" \ -Fd$(build)/$(name).pdb -I"$(zlib)/include" -I$(src) -I"$(build)" \ -I"$(windows-java-home)/include" -I"$(windows-java-home)/include/win32" shared = -dll diff --git a/readme.txt b/readme.txt index 6bfc750124..0f0ad44bb7 100644 --- a/readme.txt +++ b/readme.txt @@ -187,7 +187,7 @@ C++ portions of the VM, while the assembly code and helper tools are built using GCC. The MSVC build has been tested with Visual Studio Express Edition -versions 8 and 9. Other versions may also work. +versions 8, 9, and 10. Other versions may also work. To build with MSVC, install Cygwin as described above and set the following environment variables: diff --git a/src/finder.cpp b/src/finder.cpp index 543c45e312..b0e25ef82a 100644 --- a/src/finder.cpp +++ b/src/finder.cpp @@ -458,7 +458,7 @@ class JarIndex { RUNTIME_ARRAY_BODY(n)[length] = '/'; RUNTIME_ARRAY_BODY(n)[length + 1] = 0; - node = findNode(n); + node = findNode(RUNTIME_ARRAY_BODY(n)); if (node) { return System::TypeDirectory; } else { diff --git a/src/main.cpp b/src/main.cpp index 8cdb435dd0..9996e6078d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -62,7 +62,13 @@ 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(); } +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 diff --git a/src/process.cpp b/src/process.cpp index 00edbe66b7..4c06794461 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -206,12 +206,12 @@ resolveNativeMethod(Thread* t, object method, const char* prefix, object resolveNativeMethod(Thread* t, object method) { - void* p = ::resolveNativeMethod(t, method, "Avian_", 6, 3); + void* p = resolveNativeMethod(t, method, "Avian_", 6, 3); if (p) { return makeNative(t, p, true); } - p = ::resolveNativeMethod(t, method, "Java_", 5, -1); + p = resolveNativeMethod(t, method, "Java_", 5, -1); if (p) { return makeNative(t, p, false); }