Skip to content

Commit

Permalink
[Add] 初步完成架构
Browse files Browse the repository at this point in the history
  • Loading branch information
counten committed Jan 8, 2020
1 parent c232386 commit c533738
Show file tree
Hide file tree
Showing 18 changed files with 3,661 additions and 61 deletions.
1 change: 1 addition & 0 deletions .idea/compiler.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

64 changes: 60 additions & 4 deletions .idea/workspace.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions src/main/java/cn/codeyourlife/WebIdeApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@


/**
* 服务的主入口
*
* @author superzhan
* Author: [email protected]
* Copyright: http://codeyourlife.cn
* Platform: Win10 Jdk8
* Date: 2020/1/8
*/
public final class WebIdeApplication {

Expand Down
115 changes: 115 additions & 0 deletions src/main/java/cn/codeyourlife/compile/StringSourceCompiler.java
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 src/main/java/cn/codeyourlife/controller/RunCodeController.java
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";
// }
}
40 changes: 40 additions & 0 deletions src/main/java/cn/codeyourlife/execute/ByteUtils.java
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;
}
}
77 changes: 77 additions & 0 deletions src/main/java/cn/codeyourlife/execute/ClassModifier.java
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;
}

}
Loading

0 comments on commit c533738

Please sign in to comment.