From c92bdde0b3515d16af93a8366f3945a9691feb6d Mon Sep 17 00:00:00 2001 From: lzpeng723 <1500913306@qq.com> Date: Mon, 23 Nov 2020 00:21:21 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=A2=9E=E5=8A=A0Java=E6=BA=90=E7=A0=81?= =?UTF-8?q?=E7=BC=96=E8=AF=91=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/compiler/JavaClassFileManager.java | 105 +++++++ .../core/compiler/JavaClassFileObject.java | 63 ++++ .../core/compiler/JavaSourceCompiler.java | 292 ++++++++++++++++++ .../core/compiler/JavaSourceFileObject.java | 98 ++++++ .../cn/hutool/core/compiler/package-info.java | 6 + .../core/compiler/JavaSourceCompilerTest.java | 42 +++ .../src/test/resources/test-compile/a/A.java | 24 ++ .../src/test/resources/test-compile/b/B.java | 8 + .../src/test/resources/test-compile/c/C.java | 9 + 9 files changed, 647 insertions(+) create mode 100644 hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileManager.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileObject.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceCompiler.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceFileObject.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/compiler/package-info.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/compiler/JavaSourceCompilerTest.java create mode 100644 hutool-core/src/test/resources/test-compile/a/A.java create mode 100644 hutool-core/src/test/resources/test-compile/b/B.java create mode 100644 hutool-core/src/test/resources/test-compile/c/C.java diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileManager.java b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileManager.java new file mode 100644 index 000000000..ce2d4af49 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileManager.java @@ -0,0 +1,105 @@ +package cn.hutool.core.compiler; + +import cn.hutool.core.io.IoUtil; + +import javax.tools.FileObject; +import javax.tools.ForwardingJavaFileManager; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.JavaFileObject.Kind; +import java.io.InputStream; +import java.security.SecureClassLoader; +import java.util.HashMap; +import java.util.Map; + +/** + * Java 字节码文件对象 + * 正常我们使用javac命令编译源码时会将class文件写入到磁盘中,但在运行时动态编译类不适合保存在磁盘中 + * 我们采取此对象来管理运行时动态编译类生成的字节码 + * + * @author lzpeng + * @see JavaSourceCompilerBak#compile() + * @see com.sun.tools.javac.api.ClientCodeWrapper.WrappedJavaFileManager#getJavaFileForOutput(javax.tools.JavaFileManager.Location, java.lang.String, javax.tools.JavaFileObject.Kind, javax.tools.FileObject) + */ +final class JavaClassFileManager extends ForwardingJavaFileManager { + + /** + * 存储java字节码文件对象映射 + */ + private final Map javaFileObjectMap = new HashMap<>(); + + /** + * 加载动态编译生成类的父类加载器 + */ + private final ClassLoader parent; + + /** + * 构造 + * + * @param parent 父类加载器 + * @param fileManager 字节码文件管理器 + * @see JavaSourceCompilerBak#compile() + */ + protected JavaClassFileManager(final ClassLoader parent, final JavaFileManager fileManager) { + super(fileManager); + if (parent == null) { + this.parent = Thread.currentThread().getContextClassLoader(); + } else { + this.parent = parent; + } + } + + /** + * 获得动态编译生成的类的类加载器 + * + * @param location 源码位置 + * @return 动态编译生成的类的类加载器 + * @see JavaSourceCompilerBak#compile() + */ + @Override + public ClassLoader getClassLoader(final Location location) { + return new SecureClassLoader(parent) { + + /** + * 查找类 + * @param name 类名 + * @return 类的class对象 + * @throws ClassNotFoundException 未找到类异常 + */ + @Override + protected Class findClass(final String name) throws ClassNotFoundException { + final JavaFileObject javaFileObject = javaFileObjectMap.get(name); + if (javaFileObject != null) { + try { + final InputStream inputStream = javaFileObject.openInputStream(); + final byte[] bytes = IoUtil.readBytes(inputStream); + final Class c = defineClass(name, bytes, 0, bytes.length); + return c; + } catch (Exception e) { + e.printStackTrace(); + } + } + throw new ClassNotFoundException(name); + } + }; + } + + /** + * 获得Java字节码文件对象 + * 编译器编译源码时会将Java源码对象编译转为Java字节码对象 + * + * @param location 源码位置 + * @param className 类名 + * @param kind 文件类型 + * @param sibling 将Java源码对象 + * @return Java字节码文件对象 + * @see com.sun.tools.javac.api.lientCodeWrapper.WrappedJavaFileManager#getJavaFileForOutput(javax.tools.JavaFileManager.Location, java.lang.String, javax.tools.JavaFileObject.Kind, javax.tools.FileObject) + */ + @Override + public JavaFileObject getJavaFileForOutput(final Location location, final String className, final Kind kind, final FileObject sibling) { + final JavaFileObject javaFileObject = new JavaClassFileObject(className, kind); + javaFileObjectMap.put(className, javaFileObject); + return javaFileObject; + } + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileObject.java b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileObject.java new file mode 100644 index 000000000..c9f92bc70 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileObject.java @@ -0,0 +1,63 @@ +package cn.hutool.core.compiler; + + +import javax.tools.SimpleJavaFileObject; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; + +/** + * Java 字节码文件对象 + * + * @author lzpeng + * @see JavaClassFileManager#getClassLoader(javax.tools.JavaFileManager.Location + * @see JavaClassFileManager#getJavaFileForOutput(javax.tools.JavaFileManager.Location, java.lang.String, javax.tools.JavaFileObject.Kind, javax.tools.FileObject) + * @see com.sun.tools.javac.jvm.ClassWriter.ClassWriter#writeClass(com.sun.tools.javac.code.Symbol.ClassSymbol) + */ +final class JavaClassFileObject extends SimpleJavaFileObject { + + /** + * 字节码输出流 + */ + private final ByteArrayOutputStream byteArrayOutputStream; + + /** + * 构造 + * + * @param className 需要编译的类名 + * @param kind 需要编译的文件类型 + * @see JavaClassFileManager#getJavaFileForOutput(javax.tools.JavaFileManager.Location, java.lang.String, javax.tools.JavaFileObject.Kind, javax.tools.FileObject) + */ + protected JavaClassFileObject(final String className, final Kind kind) { + super(URI.create("string:///" + className.replaceAll("\\.", "/") + kind.extension), kind); + this.byteArrayOutputStream = new ByteArrayOutputStream(); + } + + /** + * 获得字节码输入流 + * 编译器编辑源码后,我们将通过此输出流获得编译后的字节码,以便运行时加载类 + * + * @return 字节码输入流 + * @see JavaClassFileManager#getClassLoader(javax.tools.JavaFileManager.Location) + */ + @Override + public InputStream openInputStream() { + final byte[] bytes = byteArrayOutputStream.toByteArray(); + return new ByteArrayInputStream(bytes); + } + + /** + * 获得字节码输出流 + * 编译器编辑源码时,会将编译结果输出到本输出流中 + * + * @return 字节码输出流 + * @see com.sun.tools.javac.jvm.ClassWriter.ClassWriter#writeClass(com.sun.tools.javac.code.Symbol.ClassSymbol) + */ + @Override + public OutputStream openOutputStream() { + return this.byteArrayOutputStream; + } + +} \ No newline at end of file diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceCompiler.java b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceCompiler.java new file mode 100644 index 000000000..c88e579fa --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceCompiler.java @@ -0,0 +1,292 @@ +package cn.hutool.core.compiler; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.URLUtil; + +import javax.tools.*; +import javax.tools.JavaCompiler.CompilationTask; +import javax.tools.JavaFileObject.Kind; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.*; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +/** + * Java 源码编译器 + * + * @author lzpeng + */ +public final class JavaSourceCompiler { + + /** + * java 编译器 + */ + private static final JavaCompiler JAVA_COMPILER = ToolProvider.getSystemJavaCompiler(); + + /** + * 待编译的文件 可以是 .java文件 压缩文件 文件夹 递归搜索文件夹内的zip包和jar包 + */ + private final List sourceFileList = new ArrayList<>(); + + /** + * 编译时需要加入classpath中的文件 可以是 压缩文件 文件夹递归搜索文件夹内的zip包和jar包 + */ + private final List libraryFileList = new ArrayList<>(); + + /** + * 源码映射 key: 类名 value: 类源码 + */ + private final Map sourceCodeMap = new LinkedHashMap<>(); + + /** + * 编译类时使用的父类加载器 + */ + private final ClassLoader parentClassLoader; + + + /** + * 构造 + * + * @param parent 父类加载器 + */ + private JavaSourceCompiler(ClassLoader parent) { + this.parentClassLoader = parent; + } + + + /** + * 创建Java源码编译器 + * + * @param parent 父类加载器 + * @return Java源码编译器 + */ + public static JavaSourceCompiler create(ClassLoader parent) { + return new JavaSourceCompiler(parent); + } + + + /** + * 向编译器中加入待编译的文件 支持 .java, 文件夹, 压缩文件 递归搜索文件夹内的压缩文件和jar包 + * + * @param files 待编译的文件 支持 .java, 文件夹, 压缩文件 递归搜索文件夹内的压缩文件和jar包 + * @return Java源码编译器 + */ + public JavaSourceCompiler addSource(final File... files) { + if (ArrayUtil.isNotEmpty(files)) { + this.sourceFileList.addAll(Arrays.asList(files)); + } + return this; + } + + /** + * 向编译器中加入待编译的源码Map + * + * @param sourceCodeMap 源码Map key: 类名 value 源码 + * @return Java源码编译器 + */ + public JavaSourceCompiler addSource(final Map sourceCodeMap) { + if (MapUtil.isNotEmpty(sourceCodeMap)) { + this.sourceCodeMap.putAll(sourceCodeMap); + } + return this; + } + + /** + * 加入编译Java源码时所需要的jar包 + * + * @param files 编译Java源码时所需要的jar包 + * @return Java源码编译器 + */ + public JavaSourceCompiler addLibrary(final File... files) { + if (ArrayUtil.isNotEmpty(files)) { + this.libraryFileList.addAll(Arrays.asList(files)); + } + return this; + } + + /** + * 向编译器中加入待编译的源码Map + * + * @param className 类名 + * @param sourceCode 源码 + * @return Java文件编译器 + */ + public JavaSourceCompiler addSource(final String className, final String sourceCode) { + if (className != null && sourceCode != null) { + this.sourceCodeMap.put(className, sourceCode); + } + return this; + } + + /** + * 编译所有文件并返回类加载器 + * + * @return 类加载器 + */ + public ClassLoader compile() { + final ClassLoader parent; + if (this.parentClassLoader == null) { + parent = Thread.currentThread().getContextClassLoader(); + } else { + parent = this.parentClassLoader; + } + // 获得classPath + final List classPath = getClassPath(); + final URL[] urLs = URLUtil.getURLs(classPath.toArray(new File[0])); + final URLClassLoader ucl = URLClassLoader.newInstance(urLs, parent); + if (sourceCodeMap.isEmpty() && sourceFileList.isEmpty()) { + // 没有需要编译的源码 + return ucl; + } + // 没有需要编译的源码文件返回加载zip或jar包的类加载器 + final Iterable javaFileObjectList = getJavaFileObject(); + // 创建编译器 + final JavaFileManager standardJavaFileManager = JAVA_COMPILER.getStandardFileManager(null, null, null); + final JavaFileManager javaFileManager = new JavaClassFileManager(ucl, standardJavaFileManager); + final DiagnosticCollector diagnosticCollector = new DiagnosticCollector<>(); + final List options = new ArrayList<>(); + if (!classPath.isEmpty()) { + final List cp = classPath.stream().map(File::getAbsolutePath).collect(Collectors.toList()); + options.add("-cp"); + options.addAll(cp); + } + // 编译文件 + final CompilationTask task = JAVA_COMPILER.getTask(null, javaFileManager, diagnosticCollector, + options, null, javaFileObjectList); + final Boolean result = task.call(); + if (Boolean.TRUE.equals(result)) { + return javaFileManager.getClassLoader(StandardLocation.CLASS_OUTPUT); + } else { + // 编译失败,收集错误信息 + final List diagnostics = diagnosticCollector.getDiagnostics(); + final String errorMsg = diagnostics.stream().map(String::valueOf) + .collect(Collectors.joining(System.lineSeparator())); + // CompileException + throw new RuntimeException(errorMsg); + } + } + + /** + * 获得编译源码时需要的classpath + * + * @return 编译源码时需要的classpath + */ + private List getClassPath() { + List classPathFileList = new ArrayList<>(); + for (File file : libraryFileList) { + List jarOrZipFile = FileUtil.loopFiles(file, this::isJarOrZipFile); + classPathFileList.addAll(jarOrZipFile); + if (file.isDirectory()) { + classPathFileList.add(file); + } + } + return classPathFileList; + } + + /** + * 获得待编译的Java文件对象 + * + * @return 待编译的Java文件对象 + */ + private Iterable getJavaFileObject() { + final Collection collection = new ArrayList<>(); + for (File file : sourceFileList) { + // .java 文件 + final List javaFileList = FileUtil.loopFiles(file, this::isJavaFile); + for (File javaFile : javaFileList) { + collection.add(getJavaFileObjectByJavaFile(javaFile)); + } + // 压缩包 + final List jarOrZipFileList = FileUtil.loopFiles(file, this::isJarOrZipFile); + for (File jarOrZipFile : jarOrZipFileList) { + collection.addAll(getJavaFileObjectByZipOrJarFile(jarOrZipFile)); + } + } + // 源码Map + collection.addAll(getJavaFileObjectByMap(this.sourceCodeMap)); + return collection; + } + + /** + * 通过源码Map获得Java文件对象 + * + * @param sourceCodeMap 源码Map + * @return Java文件对象集合 + */ + private Collection getJavaFileObjectByMap(final Map sourceCodeMap) { + if (MapUtil.isNotEmpty(sourceCodeMap)) { + return sourceCodeMap.entrySet().stream() + .map(entry -> new JavaSourceFileObject(entry.getKey(), entry.getValue(), Kind.SOURCE)) + .collect(Collectors.toList()); + } + return Collections.emptySet(); + } + + /** + * 通过.java文件创建Java文件对象 + * + * @param file .java文件 + * @return Java文件对象 + */ + private JavaFileObject getJavaFileObjectByJavaFile(final File file) { + return new JavaSourceFileObject(file.toURI(), Kind.SOURCE); + } + + /** + * 通过zip包或jar包创建Java文件对象 + * + * @param file 压缩文件 + * @return Java文件对象 + */ + private Collection getJavaFileObjectByZipOrJarFile(final File file) { + final Collection collection = new ArrayList<>(); + try { + final ZipFile zipFile = new ZipFile(file); + final Enumeration entries = zipFile.entries(); + while (entries.hasMoreElements()) { + final ZipEntry zipEntry = entries.nextElement(); + final String name = zipEntry.getName(); + if (name.endsWith(".java")) { + final InputStream inputStream = zipFile.getInputStream(zipEntry); + final JavaSourceFileObject fileObject = new JavaSourceFileObject(name, inputStream, Kind.SOURCE); + collection.add(fileObject); + } + } + return collection; + } catch (IOException e) { + e.printStackTrace(); + } + return Collections.emptyList(); + } + + + /** + * 是否是jar 或 zip 文件 + * + * @param file 文件 + * @return 是否是jar 或 zip 文件 + */ + private boolean isJarOrZipFile(final File file) { + final String fileName = file.getName(); + return fileName.endsWith(".jar") || fileName.endsWith(".zip"); + } + + /** + * 是否是.java文件 + * + * @param file 文件 + * @return 是否是.java文件 + */ + private boolean isJavaFile(final File file) { + final String fileName = file.getName(); + return fileName.endsWith(".java"); + } + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceFileObject.java b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceFileObject.java new file mode 100644 index 000000000..a13cc4841 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceFileObject.java @@ -0,0 +1,98 @@ +package cn.hutool.core.compiler; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.nio.charset.Charset; + +import javax.tools.SimpleJavaFileObject; + +import cn.hutool.core.io.IoUtil; + +/** + * Java 源码文件对象 + * + * @author lzpeng + * @see JavaSourceCompilerBak#getJavaFileObjectByJavaFile(java.io.File) + * @see JavaSourceCompilerBak#getJavaFileObjectByZipOrJarFile(java.io.File) + * @see JavaSourceCompilerBak#getJavaFileObject(java.util.Map) + * @see com.sun.tools.javac.api.ClientCodeWrapper.WrappedFileObject#getCharContent(boolean) + */ +final class JavaSourceFileObject extends SimpleJavaFileObject { + + /** + * 输入流 + */ + private InputStream inputStream; + + /** + * 构造 + * + * @param uri 需要编译的文件uri + * @param kind 需要编译的文件类型 + * @see JavaSourceCompilerBak#getJavaFileObjectByJavaFile(java.io.File) + */ + protected JavaSourceFileObject(URI uri, Kind kind) { + super(uri, kind); + } + + /** + * 构造 + * + * @param name 需要编译的文件名 + * @param inputStream 输入流 + * @param kind 需要编译的文件类型 + * @see JavaSourceCompilerBak#getJavaFileObjectByZipOrJarFile(java.io.File) + */ + protected JavaSourceFileObject(final String name, final InputStream inputStream, final Kind kind) { + super(URI.create("string:///" + name), kind); + this.inputStream = inputStream; + } + + /** + * 构造 + * + * @param className 需要编译的类名 + * @param code 需要编译的类源码 + * @param kind 需要编译的文件类型 + * @see JavaSourceCompilerBak#getJavaFileObject(java.util.Map) + */ + protected JavaSourceFileObject(final String className, final String code, final Kind kind) { + super(URI.create("string:///" + className.replaceAll("\\.", "/") + kind.extension), kind); + this.inputStream = new ByteArrayInputStream(code.getBytes()); + } + + /** + * 获得类源码的输入流 + * + * @return 类源码的输入流 + * @throws IOException IO 异常 + */ + @Override + public InputStream openInputStream() throws IOException { + if (inputStream == null) { + inputStream = toUri().toURL().openStream(); + } + return new BufferedInputStream(inputStream); + } + + /** + * 获得类源码 + * 编译器编辑源码前,会通过此方法获取类的源码 + * + * @param ignoreEncodingErrors 是否忽略编码错误 + * @return 需要编译的类的源码 + * @throws IOException IO异常 + * @see com.sun.tools.javac.api.ClientCodeWrapper.WrappedFileObject#getCharContent(boolean) + */ + @Override + public CharSequence getCharContent(final boolean ignoreEncodingErrors) throws IOException { + final InputStream in = openInputStream(); + final String code = IoUtil.read(in, Charset.defaultCharset()); + IoUtil.close(in); + return code; + } + +} \ No newline at end of file diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/package-info.java b/hutool-core/src/main/java/cn/hutool/core/compiler/package-info.java new file mode 100644 index 000000000..a0f3ef2b4 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/compiler/package-info.java @@ -0,0 +1,6 @@ +/** + * 运行时编译java源码,动态从字符串或外部文件加载类 + * + * @author : Lzpeng + */ +package cn.hutool.core.compiler; \ No newline at end of file diff --git a/hutool-core/src/test/java/cn/hutool/core/compiler/JavaSourceCompilerTest.java b/hutool-core/src/test/java/cn/hutool/core/compiler/JavaSourceCompilerTest.java new file mode 100644 index 000000000..b8ce0a6c7 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/compiler/JavaSourceCompilerTest.java @@ -0,0 +1,42 @@ +package cn.hutool.core.compiler; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.ZipUtil; +import org.junit.Assert; +import org.junit.Test; + +import java.io.File; +import java.io.InputStream; + +/** + * Java源码编译器测试 + * + * @author lzpeng + */ +public class JavaSourceCompilerTest { + + /** + * 测试编译Java源码 + */ + @Test + public void testCompile() throws ClassNotFoundException { + final File libFile = ZipUtil.zip(FileUtil.file("lib.jar"), + new String[]{"a/A.class", "a/A$1.class", "a/A$InnerClass.class"}, + new InputStream[]{ + FileUtil.getInputStream("test-compile/a/A.class"), + FileUtil.getInputStream("test-compile/a/A$1.class"), + FileUtil.getInputStream("test-compile/a/A$InnerClass.class") + }); + final ClassLoader classLoader = JavaSourceCompiler.create(null) + .addSource(FileUtil.file("test-compile/b/B.java")) + .addSource("c.C", FileUtil.readUtf8String("test-compile/c/C.java")) + .addLibrary(libFile) + .compile(); + final Class clazz = classLoader.loadClass("c.C"); + Object obj = ReflectUtil.newInstance(clazz); + Assert.assertTrue(String.valueOf(obj).startsWith("c.C@")); + FileUtil.del(libFile); + } + +} \ No newline at end of file diff --git a/hutool-core/src/test/resources/test-compile/a/A.java b/hutool-core/src/test/resources/test-compile/a/A.java new file mode 100644 index 000000000..8424934d5 --- /dev/null +++ b/hutool-core/src/test/resources/test-compile/a/A.java @@ -0,0 +1,24 @@ +package a; + +import cn.hutool.core.lang.ConsoleTable; +import cn.hutool.core.lang.caller.CallerUtil; + +public class A { + private class InnerClass { + } + + public A() { + new InnerClass() {{ + int i = 0; + Class caller = CallerUtil.getCaller(i); + final ConsoleTable t = new ConsoleTable(); + t.addHeader("类名", "类加载器"); + System.out.println("初始化 " + getClass() + " 的调用链为: "); + while (caller != null) { + t.addBody(caller.toString(), caller.getClassLoader().toString()); + caller = CallerUtil.getCaller(++i); + } + t.print(); + }}; + } +} \ No newline at end of file diff --git a/hutool-core/src/test/resources/test-compile/b/B.java b/hutool-core/src/test/resources/test-compile/b/B.java new file mode 100644 index 000000000..84c294dea --- /dev/null +++ b/hutool-core/src/test/resources/test-compile/b/B.java @@ -0,0 +1,8 @@ +package b; +import a.A; + +public class B { + public B() { + new A(); + } +} \ No newline at end of file diff --git a/hutool-core/src/test/resources/test-compile/c/C.java b/hutool-core/src/test/resources/test-compile/c/C.java new file mode 100644 index 000000000..7053de08d --- /dev/null +++ b/hutool-core/src/test/resources/test-compile/c/C.java @@ -0,0 +1,9 @@ +package c; + +import b.B; + +public class C { + public C() { + new B(); + } +} \ No newline at end of file From cde428b3700778091633c8555c2ed9ddd5aa0b38 Mon Sep 17 00:00:00 2001 From: lzpeng723 <1500913306@qq.com> Date: Mon, 23 Nov 2020 00:24:10 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E5=A2=9E=E5=8A=A0Java=E6=BA=90=E7=A0=81?= =?UTF-8?q?=E7=BC=96=E8=AF=91=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/test/resources/test-compile/a/A$1.class | Bin 0 -> 1679 bytes .../resources/test-compile/a/A$InnerClass.class | Bin 0 -> 447 bytes .../src/test/resources/test-compile/a/A.class | Bin 0 -> 357 bytes .../src/test/resources/test-compile/b/B.class | Bin 0 -> 260 bytes .../src/test/resources/test-compile/c/C.class | Bin 0 -> 260 bytes 5 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 hutool-core/src/test/resources/test-compile/a/A$1.class create mode 100644 hutool-core/src/test/resources/test-compile/a/A$InnerClass.class create mode 100644 hutool-core/src/test/resources/test-compile/a/A.class create mode 100644 hutool-core/src/test/resources/test-compile/b/B.class create mode 100644 hutool-core/src/test/resources/test-compile/c/C.class diff --git a/hutool-core/src/test/resources/test-compile/a/A$1.class b/hutool-core/src/test/resources/test-compile/a/A$1.class new file mode 100644 index 0000000000000000000000000000000000000000..78aff511d2899e9b4560c2ec7a0e5d2260291e01 GIT binary patch literal 1679 zcma)6TW=dh6#gb&XPxah7dvj_kb)^`vE7TCQYgeBkeHT+I!VE;gDJO-JxMm*>{|A^ zC=xG}J|Hd<5(o(-B;MM`wi27DQTmF+FX#`zO8FC%GrJ9r(^kdnotZsz<~!dxbI$qm zpF4K}oWzQX4u+tfnHU~Xp)hpI<&C#5`XWl1n*ScWss5Kej$ zZ%7pxl$>1=U0~S55k#kk0Kyz9x)}yqsyo25siKBnL{#)K94uRzrG{hMW~OY{M8?#u z#Y|Z@O;O9_yy>E2m>f~`GjuHq#}_j6C#O>RW&MioAMt2sRqSO*JPtBvTXowM7xZ~k zXoz7zQj9YMlBuGKXBc{0SOuqMSc@U-C$G0|Z8X3CE`;Y8xLkkz+x^YW=Fc}I#`7Ep z@B-1&E0wcCuZSAMspO>{tbC+wD*rS}*)AN!iz*Ja6Irc0Vud4#6fw0M4nxdGH|)%L zBI%F;p|6nNhZ*+m_F%GMm_#asqYPT}t6!VfziEE^Q$j-;8R5nG#JtdJodXs~oRkltQRitHP=<9NlTlN2AC*fpj*6F@{KD z>z8ZyZ+^M;vA!ZjC4*f58$8+p$O3sNRAdhzBw6aiBh&+!LGKh@sb8QBeLJqtuRv0uEIsh_glE{>ZgaJ| zZMT0n>R5W5tcMGZUcNl7S3Pw|KJq)X^kKFJ9vI)hj-F9v z1F_N?_KgPP!QdtaOM%Exp`=8fEx1U`-Nx_+(xu3eH5^O37;$kbl6CRAi#J`obseeg zLvdvtQ*B}Ivx;wrwXQGf&MhjsAPhgSO80;(E$U?G%HxZ04~!iLNXas`3sQLM906$ C`lH|g literal 0 HcmV?d00001 diff --git a/hutool-core/src/test/resources/test-compile/a/A$InnerClass.class b/hutool-core/src/test/resources/test-compile/a/A$InnerClass.class new file mode 100644 index 0000000000000000000000000000000000000000..ad922c85f0fb829e396f11664d0b3fcf6ce9f8f0 GIT binary patch literal 447 zcmZut%SyvQ6g@XdlQzaS)>nNjToma8j0<;)Pzr(&bfI)Vjf1Ad1d`PMav=p5et;h( z-Wv-Qk%2oiXU;w6p85QG{{V1?Lk9+-se)1WI!$#xPE=7iun7K4y{I5j>0@v^o$06~ z*yU4PbWaJjkQ>e!xQNrZyd>0n71Ey&ta0|B8z8KCsKIt&V%>v-Cc*3pp%uodzL`Hy zb$+j=i6*qeEK8n@_?9qv& zA$Y5hnq%(sOf1jr6^xdZNC_{L=c@3rEzxZ2DCbA7H} ob#}3VHUx06xl}mjesQn|!v(hf?-mCGJ5}99hj&Rw*uw#gZWmnT8N58#Iq zXIG(nc#nC^Z)W!U=i>{&84f~tgr3Z1A$-DUDPLt)N%NH56-!ligy2FO-Cc6i-~r1aV)lno?bB3k0(hdmJIUF-A4>O17y5To*V5pU!^;JC8=d z8gg~TDY3`XcWit@OxdG14m=E569)4J!{hZww8&`HheC=!B-70i?wid#>=E)fb z3{xWFgpt13Y;G_Ng-Pyfx7xlIu6?vc<%C(@l(t&i*7yED47(TqCPcY@d?lENX4{r- z>H9*wxahCo1l_DhqEk^~)kNLR-oxm8$H5n7NBW_0z@h4RUx^46H;RunOSPal6Q(+a IBh`S}AMYY6h5!Hn literal 0 HcmV?d00001 diff --git a/hutool-core/src/test/resources/test-compile/c/C.class b/hutool-core/src/test/resources/test-compile/c/C.class new file mode 100644 index 0000000000000000000000000000000000000000..42966eed7c45baee48b4b4abc8ec58ea94e8442a GIT binary patch literal 260 zcmXYry-veG5QM*VoO5z9B!nlR0}9;G@l#-l%7Oxv{%lTgk+CJ?@Lp7rK;i*-D8xFV znbGXd?9R{c=QnW9VL~K~sw_{4g~i&vyR3H2b9P^@ebo!*GBlyTR@3pxN{I9J$y-KD zQ^uHtnQqGL#$uTY^CC3