| /* |
| * Copyright (c) 2015, 2021, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| import java.io.File; |
| import java.io.InputStream; |
| import java.io.Writer; |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.lang.reflect.Method; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.io.IOException; |
| import java.nio.file.FileVisitResult; |
| import java.nio.file.FileVisitor; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.nio.file.attribute.BasicFileAttributes; |
| import java.util.Enumeration; |
| import java.util.HashSet; |
| import java.util.Set; |
| import java.util.jar.JarEntry; |
| import java.util.jar.JarFile; |
| import java.util.stream.Collectors; |
| import java.util.stream.Stream; |
| import toolbox.JavacTask; |
| import toolbox.Task; |
| import toolbox.Task.Expect; |
| import toolbox.ToolBox; |
| import build.tools.symbolgenerator.CreateSymbols; |
| import build.tools.symbolgenerator.CreateSymbols.ClassDescription; |
| import build.tools.symbolgenerator.CreateSymbols.ClassList; |
| import build.tools.symbolgenerator.CreateSymbols.ExcludeIncludeList; |
| import build.tools.symbolgenerator.CreateSymbols.VersionDescription; |
| |
| public class CreateSymbolsTestImpl { |
| |
| static final String CREATE_SYMBOLS_NAME = "symbolgenerator.CreateSymbols"; |
| |
| public static void main(String... args) throws Exception { |
| new CreateSymbolsTestImpl().doTest(); |
| } |
| |
| void doTest() throws Exception { |
| boolean testRun = false; |
| for (Method m : CreateSymbolsTestImpl.class.getDeclaredMethods()) { |
| if (m.isAnnotationPresent(Test.class)) { |
| m.invoke(this); |
| testRun = true; |
| } |
| } |
| if (!testRun) { |
| throw new IllegalStateException("No tests found."); |
| } |
| } |
| |
| @Test |
| void testMethodRemoved() throws Exception { |
| doTest("package t; public class T { public void m() { } }", |
| "package t; public class T { }", |
| "package t; public class Test { { T t = null; t.m(); } }", |
| Expect.SUCCESS, |
| Expect.FAIL); |
| doTest("package t; public class T { public void b() { } public void m() { } public void a() { } }", |
| "package t; public class T { public void b() { } public void a() { } }", |
| "package t; public class Test { { T t = null; t.b(); t.a(); } }", |
| Expect.SUCCESS, |
| Expect.SUCCESS); |
| //with additional attribute (need to properly skip the member): |
| doTest("package t; public class T { public void m() throws IllegalStateException { } public void a() { } }", |
| "package t; public class T { public void a() { } }", |
| "package t; public class Test { { T t = null; t.a(); } }", |
| Expect.SUCCESS, |
| Expect.SUCCESS); |
| } |
| |
| @Test |
| void testMethodAdded() throws Exception { |
| doTest("package t; public class T { }", |
| "package t; public class T { public void m() { } }", |
| "package t; public class Test { { T t = null; t.m(); } }", |
| Expect.FAIL, |
| Expect.SUCCESS); |
| doTest("package t; public class T { public void b() { } public void a() { } }", |
| "package t; public class T { public void b() { } public void m() { } public void a() { } }", |
| "package t; public class Test { { T t = null; t.b(); t.a(); } }", |
| Expect.SUCCESS, |
| Expect.SUCCESS); |
| } |
| |
| //verify fields added/modified/removed |
| |
| @Test |
| void testClassAdded() throws Exception { |
| doTest("class Dummy {}", |
| "package t; public class T { }", |
| "package t; public class Test { { T t = new T(); } }", |
| Expect.FAIL, |
| Expect.SUCCESS); |
| } |
| |
| @Test |
| void testClassModified() throws Exception { |
| doTest("package t; public class T { public void m() { } }", |
| "package t; public class T implements java.io.Serializable { public void m() { } }", |
| "package t; public class Test { { java.io.Serializable t = new T(); } }", |
| Expect.FAIL, |
| Expect.SUCCESS); |
| } |
| |
| @Test |
| void testClassRemoved() throws Exception { |
| doTest("package t; public class T { }", |
| "class Dummy {}", |
| "package t; public class Test { { T t = new T(); } }", |
| Expect.SUCCESS, |
| Expect.FAIL); |
| } |
| |
| @Test |
| void testInnerClassAttributes() throws Exception { |
| doTest("package t; public class T { public static class Inner { } }", |
| "package t; public class T { public static class Inner { } public void extra() {} }", |
| "package t; import t.T.Inner; public class Test { Inner i; }", |
| Expect.SUCCESS, |
| Expect.SUCCESS); |
| } |
| |
| @Test |
| void testConstantAdded() throws Exception { |
| doTest("package t; public class T { }", |
| "package t; public class T { public static final int A = 0; }", |
| "package t; public class Test { void t(int i) { switch (i) { case T.A: break;} } }", |
| Expect.FAIL, |
| Expect.SUCCESS); |
| } |
| |
| @Test |
| void testAnnotationAttributeDefaultvalue() throws Exception { |
| //TODO: this only verifies that there is *some* value, but we should also verify there is a specific value: |
| doTest("package t; public @interface T { }", |
| "package t;\n" + |
| "public @interface T {\n" + |
| " public boolean booleanValue() default true;\n" + |
| " public byte byteValue() default 1;\n" + |
| " public char charValue() default 2;\n" + |
| " public short shortValue() default 3;\n" + |
| " public int intValue() default 4;\n" + |
| " public long longValue() default 5;\n" + |
| " public float floatValue() default 6;\n" + |
| " public double doubleValue() default 7;\n" + |
| " public String stringValue() default \"8\";\n" + |
| " public java.lang.annotation.RetentionPolicy enumValue() default java.lang.annotation.RetentionPolicy.RUNTIME;\n" + |
| " public Class classValue() default Number.class;\n" + |
| " public int[] arrayValue() default {1, 2};\n" + |
| " public SuppressWarnings annotationValue() default @SuppressWarnings(\"cast\");\n" + |
| "}\n", |
| "package t; public @T class Test { }", |
| Expect.SUCCESS, |
| Expect.SUCCESS); |
| } |
| |
| @Test |
| void testConstantTest() throws Exception { |
| //XXX: other constant types (String in particular) - see testStringConstant |
| doPrintElementTest("package t; public class T { public static final int A = 1; }", |
| "package t; public class T { public static final int A = 2; }", |
| "t.T", |
| "package t;\n\n" + |
| "public class T {\n" + |
| " public static final int A = 1;\n\n" + |
| " public T();\n" + |
| "}\n", |
| "t.T", |
| "package t;\n\n" + |
| "public class T {\n" + |
| " public static final int A = 2;\n\n" + |
| " public T();\n" + |
| "}\n"); |
| doPrintElementTest("package t; public class T { public static final boolean A = false; }", |
| "package t; public class T { public static final boolean A = true; }", |
| "t.T", |
| "package t;\n\n" + |
| "public class T {\n" + |
| " public static final boolean A = false;\n\n" + |
| " public T();\n" + |
| "}\n", |
| "t.T", |
| "package t;\n\n" + |
| "public class T {\n" + |
| " public static final boolean A = true;\n\n" + |
| " public T();\n" + |
| "}\n"); |
| } |
| |
| @Test |
| void testAnnotations() throws Exception { |
| Set<String> extraAnnotations = Set.of("Ljava/lang/annotation/Retention;"); |
| CreateSymbols.HARDCODED_ANNOTATIONS.addAll(extraAnnotations); |
| try { |
| doPrintElementTest("package t;" + |
| "import java.lang.annotation.*;" + |
| "public @Visible @Invisible class T { public void extra() { } }" + |
| "@Retention(RetentionPolicy.RUNTIME) @interface Visible { }" + |
| "@Retention(RetentionPolicy.CLASS) @interface Invisible { }", |
| "package t;" + |
| "import java.lang.annotation.*;" + |
| "public @Visible @Invisible class T { }" + |
| "@Retention(RetentionPolicy.RUNTIME) @interface Visible { }" + |
| "@Retention(RetentionPolicy.CLASS) @interface Invisible { }", |
| "t.T", |
| "package t;\n\n" + |
| "@t.Invisible\n" + |
| "@t.Visible\n" + |
| "public class T {\n\n" + |
| " public T();\n\n" + |
| " public void extra();\n" + |
| "}\n", |
| "t.Visible", |
| "package t;\n\n" + |
| "@java.lang.annotation.Retention(RUNTIME)\n" + |
| "@interface Visible {\n" + |
| "}\n"); |
| doPrintElementTest("package t;" + |
| "import java.lang.annotation.*;" + |
| "import java.util.*;" + |
| "public class T {" + |
| " public void test(int h, @Invisible int i, @Visible List<String> j, int k) { }" + |
| "}" + |
| "@Retention(RetentionPolicy.RUNTIME) @interface Visible { }" + |
| "@Retention(RetentionPolicy.CLASS) @interface Invisible { }", |
| "package t;" + |
| "import java.lang.annotation.*;" + |
| "import java.util.*;" + |
| "public class T {" + |
| " public void test(int h, @Invisible int i, @Visible List<String> j, int k) { }" + |
| " public void extra() { }" + |
| "}" + |
| "@Retention(RetentionPolicy.RUNTIME) @interface Visible { }" + |
| "@Retention(RetentionPolicy.CLASS) @interface Invisible { }", |
| "t.T", |
| "package t;\n\n" + |
| "public class T {\n\n" + |
| " public T();\n\n" + |
| " public void test(int arg0,\n" + |
| " @t.Invisible int arg1,\n" + |
| " @t.Visible java.util.List<java.lang.String> arg2,\n" + |
| " int arg3);\n" + |
| "}\n", |
| "t.Visible", |
| "package t;\n\n" + |
| "@java.lang.annotation.Retention(RUNTIME)\n" + |
| "@interface Visible {\n" + |
| "}\n"); |
| doPrintElementTest("package t;" + |
| "import java.lang.annotation.*;" + |
| "public class T {" + |
| " public void test(@Ann(v=\"url\", dv=\"\\\"\\\"\") String str) { }" + |
| "}" + |
| "@Retention(RetentionPolicy.RUNTIME) @interface Ann {" + |
| " public String v();" + |
| " public String dv();" + |
| "}", |
| "package t;" + |
| "public class T { }", |
| "t.T", |
| "package t;\n\n" + |
| "public class T {\n\n" + |
| " public T();\n\n" + |
| " public void test(@t.Ann(dv=\"\\\"\\\"\", v=\"url\") java.lang.String arg0);\n" + |
| "}\n", |
| "t.T", |
| "package t;\n\n" + |
| "public class T {\n\n" + |
| " public T();\n" + |
| "}\n"); |
| } finally { |
| CreateSymbols.HARDCODED_ANNOTATIONS.removeAll(extraAnnotations); |
| } |
| } |
| |
| @Test |
| void testStringConstant() throws Exception { |
| doTest("package t; public class T { public static final String C = \"\"; }", |
| "package t; public class T { public static final String C = \"\"; public void extra() { } }", |
| "package t; public class Test { { System.err.println(T.C); } }", |
| Expect.SUCCESS, |
| Expect.SUCCESS); |
| } |
| |
| @Test |
| void testCopyProfileAnnotation() throws Exception { |
| String oldProfileAnnotation = CreateSymbols.PROFILE_ANNOTATION; |
| try { |
| CreateSymbols.PROFILE_ANNOTATION = "Lt/Ann;"; |
| doTestEquivalence("package t; public @Ann class T { public void t() {} } @interface Ann { }", |
| "package t; public class T { public void t() {} }", |
| "t.T"); |
| } finally { |
| CreateSymbols.PROFILE_ANNOTATION = oldProfileAnnotation; |
| } |
| } |
| |
| @Test |
| void testParseAnnotation() throws Exception { |
| CreateSymbols.parseAnnotations("@Lsun/Proprietary+Annotation;@Ljdk/Profile+Annotation;(value=I1)", new int[1]); |
| CreateSymbols.parseAnnotations("@Ltest;(value={\"\"})", new int[1]); |
| CreateSymbols.parseAnnotations("@Ljava/beans/ConstructorProperties;(value={\"path\"})", new int[1]); |
| CreateSymbols.parseAnnotations("@Ljava/beans/ConstructorProperties;(value=I-2)", new int[1]); |
| } |
| |
| @Test |
| void testStringCharLiterals() throws Exception { |
| doPrintElementTest("package t;" + |
| "public class T {" + |
| " public static final String STR = \"\\u0000\\u0001\\uffff\";" + |
| " public static final String EMPTY = \"\";" + |
| " public static final String AMP = \"&&<<>>''\";" + |
| "}", |
| "package t;" + |
| " public class T {" + |
| " public static final char c = '\\uffff';" + |
| "}", |
| "t.T", |
| "package t;\n\n" + |
| "public class T {\n" + |
| " public static final java.lang.String STR = \"\\u0000\\u0001\\uffff\";\n" + |
| " public static final java.lang.String EMPTY = \"\";\n" + |
| " public static final java.lang.String AMP = \"&&<<>>'\\'\";\n\n" + |
| " public T();\n" + |
| "}\n", |
| "t.T", |
| "package t;\n\n" + |
| "public class T {\n" + |
| " public static final char c = '\\uffff';\n\n" + |
| " public T();\n" + |
| "}\n"); |
| } |
| |
| @Test |
| void testGenerification() throws Exception { |
| doTest("package t; public class T { public class TT { public Object t() { return null; } } }", |
| "package t; public class T<E> { public class TT { public E t() { return null; } } }", |
| "package t; public class Test { { T.TT tt = null; tt.t(); } }", |
| Expect.SUCCESS, |
| Expect.SUCCESS); |
| } |
| |
| @Test |
| void testClearMissingAnnotations() throws Exception { |
| doPrintElementTest(new String[] { |
| """ |
| package t; |
| import t.impl.HC; |
| import t.impl.HR; |
| @HC @HR public class T { |
| @HC @HR public static final int i = 0; |
| @HC @HR public void t() {} |
| } |
| """, |
| """ |
| package t.impl; |
| import java.lang.annotation.*; |
| @Retention(RetentionPolicy.CLASS) |
| public @interface HC { |
| } |
| """, |
| """ |
| package t.impl; |
| import java.lang.annotation.*; |
| @Retention(RetentionPolicy.RUNTIME) |
| public @interface HR { |
| } |
| """ |
| }, |
| new String[] { |
| """ |
| package t; |
| public class T { |
| public static final int i = 0; |
| } |
| """ |
| }, |
| "t.T", |
| """ |
| package t; |
| |
| public class T { |
| public static final int i = 0; |
| |
| public T(); |
| |
| public void t(); |
| } |
| """, |
| "t.T", |
| """ |
| package t; |
| |
| public class T { |
| public static final int i = 0; |
| |
| public T(); |
| } |
| """); |
| } |
| |
| int i = 0; |
| |
| void doTest(String code7, String code8, String testCode, Expect result7, Expect result8) throws Exception { |
| ToolBox tb = new ToolBox(); |
| Path classes = prepareVersionedCTSym(new String[] {code7}, new String[] {code8}); |
| Path output = classes.getParent(); |
| Path scratch = output.resolve("scratch"); |
| |
| Files.createDirectories(scratch); |
| |
| new JavacTask(tb) |
| .sources(testCode) |
| .options("-d", scratch.toAbsolutePath().toString(), "-classpath", computeClassPath(classes, "7")) |
| .run(result7) |
| .writeAll(); |
| new JavacTask(tb) |
| .sources(testCode) |
| .options("-d", scratch.toAbsolutePath().toString(), "-classpath", computeClassPath(classes, "8")) |
| .run(result8) |
| .writeAll(); |
| } |
| |
| private static String computeClassPath(Path classes, String version) throws IOException { |
| try (Stream<Path> elements = Files.list(classes)) { |
| return elements.filter(el -> el.getFileName().toString().contains(version)) |
| .map(el -> el.resolve("java.base")) |
| .map(el -> el.toAbsolutePath().toString()) |
| .collect(Collectors.joining(File.pathSeparator)); |
| } |
| } |
| |
| void doPrintElementTest(String code7, String code8, String className7, String printed7, String className8, String printed8) throws Exception { |
| doPrintElementTest(new String[] {code7}, new String[] {code8}, className7, printed7, className8, printed8); |
| } |
| |
| void doPrintElementTest(String[] code7, String[] code8, String className7, String printed7, String className8, String printed8) throws Exception { |
| ToolBox tb = new ToolBox(); |
| Path classes = prepareVersionedCTSym(code7, code8); |
| Path output = classes.getParent(); |
| Path scratch = output.resolve("scratch"); |
| |
| Files.createDirectories(scratch); |
| |
| String out; |
| out = new JavacTask(tb, Task.Mode.CMDLINE) |
| .options("-d", scratch.toAbsolutePath().toString(), "-classpath", computeClassPath(classes, "7"), "-Xprint", className7) |
| .run(Expect.SUCCESS) |
| .getOutput(Task.OutputKind.STDOUT) |
| .replaceAll("\\R", "\n"); |
| if (!out.equals(printed7)) { |
| throw new AssertionError("out=" + out + "; printed7=" + printed7); |
| } |
| out = new JavacTask(tb, Task.Mode.CMDLINE) |
| .options("-d", scratch.toAbsolutePath().toString(), "-classpath", computeClassPath(classes, "8"), "-Xprint", className8) |
| .run(Expect.SUCCESS) |
| .getOutput(Task.OutputKind.STDOUT) |
| .replaceAll("\\R", "\n"); |
| if (!out.equals(printed8)) { |
| throw new AssertionError("out=" + out + "; printed8=" + printed8); |
| } |
| } |
| |
| void doTestEquivalence(String code7, String code8, String testClass) throws Exception { |
| Path classes = prepareVersionedCTSym(new String[] {code7}, new String[] {code8}); |
| Path classfile = classes.resolve("78").resolve("java.base").resolve(testClass.replace('.', '/') + ".class"); |
| |
| if (!Files.isReadable(classfile)) { |
| throw new AssertionError("Cannot find expected class."); |
| } |
| } |
| |
| @Test |
| void testIncluded() throws Exception { |
| doTestIncluded("package t;\n" + |
| "public class Test extends PP1<PP2> implements PP3<PP4>, PP5<PP6> {\n" + |
| " public PP7 m1(PP8 p) { return null;}\n" + |
| " public PP9<PPA> m2(PPB<PPC> p) { return null;}\n" + |
| " public PPD f1;\n" + |
| " public PPE<PPF> f2;\n" + |
| " public Test2 aux;\n" + |
| "}\n" + |
| "class Test2 extends PPG implements PPH, PPI {\n" + |
| "}\n" + |
| "class PP1<T> {}\n" + |
| "class PP2 {}\n" + |
| "interface PP3<T> {}\n" + |
| "class PP4 {}\n" + |
| "interface PP5<T> {}\n" + |
| "class PP6 {}\n" + |
| "class PP7 {}\n" + |
| "class PP8 {}\n" + |
| "class PP9<T> {}\n" + |
| "class PPA {}\n" + |
| "class PPB<T> {}\n" + |
| "class PPC {}\n" + |
| "class PPD {}\n" + |
| "class PPE<T> {}\n" + |
| "class PPF {}\n" + |
| "class PPG {}\n" + |
| "interface PPH {}\n" + |
| "interface PPI {}\n", |
| "t.Test", |
| "t.Test2", |
| "t.PP1", |
| "t.PP2", |
| "t.PP3", |
| "t.PP4", |
| "t.PP5", |
| "t.PP6", |
| "t.PP7", |
| "t.PP8", |
| "t.PP9", |
| "t.PPA", |
| "t.PPB", |
| "t.PPC", |
| "t.PPD", |
| "t.PPE", |
| "t.PPF", |
| "t.PPG", |
| "t.PPH", |
| "t.PPI"); |
| } |
| |
| @Test |
| void testRecords() throws Exception { |
| doPrintElementTest("package t;" + |
| "public class T {" + |
| " public record R(int i, java.util.List<String> l) { }" + |
| "}", |
| "package t;" + |
| "public class T {" + |
| " public record R(@Ann int i, long j, java.util.List<String> l) { }" + |
| " public @interface Ann {} " + |
| "}", |
| "t.T$R", |
| """ |
| |
| public static record R(int i, java.util.List<java.lang.String> l) { |
| |
| public R(int i, |
| java.util.List<java.lang.String> l); |
| |
| public final java.lang.String toString(); |
| |
| public final int hashCode(); |
| |
| public final boolean equals(java.lang.Object arg0); |
| |
| public int i(); |
| |
| public java.util.List<java.lang.String> l(); |
| } |
| """, |
| "t.T$R", |
| """ |
| |
| public static record R(@t.T.Ann int i, long j, java.util.List<java.lang.String> l) { |
| |
| public final java.lang.String toString(); |
| |
| public final int hashCode(); |
| |
| public final boolean equals(java.lang.Object arg0); |
| |
| public java.util.List<java.lang.String> l(); |
| |
| public R(@t.T.Ann int i, |
| long j, |
| java.util.List<java.lang.String> l); |
| |
| @t.T.Ann |
| public int i(); |
| |
| public long j(); |
| } |
| """); |
| doPrintElementTest("package t;" + |
| "public record R() {" + |
| "}", |
| "package t;" + |
| "public record R(int i) {" + |
| "}", |
| "t.R", |
| """ |
| package t; |
| \n\ |
| public record R() { |
| \n\ |
| public R(); |
| \n\ |
| public final java.lang.String toString(); |
| \n\ |
| public final int hashCode(); |
| \n\ |
| public final boolean equals(java.lang.Object arg0); |
| } |
| """, |
| "t.R", |
| """ |
| package t; |
| \n\ |
| public record R(int i) { |
| \n\ |
| public final java.lang.String toString(); |
| \n\ |
| public final int hashCode(); |
| \n\ |
| public final boolean equals(java.lang.Object arg0); |
| \n\ |
| public R(int i); |
| \n\ |
| public int i(); |
| } |
| """); |
| } |
| |
| void doTestIncluded(String code, String... includedClasses) throws Exception { |
| boolean oldIncludeAll = includeAll; |
| try { |
| includeAll = false; |
| Path classes = prepareVersionedCTSym(new String[] {code}, new String[] {"package other; public class Other {}"}); |
| Path root = classes.resolve("7").resolve("java.base"); |
| try (Stream<Path> classFiles = Files.walk(root)) { |
| Set<String> names = classFiles.map(p -> root.relativize(p)) |
| .map(p -> p.toString()) |
| .map(n -> {System.err.println("n= " + n); return n;}) |
| .filter(n -> n.endsWith(".class")) |
| .map(n -> n.substring(0, n.lastIndexOf('.'))) |
| .map(n -> n.replace(File.separator, ".")) |
| .collect(Collectors.toSet()); |
| |
| if (!names.equals(new HashSet<>(Arrays.asList(includedClasses)))) |
| throw new AssertionError("Expected classes not included: " + names); |
| } |
| } finally { |
| includeAll = oldIncludeAll; |
| } |
| } |
| |
| Path prepareVersionedCTSym(String[] code7, String[] code8) throws Exception { |
| String testClasses = System.getProperty("test.classes"); |
| Path output = Paths.get(testClasses, "test-data" + i++); |
| deleteRecursively(output); |
| Files.createDirectories(output); |
| Path ver7Jar = output.resolve("7.jar"); |
| compileAndPack(output, ver7Jar, code7); |
| Path ver8Jar = output.resolve("8.jar"); |
| compileAndPack(output, ver8Jar, code8); |
| |
| Path classes = output.resolve("classes.zip"); |
| |
| Path ctSym = output.resolve("ct.sym"); |
| |
| deleteRecursively(ctSym); |
| |
| CreateSymbols.ALLOW_NON_EXISTING_CLASSES = true; |
| CreateSymbols.EXTENSION = ".class"; |
| |
| testGenerate(ver7Jar, ver8Jar, ctSym, "8", classes.toAbsolutePath().toString()); |
| |
| Path classesDir = output.resolve("classes"); |
| |
| try (JarFile jf = new JarFile(classes.toFile())) { |
| Enumeration<JarEntry> en = jf.entries(); |
| |
| while (en.hasMoreElements()) { |
| JarEntry je = en.nextElement(); |
| if (je.isDirectory()) continue; |
| Path target = classesDir.resolve(je.getName()); |
| Files.createDirectories(target.getParent()); |
| Files.copy(jf.getInputStream(je), target); |
| } |
| } |
| |
| return classesDir; |
| } |
| |
| boolean includeAll = true; |
| |
| void testGenerate(Path jar7, Path jar8, Path descDest, String version, String classDest) throws IOException { |
| deleteRecursively(descDest); |
| |
| List<VersionDescription> versions = |
| Arrays.asList(new VersionDescription(jar7.toAbsolutePath().toString(), "7", null), |
| new VersionDescription(jar8.toAbsolutePath().toString(), "8", "7")); |
| |
| ExcludeIncludeList acceptAll = new ExcludeIncludeList(null, null) { |
| @Override public boolean accepts(String className) { |
| return true; |
| } |
| }; |
| new CreateSymbols() { |
| @Override |
| protected boolean includeEffectiveAccess(ClassList classes, ClassDescription clazz) { |
| return includeAll ? true : super.includeEffectiveAccess(classes, clazz); |
| } |
| }.createBaseLine(versions, acceptAll, descDest, new String[0]); |
| Path symbolsDesc = descDest.resolve("symbols"); |
| Path systemModules = descDest.resolve("systemModules"); |
| |
| Files.newBufferedWriter(systemModules).close(); |
| |
| try { |
| new CreateSymbols().createSymbols(null, symbolsDesc.toAbsolutePath().toString(), classDest, 0, "8", systemModules.toString()); |
| } catch (Throwable t) { |
| t.printStackTrace(); |
| throw t; |
| } |
| } |
| |
| void compileAndPack(Path output, Path outputFile, String... code) throws Exception { |
| ToolBox tb = new ToolBox(); |
| Path scratch = output.resolve("temp"); |
| deleteRecursively(scratch); |
| Files.createDirectories(scratch); |
| System.err.println(Arrays.asList(code)); |
| new JavacTask(tb).sources(code).options("-d", scratch.toAbsolutePath().toString()).run(Expect.SUCCESS); |
| List<String> classFiles = collectClassFile(scratch); |
| try (Writer out = Files.newBufferedWriter(outputFile)) { |
| for (String classFile : classFiles) { |
| try (InputStream in = Files.newInputStream(scratch.resolve(classFile))) { |
| int read; |
| |
| while ((read = in.read()) != (-1)) { |
| out.write(String.format("%02x", read)); |
| } |
| |
| out.write("\n"); |
| } |
| } |
| } |
| } |
| |
| List<String> collectClassFile(Path root) throws IOException { |
| try (Stream<Path> files = Files.walk(root)) { |
| return files.filter(p -> Files.isRegularFile(p)) |
| .filter(p -> p.getFileName().toString().endsWith(".class")) |
| .map(p -> root.relativize(p).toString()) |
| .filter(p -> !p.contains("impl")) |
| .collect(Collectors.toList()); |
| } |
| } |
| |
| void deleteRecursively(Path dir) throws IOException { |
| Files.walkFileTree(dir, new FileVisitor<Path>() { |
| @Override |
| public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { |
| return FileVisitResult.CONTINUE; |
| } |
| @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { |
| Files.delete(file); |
| return FileVisitResult.CONTINUE; |
| } |
| @Override public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { |
| return FileVisitResult.CONTINUE; |
| } |
| @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { |
| Files.delete(dir); |
| return FileVisitResult.CONTINUE; |
| } |
| }); |
| } |
| |
| @Retention(RetentionPolicy.RUNTIME) |
| @interface Test { |
| } |
| } |