AsgcCOmpiler重构
This commit is contained in:
@@ -22,16 +22,14 @@
|
||||
package fun.asgc.neutrino.core.aop.compiler;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.sun.tools.javac.resources.compiler;
|
||||
import fun.asgc.neutrino.core.base.GlobalConfig;
|
||||
import fun.asgc.neutrino.core.util.CollectionUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.tools.*;
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -42,23 +40,34 @@ import java.util.stream.Collectors;
|
||||
@Slf4j
|
||||
@SuppressWarnings("all")
|
||||
public class AsgcCompiler {
|
||||
private static final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
|
||||
private DiagnosticCollector<JavaFileObject> collector;
|
||||
private List<String> classpathList;
|
||||
private final JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
|
||||
private final DiagnosticCollector<JavaFileObject> collector;
|
||||
private final StandardJavaFileManager standardJavaFileManager;
|
||||
private final List<String> options = new ArrayList<>();
|
||||
private final List<String> classpathList = new ArrayList<>();
|
||||
private final Collection<JavaFileObject> compilationUnits = new ArrayList<JavaFileObject>();
|
||||
private boolean isSaveSourceCodeFile;
|
||||
private boolean isSaveClassFile;
|
||||
private String generatorCodeSavePath;
|
||||
private ByteCodeClassLoader classLoader;
|
||||
private DynamicClassLoader dynamicClassLoader;
|
||||
|
||||
public AsgcCompiler() {
|
||||
if (null == compiler) {
|
||||
throw new RuntimeException("Can not get javax.tools.JavaCompiler, check whether \"tools.jar\" is in the environment variable CLASSPATH \nVisit https://jfinal.com/doc/4-8 for details \n");
|
||||
this(ClassLoader.getSystemClassLoader());
|
||||
}
|
||||
|
||||
public AsgcCompiler(ClassLoader classLoader) {
|
||||
if (null == javaCompiler) {
|
||||
throw new RuntimeException("Can not load JavaCompiler from javax.tools.ToolProvider#getSystemJavaCompiler(),\n please confirm the application running in JDK not JRE.");
|
||||
}
|
||||
this.collector = new DiagnosticCollector<>();
|
||||
this.classpathList = Lists.newArrayList();
|
||||
this.standardJavaFileManager = javaCompiler.getStandardFileManager(collector, null, null);
|
||||
this.isSaveClassFile = false;
|
||||
this.generatorCodeSavePath = GlobalConfig.getGeneratorCodeSavePath();
|
||||
this.classLoader = new ByteCodeClassLoader();
|
||||
this.dynamicClassLoader = new DynamicClassLoader(classLoader);
|
||||
|
||||
addOption("-Xlint:unchecked");
|
||||
addOption("-source", "1.8");
|
||||
addOption("-target", "1.8");
|
||||
}
|
||||
|
||||
public void addClasspath(String classpath) {
|
||||
@@ -78,16 +87,29 @@ public class AsgcCompiler {
|
||||
}
|
||||
|
||||
private List<String> getOptions() {
|
||||
List<String> options = Lists.newArrayList();
|
||||
options.add("-source");
|
||||
options.add("1.8");
|
||||
options.add("-target");
|
||||
options.add("1.8");
|
||||
List<String> list = Lists.newArrayList(options);
|
||||
if (!CollectionUtil.isEmpty(classpathList)) {
|
||||
options.add("-classpath");
|
||||
options.add(classpathList.stream().collect(Collectors.joining(File.pathSeparator)));
|
||||
list.add("-classpath");
|
||||
list.add(classpathList.stream().collect(Collectors.joining(File.pathSeparator)));
|
||||
}
|
||||
return options;
|
||||
return list;
|
||||
}
|
||||
|
||||
private void addOption(String option) {
|
||||
this.options.add(option);
|
||||
}
|
||||
|
||||
private void addOption(String key, String val) {
|
||||
this.options.add(key);
|
||||
this.options.add(val);
|
||||
}
|
||||
|
||||
private void addSource(String className, String source) {
|
||||
addSource(new StringSource(className, source));
|
||||
}
|
||||
|
||||
private void addSource(JavaFileObject javaFileObject) {
|
||||
compilationUnits.add(javaFileObject);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,32 +117,17 @@ public class AsgcCompiler {
|
||||
* @param className 类名
|
||||
* @param sourceCode 源代码
|
||||
*/
|
||||
public Map<String,byte[]> compile(String className, String sourceCode) {
|
||||
DynamicJavaFileManager javaFileManager = new DynamicJavaFileManager(compiler.getStandardFileManager(collector, null, null));
|
||||
CharSequenceJavaFileObject javaFileObject = new CharSequenceJavaFileObject(className, sourceCode);
|
||||
Boolean result = compiler.getTask(null, javaFileManager, collector, getOptions(), null, Arrays.asList(javaFileObject)).call();
|
||||
public Class<?> compile(String className, String sourceCode) throws ClassNotFoundException {
|
||||
JavaFileManager javaFileManager = new DynamicJavaFileManager(standardJavaFileManager, dynamicClassLoader);
|
||||
Boolean result = javaCompiler.getTask(null, javaFileManager, collector, getOptions(), null, Lists.newArrayList(new StringSource(className, sourceCode))).call();
|
||||
if (!result) {
|
||||
collector.getDiagnostics().forEach(item -> log.error(item.toString()));
|
||||
}
|
||||
|
||||
Map<String, byte[]> ret = new HashMap<>();
|
||||
for (Map.Entry<String, CharSequenceJavaFileObject> e : javaFileManager.fileObjects.entrySet()) {
|
||||
ret.put(e.getKey(), e.getValue().getByteCode());
|
||||
Map<String, Class<?>> map = dynamicClassLoader.getClasses();
|
||||
if (CollectionUtil.isEmpty(map)) {
|
||||
return null;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 编译并加载类
|
||||
* @param pkg
|
||||
* @param className
|
||||
* @param sourceCode
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public <T> Class<T> compileAndLoadClass(String pkg, String className, String sourceCode) throws ClassNotFoundException {
|
||||
Map<String,byte[]> byteCodeMap = compile(className, sourceCode);
|
||||
classLoader.addByteCode(byteCodeMap);
|
||||
return (Class<T>)classLoader.loadClass(pkg + "." + className);
|
||||
return map.values().stream().filter(c -> c.getSimpleName().equals(className)).findFirst().orElseGet(null);
|
||||
}
|
||||
}
|
||||
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop.compiler;
|
||||
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.NestingKind;
|
||||
import javax.tools.JavaFileObject;
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: wen.y
|
||||
* @date: 2022/8/25
|
||||
*/
|
||||
public class CustomJavaFileObject implements JavaFileObject {
|
||||
private final String binaryName;
|
||||
private final URI uri;
|
||||
private final String name;
|
||||
|
||||
public CustomJavaFileObject(String binaryName, URI uri) {
|
||||
this.uri = uri;
|
||||
this.binaryName = binaryName;
|
||||
name = uri.getPath() == null ? uri.getSchemeSpecificPart() : uri.getPath(); // for FS based URI the path is not null, for JAR URI the scheme specific part is not null
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI toUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream openInputStream() throws IOException {
|
||||
return uri.toURL().openStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream openOutputStream() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Reader openReader(boolean ignoreEncodingErrors) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Writer openWriter() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastModified() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Kind getKind() {
|
||||
return Kind.CLASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNameCompatible(String simpleName, Kind kind) {
|
||||
String baseName = simpleName + kind.extension;
|
||||
return kind.equals(getKind())
|
||||
&& (baseName.equals(getName())
|
||||
|| getName().endsWith("/" + baseName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NestingKind getNestingKind() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Modifier getAccessLevel() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public String binaryName() {
|
||||
return binaryName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getName() + "[" + this.toUri() + "]";
|
||||
}
|
||||
}
|
||||
+28
-27
@@ -21,47 +21,48 @@
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop.compiler;
|
||||
|
||||
import fun.asgc.neutrino.core.util.CollectionUtil;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/8/17
|
||||
* @author: wen.y
|
||||
* @date: 2022/8/25
|
||||
*/
|
||||
public class ByteCodeClassLoader extends ClassLoader {
|
||||
private Map<String, byte[]> byteCodeMap = new ConcurrentHashMap<>();
|
||||
static {
|
||||
registerAsParallelCapable();
|
||||
public class DynamicClassLoader extends ClassLoader {
|
||||
private final Map<String, MemoryByteCode> byteCodes = new HashMap<>();
|
||||
|
||||
public DynamicClassLoader(ClassLoader classLoader) {
|
||||
super(classLoader);
|
||||
}
|
||||
|
||||
public ByteCodeClassLoader() {
|
||||
super(getParentClassLoader());
|
||||
}
|
||||
|
||||
protected static ClassLoader getParentClassLoader() {
|
||||
ClassLoader ret = Thread.currentThread().getContextClassLoader();
|
||||
return ret != null ? ret : ByteCodeClassLoader.class.getClassLoader();
|
||||
public void registerCompiledSource(MemoryByteCode byteCode) {
|
||||
byteCodes.put(byteCode.getClassName(), byteCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
byte[] bytes = byteCodeMap.get(name);
|
||||
if (null != bytes) {
|
||||
Class<?> ret = defineClass(name, bytes, 0, bytes.length);
|
||||
byteCodeMap.remove(name);
|
||||
return ret;
|
||||
MemoryByteCode byteCode = byteCodes.get(name);
|
||||
if (byteCode == null) {
|
||||
return super.findClass(name);
|
||||
}
|
||||
return super.findClass(name);
|
||||
|
||||
return super.defineClass(name, byteCode.getByteCode(), 0, byteCode.getByteCode().length);
|
||||
}
|
||||
|
||||
public void addByteCode(Map<String, byte[]> byteCodeMap) {
|
||||
if (!CollectionUtil.isEmpty(byteCodeMap)) {
|
||||
for (Map.Entry<String, byte[]> e : byteCodeMap.entrySet()) {
|
||||
this.byteCodeMap.putIfAbsent(e.getKey(), e.getValue());
|
||||
}
|
||||
public Map<String, Class<?>> getClasses() throws ClassNotFoundException {
|
||||
Map<String, Class<?>> classes = new HashMap<String, Class<?>>();
|
||||
for (MemoryByteCode byteCode : byteCodes.values()) {
|
||||
classes.put(byteCode.getClassName(), findClass(byteCode.getClassName()));
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
public Map<String, byte[]> getByteCodes() {
|
||||
Map<String, byte[]> result = new HashMap<String, byte[]>(byteCodes.size());
|
||||
for (Map.Entry<String, MemoryByteCode> entry : byteCodes.entrySet()) {
|
||||
result.put(entry.getKey(), entry.getValue().getByteCode());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+101
-18
@@ -21,13 +21,9 @@
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop.compiler;
|
||||
|
||||
import javax.tools.FileObject;
|
||||
import javax.tools.ForwardingJavaFileManager;
|
||||
import javax.tools.JavaFileManager;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.*;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -35,25 +31,112 @@ import java.util.Map;
|
||||
* @date: 2022/8/17
|
||||
*/
|
||||
public class DynamicJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> {
|
||||
public Map<String, CharSequenceJavaFileObject> fileObjects = new HashMap<>();
|
||||
private static final String[] superLocationNames = { StandardLocation.PLATFORM_CLASS_PATH.name(),
|
||||
/** JPMS StandardLocation.SYSTEM_MODULES **/
|
||||
"SYSTEM_MODULES" };
|
||||
private final PackageInternalsFinder finder;
|
||||
private final DynamicClassLoader classLoader;
|
||||
private final List<MemoryByteCode> byteCodes = new ArrayList<MemoryByteCode>();
|
||||
|
||||
public DynamicJavaFileManager(JavaFileManager fileManager) {
|
||||
public DynamicJavaFileManager(JavaFileManager fileManager, DynamicClassLoader classLoader) {
|
||||
super(fileManager);
|
||||
this.classLoader = classLoader;
|
||||
this.finder = new PackageInternalsFinder(classLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String qualifiedClassName, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
|
||||
CharSequenceJavaFileObject javaFileObject = new CharSequenceJavaFileObject(qualifiedClassName, kind);
|
||||
this.fileObjects.put(qualifiedClassName, javaFileObject);
|
||||
return javaFileObject;
|
||||
}
|
||||
public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className,
|
||||
JavaFileObject.Kind kind, FileObject sibling) throws IOException {
|
||||
|
||||
for (MemoryByteCode byteCode : byteCodes) {
|
||||
if (byteCode.getClassName().equals(className)) {
|
||||
return byteCode;
|
||||
}
|
||||
}
|
||||
|
||||
MemoryByteCode innerClass = new MemoryByteCode(className);
|
||||
byteCodes.add(innerClass);
|
||||
classLoader.registerCompiledSource(innerClass);
|
||||
return innerClass;
|
||||
|
||||
}
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForInput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind) throws IOException {
|
||||
JavaFileObject javaFileObject = fileObjects.get(className);
|
||||
if (javaFileObject == null) {
|
||||
javaFileObject = super.getJavaFileForInput(location, className, kind);
|
||||
public ClassLoader getClassLoader(JavaFileManager.Location location) {
|
||||
return classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String inferBinaryName(Location location, JavaFileObject file) {
|
||||
if (file instanceof CustomJavaFileObject) {
|
||||
return ((CustomJavaFileObject) file).binaryName();
|
||||
} else {
|
||||
/**
|
||||
* if it's not CustomJavaFileObject, then it's coming from standard file manager
|
||||
* - let it handle the file
|
||||
*/
|
||||
return super.inferBinaryName(location, file);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<JavaFileObject> list(Location location, String packageName, Set<JavaFileObject.Kind> kinds,
|
||||
boolean recurse) throws IOException {
|
||||
if (location instanceof StandardLocation) {
|
||||
String locationName = ((StandardLocation) location).name();
|
||||
for (String name : superLocationNames) {
|
||||
if (name.equals(locationName)) {
|
||||
return super.list(location, packageName, kinds, recurse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// merge JavaFileObjects from specified ClassLoader
|
||||
if (location == StandardLocation.CLASS_PATH && kinds.contains(JavaFileObject.Kind.CLASS)) {
|
||||
return new IterableJoin<JavaFileObject>(super.list(location, packageName, kinds, recurse),
|
||||
finder.find(packageName));
|
||||
}
|
||||
|
||||
return super.list(location, packageName, kinds, recurse);
|
||||
}
|
||||
|
||||
static class IterableJoin<T> implements Iterable<T> {
|
||||
private final Iterable<T> first, next;
|
||||
|
||||
public IterableJoin(Iterable<T> first, Iterable<T> next) {
|
||||
this.first = first;
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return new IteratorJoin<T>(first.iterator(), next.iterator());
|
||||
}
|
||||
}
|
||||
|
||||
static class IteratorJoin<T> implements Iterator<T> {
|
||||
private final Iterator<T> first, next;
|
||||
|
||||
public IteratorJoin(Iterator<T> first, Iterator<T> next) {
|
||||
this.first = first;
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return first.hasNext() || next.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
if (first.hasNext()) {
|
||||
return first.next();
|
||||
}
|
||||
return next.next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("remove");
|
||||
}
|
||||
return javaFileObject;
|
||||
}
|
||||
}
|
||||
|
||||
+27
-31
@@ -21,57 +21,53 @@
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop.compiler;
|
||||
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.SimpleJavaFileObject;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/8/17
|
||||
* @date: 2022/8/25
|
||||
*/
|
||||
class CharSequenceJavaFileObject extends SimpleJavaFileObject {
|
||||
private final CharSequence sourceCode;
|
||||
private ByteArrayOutputStream out;
|
||||
public class MemoryByteCode extends SimpleJavaFileObject {
|
||||
private static final char PKG_SEPARATOR = '.';
|
||||
private static final char DIR_SEPARATOR = '/';
|
||||
private static final String CLASS_FILE_SUFFIX = ".class";
|
||||
|
||||
public CharSequenceJavaFileObject(URI uri, Kind kind) {
|
||||
super(uri, kind);
|
||||
this.sourceCode = null;
|
||||
private ByteArrayOutputStream byteArrayOutputStream;
|
||||
|
||||
public MemoryByteCode(String className) {
|
||||
super(URI.create("byte:///" + className.replace(PKG_SEPARATOR, DIR_SEPARATOR)
|
||||
+ Kind.CLASS.extension), Kind.CLASS);
|
||||
}
|
||||
|
||||
public CharSequenceJavaFileObject(String name, String source) {
|
||||
super(URI.create(name + JavaFileObject.Kind.SOURCE.extension), JavaFileObject.Kind.SOURCE);
|
||||
this.sourceCode = source;
|
||||
}
|
||||
|
||||
public CharSequenceJavaFileObject(String name, JavaFileObject.Kind kind) {
|
||||
super(URI.create(name + kind.extension), kind);
|
||||
this.sourceCode = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
|
||||
if (this.sourceCode == null) {
|
||||
throw new IllegalStateException("源代码不能为空!");
|
||||
}
|
||||
return sourceCode;
|
||||
public MemoryByteCode(String className, ByteArrayOutputStream byteArrayOutputStream)
|
||||
throws URISyntaxException {
|
||||
this(className);
|
||||
this.byteArrayOutputStream = byteArrayOutputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream openOutputStream() throws IOException {
|
||||
if (null == out) {
|
||||
this.out = new ByteArrayOutputStream();
|
||||
if (byteArrayOutputStream == null) {
|
||||
byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
}
|
||||
return out;
|
||||
return byteArrayOutputStream;
|
||||
}
|
||||
|
||||
public byte[] getByteCode() {
|
||||
if (null == out) {
|
||||
return null;
|
||||
}
|
||||
return out.toByteArray();
|
||||
return byteArrayOutputStream.toByteArray();
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
String className = getName();
|
||||
className = className.replace(DIR_SEPARATOR, PKG_SEPARATOR);
|
||||
className = className.substring(1, className.indexOf(CLASS_FILE_SUFFIX));
|
||||
return className;
|
||||
}
|
||||
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop.compiler;
|
||||
|
||||
import javax.tools.JavaFileObject;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.JarURLConnection;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.jar.JarEntry;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/8/25
|
||||
*/
|
||||
public class PackageInternalsFinder {
|
||||
private final ClassLoader classLoader;
|
||||
private static final String CLASS_FILE_EXTENSION = ".class";
|
||||
|
||||
public PackageInternalsFinder(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
public List<JavaFileObject> find(String packageName) throws IOException {
|
||||
String javaPackageName = packageName.replaceAll("\\.", "/");
|
||||
|
||||
List<JavaFileObject> result = new ArrayList<>();
|
||||
|
||||
Enumeration<URL> urlEnumeration = classLoader.getResources(javaPackageName);
|
||||
while (urlEnumeration.hasMoreElements()) { // one URL for each jar on the classpath that has the given package
|
||||
URL packageFolderURL = urlEnumeration.nextElement();
|
||||
result.addAll(listUnder(packageName, packageFolderURL));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Collection<JavaFileObject> listUnder(String packageName, URL packageFolderURL) {
|
||||
File directory = new File(decode(packageFolderURL.getFile()));
|
||||
if (directory.isDirectory()) { // browse local .class files - useful for local execution
|
||||
return processDir(packageName, directory);
|
||||
} else { // browse a jar file
|
||||
return processJar(packageFolderURL);
|
||||
} // maybe there can be something else for more involved class loaders
|
||||
}
|
||||
|
||||
private List<JavaFileObject> processJar(URL packageFolderURL) {
|
||||
List<JavaFileObject> result = new ArrayList<JavaFileObject>();
|
||||
try {
|
||||
String jarUri = packageFolderURL.toExternalForm().substring(0, packageFolderURL.toExternalForm().lastIndexOf("!/"));
|
||||
|
||||
JarURLConnection jarConn = (JarURLConnection) packageFolderURL.openConnection();
|
||||
String rootEntryName = jarConn.getEntryName();
|
||||
int rootEnd = rootEntryName.length() + 1;
|
||||
|
||||
Enumeration<JarEntry> entryEnum = jarConn.getJarFile().entries();
|
||||
while (entryEnum.hasMoreElements()) {
|
||||
JarEntry jarEntry = entryEnum.nextElement();
|
||||
String name = jarEntry.getName();
|
||||
if (name.startsWith(rootEntryName) && name.indexOf('/', rootEnd) == -1 && name.endsWith(CLASS_FILE_EXTENSION)) {
|
||||
URI uri = URI.create(jarUri + "!/" + name);
|
||||
String binaryName = name.replaceAll("/", ".");
|
||||
binaryName = binaryName.replaceAll(CLASS_FILE_EXTENSION + "$", "");
|
||||
|
||||
result.add(new CustomJavaFileObject(binaryName, uri));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Wasn't able to open " + packageFolderURL + " as a jar file", e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<JavaFileObject> processDir(String packageName, File directory) {
|
||||
List<JavaFileObject> result = new ArrayList<JavaFileObject>();
|
||||
|
||||
File[] childFiles = directory.listFiles();
|
||||
if (childFiles != null) {
|
||||
for (File childFile : childFiles) {
|
||||
if (childFile.isFile()) {
|
||||
// We only want the .class files.
|
||||
if (childFile.getName().endsWith(CLASS_FILE_EXTENSION)) {
|
||||
String binaryName = packageName + "." + childFile.getName();
|
||||
binaryName = binaryName.replaceAll(CLASS_FILE_EXTENSION + "$", "");
|
||||
|
||||
result.add(new CustomJavaFileObject(binaryName, childFile.toURI()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private String decode(String filePath) {
|
||||
try {
|
||||
return URLDecoder.decode(filePath, "utf-8");
|
||||
} catch (Exception e) {
|
||||
// ignore, return original string
|
||||
}
|
||||
|
||||
return filePath;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop.compiler;
|
||||
|
||||
import javax.tools.SimpleJavaFileObject;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/8/25
|
||||
*/
|
||||
public class StringSource extends SimpleJavaFileObject {
|
||||
private final String contents;
|
||||
|
||||
public StringSource(String className, String contents) {
|
||||
super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
|
||||
this.contents = contents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
|
||||
return contents;
|
||||
}
|
||||
|
||||
}
|
||||
+5
-5
@@ -43,7 +43,7 @@ public class AsgcCompilerTest {
|
||||
"\t\tSystem.out.println(\"hello\");\n" +
|
||||
"\t}\n" +
|
||||
"}\n";
|
||||
Class clazz = compiler.compileAndLoadClass("a.b","Hello", code);
|
||||
Class clazz = compiler.compile("Hello", code);
|
||||
Method method = ReflectUtil.getMethods(clazz).stream().filter(m -> m.getName().equals("hello")).findFirst().get();
|
||||
Object instance = clazz.newInstance();
|
||||
method.invoke(instance);
|
||||
@@ -59,7 +59,7 @@ public class AsgcCompilerTest {
|
||||
"\t\tSystem.out.println(\"熊猫正在吃\" + food);\n" +
|
||||
"\t}\n" +
|
||||
"}\n";
|
||||
Class clazz = compiler.compileAndLoadClass("a.b","Panda", code);
|
||||
Class clazz = compiler.compile("Panda", code);
|
||||
Method method = ReflectUtil.getMethods(clazz).stream().filter(m -> m.getName().equals("eat")).findFirst().get();
|
||||
Object instance = clazz.newInstance();
|
||||
method.invoke(instance, "竹子");
|
||||
@@ -76,7 +76,7 @@ public class AsgcCompilerTest {
|
||||
"\t\tSystem.out.println(\"收音机播放\");\n" +
|
||||
"\t}\n" +
|
||||
"}\n";
|
||||
Class clazz = compiler.compileAndLoadClass("a.b","RadioPlayer", code);
|
||||
Class clazz = compiler.compile("RadioPlayer", code);
|
||||
Method method = ReflectUtil.getMethods(clazz).stream().filter(m -> m.getName().equals("play")).findFirst().get();
|
||||
Object instance = clazz.newInstance();
|
||||
method.invoke(instance);
|
||||
@@ -97,7 +97,7 @@ public class AsgcCompilerTest {
|
||||
"\t\tSystem.out.println(\"收音机播放\");\n" +
|
||||
"\t}\n" +
|
||||
"}\n";
|
||||
Class clazz = compiler.compileAndLoadClass("a.b","RadioPlayer", code);
|
||||
Class clazz = compiler.compile("RadioPlayer", code);
|
||||
Method method = ReflectUtil.getMethods(clazz).stream().filter(m -> m.getName().equals("play")).findFirst().get();
|
||||
Object instance = clazz.newInstance();
|
||||
method.invoke(instance);
|
||||
@@ -113,7 +113,7 @@ public class AsgcCompilerTest {
|
||||
"}\n";
|
||||
AsgcCompiler compiler = new AsgcCompiler();
|
||||
try {
|
||||
Class clazz = compiler.compileAndLoadClass("fun.asgc.test", "Calc", code);
|
||||
Class clazz = compiler.compile( "Calc", code);
|
||||
Method method = ReflectUtil.getMethods(clazz).stream().filter(m -> m.getName().equals("invoke")).findFirst().get();
|
||||
return method.invoke(null);
|
||||
} catch (ClassNotFoundException|IllegalAccessException|InvocationTargetException e) {
|
||||
|
||||
Reference in New Issue
Block a user