-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
3,661 additions
and
61 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,9 +15,10 @@ | |
|
||
|
||
/** | ||
* 服务的主入口 | ||
* | ||
* @author superzhan | ||
* Author: [email protected] | ||
* Copyright: http://codeyourlife.cn | ||
* Platform: Win10 Jdk8 | ||
* Date: 2020/1/8 | ||
*/ | ||
public final class WebIdeApplication { | ||
|
||
|
115 changes: 115 additions & 0 deletions
115
src/main/java/cn/codeyourlife/compile/StringSourceCompiler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
package cn.codeyourlife.compile; | ||
|
||
import javax.tools.*; | ||
import java.io.ByteArrayOutputStream; | ||
import java.io.IOException; | ||
import java.io.OutputStream; | ||
import java.net.URI; | ||
import java.util.Arrays; | ||
import java.util.Map; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
|
||
public class StringSourceCompiler { | ||
private static Map<String, JavaFileObject> fileObjectMap = new ConcurrentHashMap<>(); | ||
|
||
/** 使用 Pattern 预编译功能 */ | ||
private static Pattern CLASS_PATTERN = Pattern.compile("class\\s+([$_a-zA-Z][$_a-zA-Z0-9]*)\\s*"); | ||
|
||
public static byte[] compile(String source, DiagnosticCollector<JavaFileObject> compileCollector) { | ||
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); | ||
JavaFileManager javaFileManager = | ||
new TmpJavaFileManager(compiler.getStandardFileManager(compileCollector, null, null)); | ||
|
||
// 从源码字符串中匹配类名 | ||
Matcher matcher = CLASS_PATTERN.matcher(source); | ||
String className; | ||
if (matcher.find()) { | ||
className = matcher.group(1); | ||
} else { | ||
throw new IllegalArgumentException("No valid class"); | ||
} | ||
|
||
// 把源码字符串构造成JavaFileObject,供编译使用 | ||
JavaFileObject sourceJavaFileObject = new TmpJavaFileObject(className, source); | ||
|
||
Boolean result = compiler.getTask(null, javaFileManager, compileCollector, | ||
null, null, Arrays.asList(sourceJavaFileObject)).call(); | ||
|
||
JavaFileObject bytesJavaFileObject = fileObjectMap.get(className); | ||
if (result && bytesJavaFileObject != null) { | ||
return ((TmpJavaFileObject) bytesJavaFileObject).getCompiledBytes(); | ||
} | ||
return null; | ||
} | ||
|
||
/** | ||
* 管理JavaFileObject对象的工具 | ||
*/ | ||
public static class TmpJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> { | ||
protected TmpJavaFileManager(JavaFileManager fileManager) { | ||
super(fileManager); | ||
} | ||
|
||
@Override | ||
public JavaFileObject getJavaFileForInput(Location location, String className, JavaFileObject.Kind kind) throws IOException { | ||
JavaFileObject javaFileObject = fileObjectMap.get(className); | ||
if (javaFileObject == null) { | ||
return super.getJavaFileForInput(location, className, kind); | ||
} | ||
return javaFileObject; | ||
} | ||
|
||
@Override | ||
public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { | ||
JavaFileObject javaFileObject = new TmpJavaFileObject(className, kind); | ||
fileObjectMap.put(className, javaFileObject); | ||
return javaFileObject; | ||
} | ||
} | ||
|
||
/** | ||
* 用来封装表示源码与字节码的对象 | ||
*/ | ||
public static class TmpJavaFileObject extends SimpleJavaFileObject { | ||
private String source; | ||
private ByteArrayOutputStream outputStream; | ||
|
||
/** | ||
* 构造用来存储源代码的JavaFileObject | ||
* 需要传入源码source,然后调用父类的构造方法创建kind = Kind.SOURCE的JavaFileObject对象 | ||
*/ | ||
public TmpJavaFileObject(String name, String source) { | ||
super(URI.create("String:///" + name + Kind.SOURCE.extension), Kind.SOURCE); | ||
this.source = source; | ||
} | ||
|
||
/** | ||
* 构造用来存储字节码的JavaFileObject | ||
* 需要传入kind,即我们想要构建一个存储什么类型文件的JavaFileObject | ||
*/ | ||
public TmpJavaFileObject(String name, Kind kind) { | ||
super(URI.create("String:///" + name + Kind.SOURCE.extension), kind); | ||
this.source = null; | ||
} | ||
|
||
@Override | ||
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { | ||
if (source == null) { | ||
throw new IllegalArgumentException("source == null"); | ||
} | ||
return source; | ||
} | ||
|
||
@Override | ||
public OutputStream openOutputStream() throws IOException { | ||
outputStream = new ByteArrayOutputStream(); | ||
return outputStream; | ||
} | ||
|
||
public byte[] getCompiledBytes() { | ||
return outputStream.toByteArray(); | ||
} | ||
} | ||
} |
31 changes: 31 additions & 0 deletions
31
src/main/java/cn/codeyourlife/controller/RunCodeController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package cn.codeyourlife.controller; | ||
|
||
/** | ||
* Author: [email protected] | ||
* Copyright: http://codeyourlife.cn | ||
* Platform: Win10 Jdk8 | ||
* Date: 2020/1/8 | ||
*/ | ||
public class RunCodeController { | ||
private static final String defaultSource = "public class Run {\n" | ||
+ " public static void main(String[] args) {\n" | ||
+ " \n" | ||
+ " }\n" | ||
+ "}"; | ||
|
||
// public String entry(Model model) { | ||
// model.addAttribute("lastSource", defaultSource); | ||
// return "ide"; | ||
// } | ||
// | ||
// public String runCode(@RequestParam("source") String source, | ||
// @RequestParam("systemIn") String systemIn, Model model) { | ||
// String runResult = executeStringSourceService.execute(source, systemIn); | ||
// runResult = runResult.replaceAll(System.lineSeparator(), "<br/>"); // 处理html中换行的问题 | ||
// | ||
// model.addAttribute("lastSource", source); | ||
// model.addAttribute("lastSystemIn", systemIn); | ||
// model.addAttribute("runResult", runResult); | ||
// return "ide"; | ||
// } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package cn.codeyourlife.execute; | ||
|
||
public class ByteUtils { | ||
|
||
public static int byte2Int(byte[] b, int start, int len) { | ||
int res = 0; | ||
int end = start + len; | ||
for (int i = start; i < end; i++) { | ||
int cur = ((int) b[i]) & 0xff; | ||
cur <<= (--len) * 8; | ||
res += cur; | ||
} | ||
return res; | ||
} | ||
|
||
public static byte[] int2Byte(int num, int len) { | ||
byte[] b = new byte[len]; | ||
for (int i = 0; i < len; i++) { | ||
b[len - i - 1] = (byte) ((num >> (8 * i)) & 0xff); | ||
} | ||
return b; | ||
} | ||
|
||
public static String byte2String(byte[] b, int start, int len) { | ||
return new String(b, start, len); | ||
} | ||
|
||
public static byte[] string2Byte(String str) { | ||
return str.getBytes(); | ||
} | ||
|
||
public static byte[] byteReplace(byte[] oldBytes, int offset, int len, byte[] replaceBytes) { | ||
byte[] newBytes = new byte[oldBytes.length + replaceBytes.length - len]; | ||
System.arraycopy(oldBytes, 0, newBytes, 0, offset); | ||
System.arraycopy(replaceBytes, 0, newBytes, offset, replaceBytes.length); | ||
System.arraycopy(oldBytes, offset + len, newBytes, offset + replaceBytes.length, | ||
oldBytes.length - offset - len); | ||
return newBytes; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package cn.codeyourlife.execute; | ||
|
||
public class ClassModifier { | ||
/** | ||
* Class文件中常量池的起始偏移 | ||
*/ | ||
private static final int CONSTANT_POOL_COUNT_INDEX = 8; | ||
|
||
/** | ||
* CONSTANT_UTF8_INFO常量的tag | ||
*/ | ||
private static final int CONSTANT_UTF8_INFO = 1; | ||
|
||
/** | ||
* 常量池中11种常量的长度,CONSTANT_ITEM_LENGTH[tag]表示它的长度 | ||
*/ | ||
private static final int[] CONSTANT_ITEM_LENGTH = {-1, -1, -1, 5, 5, 9, 9, 3, 3, 5, 5, 5, 5}; | ||
|
||
/** | ||
* 1个和2个字节的符号数,用来在classByte数组中取tag和len | ||
* tag用u1个字节表示 | ||
* len用u2个字节表示 | ||
*/ | ||
private static final int u1 = 1; | ||
private static final int u2 = 2; | ||
|
||
/** | ||
* 要被修改的字节码文件 | ||
*/ | ||
private byte[] classByte; | ||
|
||
public ClassModifier(byte[] classByte) { | ||
this.classByte = classByte; | ||
} | ||
|
||
/** | ||
* 从0x00000008开始向后取2个字节,表示的是常量池中常量的个数 | ||
* @return 常量池中常量的个数 | ||
*/ | ||
public int getConstantPoolCount() { | ||
return ByteUtils.byte2Int(classByte, CONSTANT_POOL_COUNT_INDEX, u2); | ||
} | ||
|
||
/** | ||
* 字节码修改器,替换字节码常量池中 oldStr 为 newStr | ||
* @param oldStr | ||
* @param newStr | ||
* @return 修改后的字节码字节数组 | ||
*/ | ||
public byte[] modifyUTF8Constant(String oldStr, String newStr) { | ||
int cpc = getConstantPoolCount(); | ||
int offset = CONSTANT_POOL_COUNT_INDEX + u2; // 真实的常量起始位置 | ||
for (int i = 1; i < cpc; i++) { | ||
int tag = ByteUtils.byte2Int(classByte, offset, u1); | ||
if (tag == CONSTANT_UTF8_INFO) { | ||
int len = ByteUtils.byte2Int(classByte, offset + u1, u2); | ||
offset += u1 + u2; | ||
String str = ByteUtils.byte2String(classByte, offset, len); | ||
if (str.equals(oldStr)) { | ||
byte[] strReplaceBytes = ByteUtils.string2Byte(newStr); | ||
byte[] intReplaceBytes = ByteUtils.int2Byte(strReplaceBytes.length, u2); | ||
// 替换新的字符串的长度 | ||
classByte = ByteUtils.byteReplace(classByte, offset - u2, u2, intReplaceBytes); | ||
// 替换字符串本身 | ||
classByte = ByteUtils.byteReplace(classByte, offset, len, strReplaceBytes); | ||
return classByte; // 就一个地方需要改,改完就可以返回了 | ||
} else { | ||
offset += len; | ||
} | ||
} else { | ||
offset += CONSTANT_ITEM_LENGTH[tag]; | ||
} | ||
} | ||
return classByte; | ||
} | ||
|
||
} |
Oops, something went wrong.