Skip to content

Commit bf29813

Browse files
committed
Decoupled the compiler.
Added a compiler facade and added a dispatcher that enables loading different kinds of code objects from .class-files. This enables having multiple compilers and different code implementations.
1 parent 02f2ca8 commit bf29813

9 files changed

Lines changed: 281 additions & 2 deletions

File tree

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package org.python.compiler;
2+
3+
import java.io.ByteArrayOutputStream;
4+
import java.io.OutputStream;
5+
6+
import org.python.antlr.base.mod;
7+
import org.python.core.BytecodeLoader;
8+
import org.python.core.CompilerFlags;
9+
import org.python.core.Py;
10+
import org.python.core.PyCode;
11+
import org.python.core.PythonCodeBundle;
12+
import org.python.core.PythonCompiler;
13+
14+
public class LegacyCompiler implements PythonCompiler {
15+
16+
public PythonCodeBundle compile(mod node, String name, String filename,
17+
boolean linenumbers, boolean printResults, CompilerFlags cflags) {
18+
return new LazyLegacyBundle(node, name, filename, linenumbers,
19+
printResults, cflags);
20+
}
21+
22+
private static class LazyLegacyBundle implements PythonCodeBundle {
23+
24+
private final mod node;
25+
private final String name;
26+
private final String filename;
27+
private final boolean linenumbers;
28+
private final boolean printResults;
29+
private final CompilerFlags cflags;
30+
private ByteArrayOutputStream ostream = null;
31+
32+
public LazyLegacyBundle(mod node, String name, String filename,
33+
boolean linenumbers, boolean printResults, CompilerFlags cflags) {
34+
this.node = node;
35+
this.name = name;
36+
this.filename = filename;
37+
this.linenumbers = linenumbers;
38+
this.printResults = printResults;
39+
this.cflags = cflags;
40+
}
41+
42+
public PyCode loadCode() throws Exception {
43+
return BytecodeLoader.makeCode(name, ostream().toByteArray(),
44+
filename);
45+
}
46+
47+
public void writeTo(OutputStream stream) throws Exception {
48+
if (this.ostream != null) {
49+
stream.write(ostream.toByteArray());
50+
} else {
51+
Module.compile(node, stream, name, filename, linenumbers,
52+
printResults, cflags);
53+
}
54+
}
55+
56+
public void saveCode(String directory) throws Exception {
57+
// FIXME: this is slightly broken, it should use the directory
58+
Py.saveClassFile(name, ostream());
59+
}
60+
61+
private ByteArrayOutputStream ostream() throws Exception {
62+
if (ostream == null) {
63+
ostream = new ByteArrayOutputStream();
64+
Module.compile(node, ostream, name, filename, linenumbers,
65+
printResults, cflags);
66+
}
67+
return ostream;
68+
}
69+
70+
}
71+
72+
}

src/org/python/compiler/Module.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,14 @@
1212

1313
import org.objectweb.asm.Label;
1414
import org.objectweb.asm.Opcodes;
15+
import org.python.core.CodeBootstrap;
1516
import org.python.core.CodeFlag;
17+
import org.python.core.CodeLoader;
1618
import org.python.core.CompilerFlags;
1719
import org.python.core.Py;
1820
import org.python.core.PyException;
21+
import org.python.core.PyRunnableBootstrap;
22+
import org.objectweb.asm.Type;
1923
import org.python.antlr.ParseException;
2024
import org.python.antlr.PythonTree;
2125
import org.python.antlr.ast.Suite;
@@ -547,10 +551,26 @@ public void addMain() throws IOException {
547551
c.dup();
548552
c.ldc(classfile.name);
549553
c.invokespecial(classfile.name, "<init>", "(" + $str + ")V");
554+
c.invokevirtual(classfile.name, "getMain", "()" + $pyCode);
555+
String bootstrap = Type.getDescriptor(CodeBootstrap.class);
556+
c.invokestatic(Type.getInternalName(CodeLoader.class),
557+
CodeLoader.SIMPLE_FACTORY_METHOD_NAME,
558+
"(" + $pyCode + ")" + bootstrap);
550559
c.aload(0);
551-
c.invokestatic("org/python/core/Py", "runMain", "(" + $pyRunnable + $strArr + ")V");
560+
c.invokestatic("org/python/core/Py", "runMain", "(" + bootstrap + $strArr + ")V");
552561
c.return_();
553562
}
563+
564+
public void addBootstrap() throws IOException {
565+
Code c = classfile.addMethod(CodeLoader.GET_BOOTSTRAP_METHOD_NAME,
566+
"()" + Type.getDescriptor(CodeBootstrap.class),
567+
ACC_PUBLIC | ACC_STATIC);
568+
c.ldc(Type.getType("L" + classfile.name + ";"));
569+
c.invokestatic(Type.getInternalName(PyRunnableBootstrap.class),
570+
PyRunnableBootstrap.REFLECTION_METHOD_NAME,
571+
"(" + $clss + ")" + Type.getDescriptor(CodeBootstrap.class));
572+
c.areturn();
573+
}
554574

555575
public void addConstants(Code c) throws IOException {
556576
classfile.addField("self", "L"+classfile.name+";",
@@ -605,6 +625,7 @@ public void write(OutputStream stream) throws IOException {
605625
addInit();
606626
addRunnable();
607627
addMain();
628+
addBootstrap();
608629

609630
addFunctions();
610631

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.python.core;
2+
3+
public interface CodeBootstrap {
4+
5+
PyCode loadCode(CodeLoader loader);
6+
7+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package org.python.core;
2+
3+
import java.lang.reflect.InvocationTargetException;
4+
import java.lang.reflect.Method;
5+
import java.lang.reflect.Modifier;
6+
7+
public final class CodeLoader {
8+
9+
public static final String GET_BOOTSTRAP_METHOD_NAME = "getCodeBootstrap";
10+
public final String name;
11+
public final String filename;
12+
13+
private CodeLoader(String name, String filename) {
14+
this.name = name;
15+
this.filename = filename;
16+
}
17+
18+
public static boolean canLoad(Class<?> cls) {
19+
try {
20+
Method getBootstrap = cls.getMethod(GET_BOOTSTRAP_METHOD_NAME);
21+
return Modifier.isStatic(getBootstrap.getModifiers());
22+
} catch (Exception e) {
23+
return false;
24+
}
25+
}
26+
27+
public static PyCode loadCode(Class<?> cls, String name, String filename)
28+
throws SecurityException, NoSuchMethodException,
29+
IllegalArgumentException, IllegalAccessException,
30+
InvocationTargetException {
31+
Method getBootstrap = cls.getMethod(GET_BOOTSTRAP_METHOD_NAME);
32+
CodeBootstrap bootstrap = (CodeBootstrap) getBootstrap.invoke(null);
33+
return loadCode(bootstrap, name, filename);
34+
}
35+
36+
public static PyCode loadCode(Class<?> cls) throws SecurityException,
37+
IllegalArgumentException, NoSuchMethodException,
38+
IllegalAccessException, InvocationTargetException {
39+
return loadCode(cls, null, null);
40+
}
41+
42+
public static PyCode loadCode(CodeBootstrap bootstrap, String name,
43+
String filename) {
44+
return bootstrap.loadCode(new CodeLoader(name, filename));
45+
}
46+
47+
public static PyCode loadCode(CodeBootstrap bootstrap) {
48+
return loadCode(bootstrap, null, null);
49+
}
50+
51+
public static final String SIMPLE_FACTORY_METHOD_NAME = "createSimpleBootstrap";
52+
53+
public static CodeBootstrap createSimpleBootstrap(final PyCode code) {
54+
return new CodeBootstrap() {
55+
public PyCode loadCode(CodeLoader loader) {
56+
return code;
57+
}
58+
};
59+
}
60+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package org.python.core;
2+
3+
import org.python.antlr.base.mod;
4+
import org.python.compiler.LegacyCompiler;
5+
6+
/**
7+
* Facade for different compiler implementations.
8+
*
9+
* The static methods of this class act as a Facade for the compiler subsystem.
10+
* This is so that the rest of Jython (even generated code) can statically link
11+
* to the static interface of this class, while allowing for different
12+
* implementations of the various components of the compiler subsystem.
13+
*
14+
* @author Tobias Ivarsson
15+
*/
16+
public class CompilerFacade {
17+
18+
private static volatile PythonCompiler compiler = loadDefaultCompiler();
19+
20+
public static void setCompiler(PythonCompiler compiler) {
21+
CompilerFacade.compiler = compiler;
22+
}
23+
24+
private static PythonCompiler loadDefaultCompiler() {
25+
return new LegacyCompiler();
26+
}
27+
28+
public static PyCode compile(mod node, String name, String filename,
29+
boolean linenumbers, boolean printResults, CompilerFlags cflags) {
30+
try {
31+
PythonCodeBundle bundle = compiler.compile(node, name, filename,
32+
linenumbers, printResults, cflags);
33+
bundle.saveCode(Options.proxyDebugDirectory);
34+
return bundle.loadCode();
35+
} catch (Throwable t) {
36+
throw ParserFacade.fixParseError(null, t, filename);
37+
}
38+
}
39+
}

src/org/python/core/Py.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -862,9 +862,19 @@ public static void initProxy(PyProxy proxy, String module, String pyclass, Objec
862862
* Called by the code generated in {@link Module#addMain()}
863863
*/
864864
public static void runMain(PyRunnable main, String[] args) throws Exception {
865+
runMain(new PyRunnableBootstrap(main), args);
866+
}
867+
868+
/**
869+
* Initializes a default PythonInterpreter and runs the code loaded from the
870+
* {@link CodeBootstrap} as __main__ Called by the code generated in
871+
* {@link Module#addMain()}
872+
*/
873+
public static void runMain(CodeBootstrap main, String[] args)
874+
throws Exception {
865875
PySystemState.initialize(null, null, args, main.getClass().getClassLoader());
866876
try {
867-
imp.createFromCode("__main__", main.getMain());
877+
imp.createFromCode("__main__", CodeLoader.loadCode(main));
868878
} catch (PyException e) {
869879
Py.getSystemState().callExitFunc();
870880
if (Py.matchException(e, Py.SystemExit)) {
@@ -1648,6 +1658,8 @@ public static PyCode compile(InputStream istream, String filename, CompileMode k
16481658
public static PyCode compile_flags(mod node, String name, String filename,
16491659
boolean linenumbers, boolean printResults,
16501660
CompilerFlags cflags) {
1661+
return CompilerFacade.compile(node, name, filename, linenumbers, printResults, cflags);
1662+
/*
16511663
try {
16521664
ByteArrayOutputStream ostream = new ByteArrayOutputStream();
16531665
Module.compile(node, ostream, name, filename, linenumbers, printResults, cflags);
@@ -1658,6 +1670,7 @@ public static PyCode compile_flags(mod node, String name, String filename,
16581670
} catch (Throwable t) {
16591671
throw ParserFacade.fixParseError(null, t, filename);
16601672
}
1673+
*/
16611674
}
16621675

16631676
public static PyCode compile_flags(mod node, String filename,
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package org.python.core;
2+
3+
import java.lang.reflect.Constructor;
4+
5+
public class PyRunnableBootstrap implements CodeBootstrap {
6+
7+
public static final String REFLECTION_METHOD_NAME = "getFilenameConstructorReflectionBootstrap";
8+
private final PyRunnable runnable;
9+
10+
PyRunnableBootstrap(PyRunnable runnable) {
11+
this.runnable = runnable;
12+
}
13+
14+
public PyCode loadCode(CodeLoader loader) {
15+
return runnable.getMain();
16+
}
17+
18+
public static CodeBootstrap getFilenameConstructorReflectionBootstrap(
19+
Class<? extends PyRunnable> cls) {
20+
final Constructor<? extends PyRunnable> constructor;
21+
try {
22+
constructor = cls.getConstructor(String.class);
23+
} catch (Exception e) {
24+
throw new IllegalArgumentException(
25+
"PyRunnable class does not specify apropriate constructor.",
26+
e);
27+
}
28+
return new CodeBootstrap() {
29+
30+
public PyCode loadCode(CodeLoader loader) {
31+
try {
32+
return constructor.newInstance(loader.filename).getMain();
33+
} catch (Exception e) {
34+
throw new IllegalArgumentException(
35+
"PyRunnable class constructor does not support instantiation protocol.",
36+
e);
37+
}
38+
}
39+
40+
};
41+
}
42+
43+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.python.core;
2+
3+
import java.io.OutputStream;
4+
5+
public interface PythonCodeBundle {
6+
7+
PyCode loadCode() throws Exception;
8+
9+
void writeTo(OutputStream stream) throws Exception;
10+
11+
void saveCode(String directory) throws Exception;
12+
13+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.python.core;
2+
3+
import org.python.antlr.base.mod;
4+
5+
public interface PythonCompiler {
6+
7+
PythonCodeBundle compile(mod node, String name, String filename,
8+
boolean linenumbers, boolean printResults, CompilerFlags cflags)
9+
throws Exception;
10+
11+
}

0 commit comments

Comments
 (0)