Skip to content

Commit 85f5d24

Browse files
authored
Support for JDK 11 (#8)
1 parent 5574c8f commit 85f5d24

File tree

9 files changed

+114
-119
lines changed

9 files changed

+114
-119
lines changed

build.gradle

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,18 @@ minimallyCorrectDefaults {
2222
dependencies {
2323
testCompile "junit:junit:4.12"
2424
//Can we make these dependencies optional? Reduced jar size if not using source for patching
25-
compile 'org.ow2.asm:asm-debug-all:5.0.4'
25+
compile 'org.ow2.asm:asm:6.2.1'
26+
compile 'org.ow2.asm:asm-util:6.2.1'
27+
compile 'org.ow2.asm:asm-tree:6.2.1'
2628
compile 'com.github.javaparser:javaparser-symbol-solver-core:3.6.24'
2729
}
30+
31+
tasks.withType(JavaCompile) {
32+
sourceCompatibility = 8
33+
targetCompatibility = 8
34+
options.with {
35+
deprecation = true
36+
encoding = 'UTF-8'
37+
compilerArgs << "-Xlint:all" << "-Xlint:-path" << "-Xlint:-processing" << "-Xlint:-options"
38+
}
39+
}

src/main/java/org/minimallycorrect/javatransformer/internal/ClassPaths.java

Lines changed: 77 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import java.io.IOException;
66
import java.io.InputStream;
77
import java.lang.management.ManagementFactory;
8+
import java.net.URI;
9+
import java.nio.file.FileSystems;
810
import java.nio.file.FileVisitResult;
911
import java.nio.file.Files;
1012
import java.nio.file.Path;
@@ -14,8 +16,7 @@
1416
import java.util.*;
1517
import java.util.function.Supplier;
1618
import java.util.stream.Collectors;
17-
import java.util.zip.ZipEntry;
18-
import java.util.zip.ZipInputStream;
19+
import java.util.zip.ZipFile;
1920

2021
import javax.annotation.Nonnull;
2122
import javax.annotation.Nullable;
@@ -52,27 +53,77 @@ public static class SystemClassPath {
5253
public static final ClassPath SYSTEM_CLASS_PATH = makeSystemJarClassPath();
5354

5455
private static ClassPath makeSystemJarClassPath() {
55-
// TODO: handle java 9 JRT (jrt:// path)
56-
// Easy solution: delegate to system classloader
57-
5856
// only scan java/ files in boot class path
5957
// avoid JVM/JDK internals
60-
val paths = Splitter.pathSplitter.split(ManagementFactory.getRuntimeMXBean().getBootClassPath())
61-
.map(it -> Paths.get(it)).filter(it -> it.getFileName().toString().equals("rt.jar"))
62-
.collect(Collectors.toList());
63-
return new FileClassPath(null, paths);
58+
try {
59+
val paths = Splitter.pathSplitter.split(ManagementFactory.getRuntimeMXBean().getBootClassPath())
60+
.map(it -> Paths.get(it)).filter(it -> it.getFileName().toString().equals("rt.jar"))
61+
.collect(Collectors.toList());
62+
return new FileClassPath(null, paths);
63+
} catch (UnsupportedOperationException ignored) {
64+
val fs = FileSystems.getFileSystem(URI.create("jrt:/"));
65+
return new FileClassPath(null, Collections.singletonList(fs.getPath("modules/java.base/")));
66+
}
6467
}
6568
}
6669

67-
static class FileClassPath implements ClassPath, TypeSolver {
70+
private static abstract class ClassPathSolver implements ClassPath, TypeSolver {
6871
@Nullable
6972
final ClassPath parent;
73+
74+
ClassPathSolver(@Nullable ClassPath parent) {
75+
this.parent = parent;
76+
}
77+
78+
@Override
79+
public TypeSolver getParent() {
80+
return parent instanceof TypeSolver ? (TypeSolver) parent : null;
81+
}
82+
83+
@Override
84+
public void setParent(TypeSolver parent) {
85+
throw new UnsupportedOperationException("TODO");
86+
}
87+
88+
static Path normalise(Path path) {
89+
return path.toAbsolutePath().normalize();
90+
}
91+
92+
@NonNull
93+
@Override
94+
public SymbolReference<ResolvedReferenceTypeDeclaration> tryToSolveType(String name) {
95+
val ci = getClassInfo(name);
96+
if (ci == null) {
97+
return SymbolReference.unsolved(ResolvedReferenceTypeDeclaration.class);
98+
}
99+
return SymbolReference.solved(getResolvedReferenceTypeDeclarationForClassInfo(ci));
100+
}
101+
102+
private ResolvedReferenceTypeDeclaration getResolvedReferenceTypeDeclarationForClassInfo(ClassInfo ci) {
103+
if (ci instanceof SourceInfo) {
104+
val jpType = ((SourceInfo) ci).getJavaParserType();
105+
if (jpType.isClassOrInterfaceDeclaration()) {
106+
return new JavaParserClassDeclaration(jpType.asClassOrInterfaceDeclaration(), this);
107+
} else if (jpType.isEnumDeclaration()) {
108+
return new JavaParserEnumDeclaration(jpType.asEnumDeclaration(), this);
109+
} else if (jpType.isAnnotationDeclaration()) {
110+
return new JavaParserAnnotationDeclaration(jpType.asAnnotationDeclaration(), this);
111+
}
112+
}
113+
if (ci instanceof ByteCodeInfo) {
114+
return AsmResolvedTypes.fromByteCodeInfo(this, (ByteCodeInfo) ci);
115+
}
116+
throw new UnsupportedOperationException("TODO " + ci);
117+
}
118+
}
119+
120+
static class FileClassPath extends ClassPathSolver {
70121
private final Map<String, ClassInfo> entries = new HashMap<>();
71122
private final Collection<Path> paths;
72123
private boolean initialised;
73124

74125
public FileClassPath(@Nullable ClassPath parent, Collection<Path> paths) {
75-
this.parent = parent;
126+
super(parent);
76127
this.paths = paths;
77128
}
78129

@@ -90,10 +141,6 @@ public ClassInfo getClassInfo(@Nonnull String className) {
90141
return entries.get(className);
91142
}
92143

93-
private static Path normalise(Path path) {
94-
return path.toAbsolutePath().normalize();
95-
}
96-
97144
@Override
98145
public synchronized boolean addPath(Path path) {
99146
path = normalise(path);
@@ -131,11 +178,8 @@ private void findPaths(String entryName, Supplier<InputStream> iss) {
131178
}
132179

133180
if (entryName.endsWith(".class")) {
134-
try (val is = iss.get()) {
135-
String name = JVMUtil.fileNameToClassName(entryName);
136-
val classNode = AsmUtil.getClassNode(StreamUtil.readFully(is), null);
137-
entries.put(name, new ByteCodeInfo(() -> classNode, name, Collections.emptyMap()));
138-
}
181+
String name = JVMUtil.fileNameToClassName(entryName);
182+
entries.put(name, new ByteCodeInfo(() -> AsmUtil.getClassNode(StreamUtil.readFully(iss.get()), null), name, Collections.emptyMap()));
139183
}
140184
}
141185

@@ -176,67 +220,23 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
176220
}
177221
});
178222
else if (Files.isRegularFile(path))
179-
try (val zis = new ZipInputStream(Files.newInputStream(path))) {
180-
ZipEntry e;
181-
val is = new InputStream() {
182-
public int read(@NonNull byte[] b, int off, int len) throws IOException {
183-
return zis.read(b, off, len);
184-
}
185-
186-
public void close() throws IOException {
187-
// don't allow closing this ZIS
188-
}
189-
190-
public int read() throws IOException {
191-
return zis.read();
192-
}
193-
};
194-
while ((e = zis.getNextEntry()) != null) {
195-
try {
196-
findPaths(e.getName(), () -> is);
197-
} finally {
198-
zis.closeEntry();
199-
}
223+
try (val zf = new ZipFile(path.toFile())) {
224+
val e$ = zf.entries();
225+
while (e$.hasMoreElements()) {
226+
val ze = e$.nextElement();
227+
val name = ze.getName();
228+
findPaths(name, () -> {
229+
try {
230+
val zff = new ZipFile(path.toFile());
231+
return zff.getInputStream(zff.getEntry(name));
232+
} catch (IOException e) {
233+
throw new IOError(e);
234+
}
235+
});
200236
}
201237
}
202238
}
203239

204-
@Override
205-
public TypeSolver getParent() {
206-
return null;
207-
}
208-
209-
@Override
210-
public void setParent(TypeSolver parent) {
211-
throw new UnsupportedOperationException("TODO");
212-
}
213-
214-
@NonNull
215-
@Override
216-
public SymbolReference<ResolvedReferenceTypeDeclaration> tryToSolveType(String name) {
217-
val ci = getClassInfo(name);
218-
if (ci == null) {
219-
return SymbolReference.unsolved(ResolvedReferenceTypeDeclaration.class);
220-
}
221-
return SymbolReference.solved(getResolvedReferenceTypeDeclarationForClassInfo(ci));
222-
}
223-
224-
private ResolvedReferenceTypeDeclaration getResolvedReferenceTypeDeclarationForClassInfo(ClassInfo ci) {
225-
if (ci instanceof SourceInfo) {
226-
val jpType = ((SourceInfo) ci).getJavaParserType();
227-
if (jpType.isClassOrInterfaceDeclaration()) {
228-
return new JavaParserClassDeclaration(jpType.asClassOrInterfaceDeclaration(), this);
229-
} else if (jpType.isEnumDeclaration()) {
230-
return new JavaParserEnumDeclaration(jpType.asEnumDeclaration(), this);
231-
} else if (jpType.isAnnotationDeclaration()) {
232-
return new JavaParserAnnotationDeclaration(jpType.asAnnotationDeclaration(), this);
233-
}
234-
}
235-
if (ci instanceof ByteCodeInfo) {
236-
return AsmResolvedTypes.fromByteCodeInfo(this, (ByteCodeInfo) ci);
237-
}
238-
throw new UnsupportedOperationException("TODO " + ci);
239-
}
240240
}
241241

242242
/*

src/main/java/org/minimallycorrect/javatransformer/internal/asm/CombinedValue.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public String toString() {
3838
private static final CombinedValue DOUBLE_VALUE = new CombinedValue(Type.DOUBLE_TYPE, Collections.emptySet());
3939
private static final CombinedValue REFERENCE_VALUE = new CombinedValue(OBJECT_TYPE, Collections.emptySet());
4040
/**
41-
* The instructions that can produce this value. For example, for the Java code below, the instructions that can produce the value of <tt>i</tt> at line 5 are the txo ISTORE instructions at line 1 and 3:
41+
* The instructions that can produce this value. For example, for the Java code below, the instructions that can produce the value of <b>i</b> at line 5 are the txo ISTORE instructions at line 1 and 3:
4242
*
4343
* <pre>
4444
* 1: i = 0;

src/main/java/org/minimallycorrect/javatransformer/internal/asm/FilteringClassWriter.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ public int newNameType(final String name, final String desc) {
4747
}
4848

4949
@Override
50+
public int newHandle(int tag, String owner, String name, String descriptor, boolean isInterface) {
51+
return super.newHandle(tag, replace(owner), name, descriptor, isInterface);
52+
}
53+
54+
@Override
55+
@SuppressWarnings("deprecation")
5056
public int newHandle(int tag, String owner, String name, String desc) {
5157
return super.newHandle(tag, replace(owner), name, desc);
5258
}

src/main/java/org/minimallycorrect/javatransformer/internal/util/DefineClass.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,9 @@
1212

1313
@UtilityClass
1414
public class DefineClass {
15-
private static final sun.misc.Unsafe $ = UnsafeAccess.$;
1615
private static final MethodHandles.Lookup $L = UnsafeAccess.IMPL_LOOKUP;
1716
private static final ProtectionDomain PROTECTION_DOMAIN = AccessController.doPrivileged((PrivilegedAction<ProtectionDomain>) DefineClass.class::getProtectionDomain);
1817
private static final MethodHandle defineClassHandle = getDefineClassHandle();
19-
private static final boolean useUnsafe = $ != null && (defineClassHandle == null || System.getProperty("org.minimallycorrect.javatransformer.internal.util.DefineClass.useUnsafe", "false").equalsIgnoreCase("true"));
2018

2119
private static MethodHandle getDefineClassHandle() {
2220
try {
@@ -30,9 +28,6 @@ private static MethodHandle getDefineClassHandle() {
3028
@SneakyThrows
3129
@SuppressWarnings("unchecked")
3230
public static Class<?> defineClass(ClassLoader classLoader, String name, byte[] bytes) {
33-
if (useUnsafe) {
34-
return $.defineClass(name, bytes, 0, bytes.length, classLoader, PROTECTION_DOMAIN);
35-
}
3631
return (Class<?>) defineClassHandle.invokeExact(classLoader, name, bytes, 0, bytes.length, PROTECTION_DOMAIN);
3732
}
3833
}

src/main/java/org/minimallycorrect/javatransformer/internal/util/UnsafeAccess.java

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,20 @@
11
package org.minimallycorrect.javatransformer.internal.util;
22

33
import java.lang.invoke.MethodHandles;
4-
import java.lang.reflect.Field;
54

65
import lombok.val;
76

8-
import sun.misc.Unsafe;
9-
107
import com.github.javaparser.utils.Log;
118

129
public class UnsafeAccess {
13-
public static final Unsafe $;
1410
public static final MethodHandles.Lookup IMPL_LOOKUP;
1511

1612
static {
17-
Unsafe temp = null;
18-
try {
19-
Field theUnsafe = Class.forName("sun.misc.Unsafe").getDeclaredField("theUnsafe");
20-
theUnsafe.setAccessible(true);
21-
temp = (Unsafe) theUnsafe.get(null);
22-
} catch (Throwable t) {
23-
Log.error("Failed to get unsafe", t);
24-
}
25-
$ = temp;
26-
2713
MethodHandles.Lookup lookup = MethodHandles.lookup();
2814
try {
2915
val field = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
30-
if ($ != null) {
31-
lookup = (MethodHandles.Lookup) $.getObject($.staticFieldBase(field), $.staticFieldOffset(field));
32-
} else {
33-
field.setAccessible(true);
34-
lookup = (MethodHandles.Lookup) field.get(null);
35-
}
16+
field.setAccessible(true);
17+
lookup = (MethodHandles.Lookup) field.get(null);
3618
} catch (Throwable t) {
3719
Log.error("Failed to get MethodHandles.Lookup.IMPL_LOOKUP", t);
3820
}

src/test/java/org/minimallycorrect/javatransformer/api/JavaTransformerRuntimeTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88
import java.util.ArrayList;
99
import java.util.Arrays;
1010
import java.util.List;
11+
import java.util.concurrent.atomic.AtomicBoolean;
1112
import java.util.stream.Collectors;
1213

1314
import lombok.val;
1415

1516
import org.junit.Assert;
1617
import org.junit.Test;
17-
import org.omg.CORBA.BooleanHolder;
1818

1919
import org.minimallycorrect.javatransformer.api.code.CodeFragment;
2020
import org.minimallycorrect.javatransformer.internal.ByteCodeInfo;
@@ -32,11 +32,11 @@ public void testTransformRuntime() throws Exception {
3232
JavaTransformer transformer = new JavaTransformer();
3333

3434
val targetClass = this.getClass().getName();
35-
BooleanHolder holder = new BooleanHolder(false);
35+
AtomicBoolean check = new AtomicBoolean(false);
3636

3737
transformer.addTransformer(name, c -> {
3838
Assert.assertEquals(name, c.getName());
39-
holder.value = true;
39+
check.set(true);
4040
c.accessFlags(it -> it.makeAccessible(true));
4141
c.getAnnotations();
4242
val fields = c.getFields().collect(Collectors.toList());
@@ -95,7 +95,7 @@ public void testTransformRuntime() throws Exception {
9595
transformer.load(input);
9696
val clazz = transformer.defineClass(this.getClass().getClassLoader(), name);
9797
Assert.assertEquals(name, clazz.getName());
98-
Assert.assertTrue("Transformer must process " + targetClass, holder.value);
98+
Assert.assertTrue("Transformer must process " + targetClass, check.get());
9999

100100
val list = new ArrayList<String>();
101101
val codeFragmentTesting = new CodeFragmentTesting(list::add);

0 commit comments

Comments
 (0)