Skip to content

Commit

Permalink
file system decoupled in compilation progress
Browse files Browse the repository at this point in the history
  • Loading branch information
pfmiles committed Aug 15, 2013
1 parent 1503e5a commit 76f3626
Show file tree
Hide file tree
Showing 11 changed files with 142 additions and 72 deletions.
18 changes: 16 additions & 2 deletions src/main/java/com/github/pfmiles/dropincc/Lang.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ public class Lang implements Serializable {
// warn messages generated while analyzing
private String warnings;

// compilation encoding
private String encoding = "UTF-8";

/**
* Create language object with a name
*
Expand Down Expand Up @@ -94,7 +97,8 @@ public TokenDef newToken(String regExpr) {
*/
public ConstructingGrule defineGrule(Object... eles) {
if (eles == null || eles.length == 0)
throw new DropinccException("Could not add empty grammar rule, if you want to add a rule alternative that matches nothing, use CC.NOTHING.");
throw new DropinccException(
"Could not add empty grammar rule, if you want to add a rule alternative that matches nothing, use CC.NOTHING.");
Grule g = new Grule(this.grules.size());
Element[] elements = Util.filterProductionEles(eles);
g.getAlts().add(new Alternative(elements));
Expand All @@ -108,7 +112,7 @@ public ConstructingGrule defineGrule(Object... eles) {
*/
public Exe compile() {
checkIfAnyEmptyGrule(this.grules);
AnalyzedLang cl = new AnalyzedLang(this.name, this.tokens, this.grules, this.whiteSpaceSensitive);
AnalyzedLang cl = new AnalyzedLang(this.name, this.tokens, this.grules, this.whiteSpaceSensitive, this.encoding);
cl.compile();
this.debugMsgs = cl.getDebugMsgs();
this.warnings = cl.getWarnings();
Expand Down Expand Up @@ -196,4 +200,14 @@ public String getWarnings() {
return warnings;
}

/**
* Set the encoding used during the compilation progress. Defaults to
* 'UTF-8' if not set.
*
* @param encoding
*/
public void setEncoding(String encoding) {
this.encoding = encoding;
}

}
12 changes: 9 additions & 3 deletions src/main/java/com/github/pfmiles/dropincc/impl/AnalyzedLang.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,12 @@ public class AnalyzedLang {
private String debugMsgs;
private String warnings;

public AnalyzedLang(String name, List<TokenDef> tokens, List<Grule> grules, boolean whitespaceSensitive) {
// compilation encoding
private String encoding;

public AnalyzedLang(String name, List<TokenDef> tokens, List<Grule> grules, boolean whitespaceSensitive, String encoding) {
this.langName = name;
this.encoding = encoding;
// build token -> tokenType mapping
this.tokens = tokens;
// Gathering instant tokenDefs...
Expand Down Expand Up @@ -128,7 +132,8 @@ public void compile() {
this.tokenPatterns = compiledTokenUnit.getRight();

// 2.resolving the parser ast
TypeMappingParam typeMappingParam = new TypeMappingParam(this.tokenTypeMapping, this.gruleTypeMapping, specialTypeMapping, this.kleeneTypeMapping);
TypeMappingParam typeMappingParam = new TypeMappingParam(this.tokenTypeMapping, this.gruleTypeMapping, specialTypeMapping,
this.kleeneTypeMapping);
// at this point, 'gruleTypeMapping' contains all grule -> type
// mappings, including generated grules
this.ruleTypeToAlts = ParserCompiler.buildRuleTypeToAlts(typeMappingParam);
Expand Down Expand Up @@ -161,7 +166,8 @@ public void compile() {
this.parserCode = parserCodeGenResult.getCode();

// 7.compile and maintain the code in a separate classloader
CompilationResult result = HotCompileUtil.compile("com.github.pfmiles.dropincc.impl.runtime.gen." + this.langName, this.parserCode);
CompilationResult result = HotCompileUtil.compile("com.github.pfmiles.dropincc.impl.runtime.gen." + this.langName, this.parserCode,
this.encoding);
if (!result.isSucceed()) {
throw new DropinccException("Parser code compilation failed. Reason: " + result.getErrMsg());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,7 @@
******************************************************************************/
package com.github.pfmiles.dropincc.impl.hotcompile;

import java.io.File;
import java.io.FileInputStream;

import com.github.pfmiles.dropincc.DropinccException;
import com.github.pfmiles.dropincc.impl.util.ByteAppender;
import com.github.pfmiles.dropincc.impl.util.Util;
import java.util.Map;

/**
* Load hot compiled code from default directory.
Expand All @@ -25,37 +20,16 @@
*/
public class HotCompileClassLoader extends ClassLoader {

public HotCompileClassLoader(ClassLoader parent) {
private Map<String, JavaMemCls> inMemCls;

public HotCompileClassLoader(ClassLoader parent, Map<String, JavaMemCls> clses) {
super(parent);
this.inMemCls = clses;
}

protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] b = loadClassData(name);
byte[] b = this.inMemCls.get(name).getClsBytes();
return defineClass(name, b, 0, b.length);
}

// read class data from default hot compilation directory
private byte[] loadClassData(String name) {
File src = new File(HotCompileConstants.TARGETDIR + Util.PATH_SEP + name.replace(".", Util.PATH_SEP) + ".class");
FileInputStream fis = null;
try {
fis = new FileInputStream(src);
ByteAppender ba = new ByteAppender();
byte[] buf = new byte[1024];
int count = fis.read(buf);
while (count != -1) {
ba.append(buf, 0, count);
count = fis.read(buf);
}
return ba.toByteArray();
} catch (Exception e) {
throw new DropinccException(e);
} finally {
try {
fis.close();
} catch (Exception e) {
throw new DropinccException(e);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,4 @@ public interface HotCompileConstants {
*/
String CLASSPATH = Util.getClassPath();

/**
* hot compilation target directory
*/
String TARGETDIR = Util.getTempDirWithFileSeparatorSuffix() + "dcHotCompile";
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,23 @@
******************************************************************************/
package com.github.pfmiles.dropincc.impl.hotcompile;

import java.io.File;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

import com.github.pfmiles.dropincc.DropinccException;
import com.github.pfmiles.dropincc.impl.util.Util;

/**
* Compiles code at runtime, using JDK1.6 compiler API.
Expand All @@ -43,17 +44,16 @@ public class HotCompileUtil {
* @return The resulting java class object and its corresponding class
* loader.
*/
public static CompilationResult compile(String qualifiedName, String sourceCode) {
public static CompilationResult compile(String qualifiedName, String sourceCode, String encoding) {
JavaStringSource source = new JavaStringSource(qualifiedName, sourceCode);
List<JavaStringSource> ss = Arrays.asList(source);
File dir = new File(HotCompileConstants.TARGETDIR);
if (!dir.exists())
dir.mkdirs();
List<String> options = Arrays.asList("-d", HotCompileConstants.TARGETDIR, "-classpath", HotCompileConstants.CLASSPATH);
List<String> options = Arrays.asList("-classpath", HotCompileConstants.CLASSPATH);
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = null;

JavaFileManager fileManager = null;
Map<String, JavaMemCls> clses = new HashMap<String, JavaMemCls>();
try {
fileManager = compiler.getStandardFileManager(null, Locale.getDefault(), Charset.forName("UTF-8"));
fileManager = new MemClsFileManager(compiler.getStandardFileManager(null, null, Charset.forName(encoding)), clses);
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
StringWriter out = new StringWriter();
CompilationTask task = compiler.getTask(out, fileManager, diagnostics, options, null, ss);
Expand All @@ -72,7 +72,7 @@ public static CompilationResult compile(String qualifiedName, String sourceCode)
}
}
// every parser class should be loaded by a new specific class loader
HotCompileClassLoader loader = new HotCompileClassLoader(HotCompileUtil.class.getClassLoader());
HotCompileClassLoader loader = new HotCompileClassLoader(Util.getParentClsLoader(), clses);
Class<?> cls = null;
try {
cls = loader.loadClass(qualifiedName);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.github.pfmiles.dropincc.impl.hotcompile;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;

import javax.tools.SimpleJavaFileObject;

/**
* Represents compiled java class files in the memory.
*
* @author pf-miles
*
*/
public class JavaMemCls extends SimpleJavaFileObject {

private ByteArrayOutputStream bos;

protected JavaMemCls(String name) {
super(URI.create("string:///" + name.replaceAll("\\.", "/") + Kind.CLASS.extension), Kind.CLASS);
this.bos = new ByteArrayOutputStream();
}

public byte[] getClsBytes() {
return this.bos.toByteArray();
}

public OutputStream openOutputStream() throws IOException {
return bos;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.github.pfmiles.dropincc.impl.hotcompile;

import java.io.IOException;
import java.util.Map;

import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import javax.tools.StandardJavaFileManager;

/**
* A forwarding file manager which is used to decouple the file system
* dependency. That is, the compilation process write compiled class files into
* memory instead of file system.
*
* @author pf-miles
*
*/
public class MemClsFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> {

private Map<String, JavaMemCls> destFiles;

protected MemClsFileManager(StandardJavaFileManager fileManager, Map<String, JavaMemCls> destFiles) {
super(fileManager);
this.destFiles = destFiles;
}

public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException {
if (destFiles.containsKey(className)) {
return destFiles.get(className);
} else {
JavaMemCls file = new JavaMemCls(className);
this.destFiles.put(className, file);
return file;
}
}

public void close() throws IOException {
super.close();
this.destFiles = null;
}

}
30 changes: 17 additions & 13 deletions src/main/java/com/github/pfmiles/dropincc/impl/util/Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -221,19 +221,6 @@ public static String join(String joint, List<String> elements) {
return sb.toString();
}

/**
* Get the platform dependent temporary directory path, with 'file
* separator' suffix('/' on linux platform, '\' on windows).
*
* @return
*/
public static String getTempDirWithFileSeparatorSuffix() {
String p = System.getProperty("java.io.tmpdir");
if (p.endsWith(File.separator))
return p;
return p + PATH_SEP;
}

/**
* build the classpath using all properties with suffix 'class.path'; to
* make some software setting its own class path variables happy(like
Expand Down Expand Up @@ -271,4 +258,21 @@ public static String getClassPath() {
}
return sb.toString();
}

/**
* Get the proper parent class loader for hot compilation class loaders.
*
* @return
*/
public static ClassLoader getParentClsLoader() {
ClassLoader ctxLoader = Thread.currentThread().getContextClassLoader();
if (ctxLoader != null) {
try {
ctxLoader.loadClass(Util.class.getName());
return ctxLoader;
} catch (ClassNotFoundException e) {
}
}
return Util.class.getClassLoader();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ public void testSubRuleRewriteWithKleeneNodes() {
addition.define(addend, CC.ks(ADD.or(SUB), addend));
addend.define(factor, CC.ks(MUL.or(DIV), factor));
factor.define(DIGIT).alt(LEFTPAREN, addition, RIGHTPAREN);
AnalyzedLang cl = new AnalyzedLang("test", (List<TokenDef>) TestHelper.priField(calculator, "tokens"), (List<Grule>) TestHelper.priField(calculator, "grules"),
false);
AnalyzedLang cl = new AnalyzedLang("test", (List<TokenDef>) TestHelper.priField(calculator, "tokens"), (List<Grule>) TestHelper.priField(
calculator, "grules"), false, "UTF-8");
KleeneStarNode k1 = (KleeneStarNode) addition.getAlts().get(0).getElements().get(1);
Object shouldBeRewritten = k1.getElements().get(0);
assertTrue(shouldBeRewritten instanceof Grule);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class HotCompileUtilTest extends TestCase {
public void testCompile() throws Exception {
String code = "package test;\n" + "public class Test {\n" + "public static void main(String... args) throws Throwable {\n"
+ "System.out.println(\"Hello World.\");\n" + "}\n" + "};\n";
CompilationResult rst = HotCompileUtil.compile("test.Test", code);
CompilationResult rst = HotCompileUtil.compile("test.Test", code, "UTF-8");
if (!rst.isSucceed()) {
assertTrue(false);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,11 @@ public static <T> T priField(Object obj, String fieldName) {
@SuppressWarnings("unchecked")
public static AnalyzedLangForTest resolveAnalyzedLangForTest(Lang lang) {
AnalyzedLangForTest ret = new AnalyzedLangForTest();
AnalyzedLang al = new AnalyzedLang("test", (List<TokenDef>) priField(lang, "tokens"), (List<Grule>) priField(lang, "grules"), (Boolean) priField(lang,
"whiteSpaceSensitive"));
AnalyzedLang al = new AnalyzedLang("test", (List<TokenDef>) priField(lang, "tokens"), (List<Grule>) priField(lang, "grules"),
(Boolean) priField(lang, "whiteSpaceSensitive"), "UTF-8");
TypeMappingParam typeMappingParam = new TypeMappingParam((Map<TokenDef, TokenType>) TestHelper.priField(al, "tokenTypeMapping"),
(Map<Grule, GruleType>) TestHelper.priField(al, "gruleTypeMapping"), (Map<Element, SpecialType>) TestHelper.priField(al, "specialTypeMapping"),
(Map<AbstractKleeneNode, KleeneType>) TestHelper.priField(al, "kleeneTypeMapping"));
(Map<Grule, GruleType>) TestHelper.priField(al, "gruleTypeMapping"), (Map<Element, SpecialType>) TestHelper.priField(al,
"specialTypeMapping"), (Map<AbstractKleeneNode, KleeneType>) TestHelper.priField(al, "kleeneTypeMapping"));
ret.kleeneTypeToNode = KleeneCompiler.buildKleeneTypeToNode(typeMappingParam);
ret.ruleTypeToAlts = ParserCompiler.buildRuleTypeToAlts(typeMappingParam);
return ret;
Expand Down

0 comments on commit 76f3626

Please sign in to comment.