summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRich Hickey <richhickey@gmail.com>2007-09-01 18:07:52 +0000
committerRich Hickey <richhickey@gmail.com>2007-09-01 18:07:52 +0000
commitaeb160665adff7925207d7f60f3a7fdf5837cad0 (patch)
tree5be34d69192ead3ae0858cb181bb6a8b402fb472
parentcbe59e53843e41c78f566f202eec2d3eb855b160 (diff)
interim checkin
-rw-r--r--clojure.iml3
-rw-r--r--src/jvm/clojure/lang/BytecodeCompiler.java189
-rw-r--r--src/jvm/clojure/lang/DynamicClassLoader.java4
-rw-r--r--src/jvm/clojure/lang/DynamicVar.java5
4 files changed, 161 insertions, 40 deletions
diff --git a/clojure.iml b/clojure.iml
index b20440f2..e09fe89f 100644
--- a/clojure.iml
+++ b/clojure.iml
@@ -9,9 +9,8 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
- <orderEntry type="library" name="asm-2.2.1" level="application" />
<orderEntry type="library" name="antlr-3" level="application" />
- <orderEntry type="library" name="asm-3.0" level="application" />
+ <orderEntry type="library" name="asm-3.0" level="project" />
<orderEntryProperties />
</component>
<component name="VcsManagerConfiguration">
diff --git a/src/jvm/clojure/lang/BytecodeCompiler.java b/src/jvm/clojure/lang/BytecodeCompiler.java
index a37d0c2c..cb80d992 100644
--- a/src/jvm/clojure/lang/BytecodeCompiler.java
+++ b/src/jvm/clojure/lang/BytecodeCompiler.java
@@ -12,7 +12,19 @@
package clojure.lang;
-public class BytecodeCompiler{
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.Method;
+import org.objectweb.asm.commons.GeneratorAdapter;
+import org.objectweb.asm.util.TraceClassVisitor;
+
+import java.io.PrintWriter;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+
+public class BytecodeCompiler implements Opcodes{
static Symbol DEF = Symbol.create("def");
static Symbol LOOP = Symbol.create("loop");
@@ -24,6 +36,7 @@ static Symbol DO = Symbol.create("do");
static Symbol FN = Symbol.create("fn");
static Symbol QUOTE = Symbol.create("quote");
static Symbol THISFN = Symbol.create("thisfn");
+static Symbol IFN = Symbol.create("clojure.lang", "IFn");
static Symbol CLASS = Symbol.create("class");
static Symbol IMPORT = Symbol.create("import");
@@ -31,6 +44,22 @@ static Symbol USE = Symbol.create("use");
static Symbol _AMP_ = Symbol.create("&");
private static final int MAX_POSITIONAL_ARITY = 20;
+private static Type OBJECT_TYPE;
+private static Type[][] ARG_TYPES;
+
+static
+ {
+ OBJECT_TYPE = Type.getType(Object.class);
+ ARG_TYPES = new Type[MAX_POSITIONAL_ARITY][];
+ for(int i = 0; i < MAX_POSITIONAL_ARITY; ++i)
+ {
+ Type[] a = new Type[i];
+ for(int j = 0; j < i; j++)
+ a[j] = OBJECT_TYPE;
+ ARG_TYPES[i] = a;
+ }
+ }
+
//symbol->localbinding
static public DynamicVar LOCAL_ENV = DynamicVar.create();
@@ -47,6 +76,15 @@ static public DynamicVar VARS = DynamicVar.create();
//FnFrame
static public DynamicVar METHOD = DynamicVar.create();
+//String
+static public DynamicVar SOURCE = DynamicVar.create();
+
+//Integer
+static public DynamicVar NEXT_LOCAL_NUM = DynamicVar.create();
+
+//DynamicClassLoader
+static public DynamicVar LOADER = DynamicVar.create();
+
enum C{
STATEMENT, //value ignored
EXPRESSION, //value required
@@ -238,6 +276,7 @@ static class FnExpr implements Expr{
//if there is a variadic overload (there can only be one) it is stored here
FnMethod variadicMethod = null;
String name = null;
+ String internalName;
//localbinding->itself
IPersistentMap closes = null;
IPersistentMap keywords = null;
@@ -253,6 +292,7 @@ static class FnExpr implements Expr{
fn.name = basename + (name != null ?
munge(name)
: ("fn__" + RT.nextID()));
+ fn.internalName = fn.name.replace('.', '/');
try
{
DynamicVar.pushThreadBindings(
@@ -313,15 +353,52 @@ static class FnExpr implements Expr{
}
private void compile(){
-
//create bytecode for a class
//with name current_ns.defname[$letname]+
//anonymous fns get names fn__id
//derived from AFn/RestFn
- //with static fields for keywords and vars
- //with instance fields for closed-overs
- //with a ctor that takes closed-overs and inits fields
- //with an override of invoke/doInvoke for each method
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+ //ClassVisitor cv = cw;
+ ClassVisitor cv = new TraceClassVisitor(cw, new PrintWriter(System.out));
+ cv.visit(V1_5, ACC_PUBLIC, name, null, isVariadic() ? "clojure/lang/RestFn" : "clojure/lang/AFn", null);
+ String source = (String) SOURCE.get();
+ if(source != null)
+ cv.visitSource(source, null);
+ //todo static fields for keywords and vars
+ //todo static init for keywords and vars
+ //instance fields for closed-overs
+ for(ISeq s = RT.keys(closes); s != null; s = s.rest())
+ {
+ LocalBinding lb = (LocalBinding) s.first();
+ cv.visitField(ACC_PUBLIC + ACC_FINAL, lb.name, OBJECT_TYPE.getDescriptor(), null, null);
+ }
+ //ctor that takes closed-overs and inits base + fields
+ Method m = new Method("<init>", Type.VOID_TYPE, ARG_TYPES[closes.count()]);
+ GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC,
+ m,
+ null,
+ null,
+ cw);
+ mg.loadThis();
+ if(isVariadic()) //RestFn ctor takes reqArity arg
+ mg.push(variadicMethod.reqParms.count());
+ mg.invokeConstructor(Type.getType(isVariadic() ? RestFn.class : AFn.class), m);
+ int a = 1;
+ for(ISeq s = RT.keys(closes); s != null; s = s.rest(), ++a)
+ {
+ LocalBinding lb = (LocalBinding) s.first();
+ mg.loadLocal(a);
+ mg.putField(Type.getObjectType(internalName), lb.name, OBJECT_TYPE);
+ }
+ mg.returnValue();
+ mg.endMethod();
+
+ //todo override of invoke/doInvoke for each method
+ cv.visitEnd();
+
+ //define class and store
+ DynamicClassLoader loader = (DynamicClassLoader) LOADER.get();
+ compiledClass = loader.defineClass(name, cw.toByteArray());
}
public Object eval() throws Exception{
@@ -345,13 +422,18 @@ private static FnMethod analyzeMethod(FnExpr fn, ISeq form) throws Exception{
RT.map(
METHOD, method,
LOCAL_ENV, LOCAL_ENV.get(),
- LOOP_LOCALS, null));
+ LOOP_LOCALS, null,
+ NEXT_LOCAL_NUM, 0));
+
+ //register 'this' as local 0
+ registerLocal(THISFN, null);
+
PSTATE state = PSTATE.REQ;
PersistentVector loopLocals = PersistentVector.EMPTY;
for(ISeq ps = parms; ps != null; ps = ps.rest())
{
- Object p = ps.first();
- if(p == _AMP_)
+ Symbol p = (Symbol) ps.first();
+ if(p.equals(_AMP_))
{
if(state == PSTATE.REQ)
state = PSTATE.REST;
@@ -361,15 +443,15 @@ private static FnMethod analyzeMethod(FnExpr fn, ISeq form) throws Exception{
else
{
+ LocalBinding lb = registerLocal(p, tagOf(p));
+ loopLocals = loopLocals.cons(lb);
switch(state)
{
case REQ:
- LocalBinding lb = createParamBinding((Symbol) p);
- loopLocals = loopLocals.cons(lb);
method.reqParms = method.reqParms.cons(lb);
break;
case REST:
- method.restParm = createParamBinding((Symbol) p);
+ method.restParm = lb;
state = PSTATE.DONE;
break;
@@ -380,9 +462,7 @@ private static FnMethod analyzeMethod(FnExpr fn, ISeq form) throws Exception{
}
if(method.reqParms.count() > MAX_POSITIONAL_ARITY)
throw new Exception("Can't specify more than " + MAX_POSITIONAL_ARITY + " params");
- //only set loop locals if non-variadic
- if(method.restParm == null)
- LOOP_LOCALS.set(loopLocals);
+ LOOP_LOCALS.set(loopLocals);
method.body = BodyExpr.parse(C.RETURN, body);
return method;
}
@@ -392,11 +472,6 @@ private static FnMethod analyzeMethod(FnExpr fn, ISeq form) throws Exception{
}
}
-static LocalBinding createParamBinding(Symbol p) throws Exception{
- LocalBinding b = new LocalBinding(p, tagOf(p));
- registerLocal(b);
- return b;
-}
static class FnMethod{
//when closures are defined inside other closures,
@@ -424,10 +499,14 @@ static class FnMethod{
static class LocalBinding{
final Symbol sym;
final Symbol tag;
+ final int num;
+ final String name;
- public LocalBinding(Symbol sym, Symbol tag){
+ public LocalBinding(int num, Symbol sym, Symbol tag){
+ this.num = num;
this.sym = sym;
this.tag = tag;
+ name = munge(sym.name);
}
}
@@ -506,7 +585,8 @@ static class LetExpr implements Expr{
if(context == C.EVAL)
return analyze(context, RT.list(RT.list(FN, PersistentVector.EMPTY, form)));
- IPersistentMap dynamicBindings = RT.map(LOCAL_ENV, LOCAL_ENV.get());
+ IPersistentMap dynamicBindings = RT.map(LOCAL_ENV, LOCAL_ENV.get(),
+ NEXT_LOCAL_NUM, NEXT_LOCAL_NUM.get());
if(isLoop)
dynamicBindings = dynamicBindings.assoc(LOOP_LOCALS, null);
@@ -523,12 +603,12 @@ static class LetExpr implements Expr{
Symbol sym = (Symbol) RT.first(bs);
if(bs.rest() == null)
throw new IllegalArgumentException("Bad binding form, expected expression following: " + sym);
- LocalBinding lb = new LocalBinding(sym, tagOf(sym));
- BindingInit bi = new BindingInit(lb, analyze(C.EXPRESSION, RT.second(bs), sym.name));
- bindingInits = bindingInits.cons(bi);
- //sequential enhancement of env
- registerLocal(lb);
+ Expr init = analyze(C.EXPRESSION, RT.second(bs), sym.name);
+ //sequential enhancement of env (like Lisp let*)
+ LocalBinding lb = registerLocal(sym, tagOf(sym));
+ BindingInit bi = new BindingInit(lb, init);
+ bindingInits = bindingInits.cons(bi);
if(isLoop)
loopLocals = loopLocals.cons(lb);
@@ -548,11 +628,15 @@ static class LetExpr implements Expr{
}
}
-private static void registerLocal(LocalBinding b) throws Exception{
+private static LocalBinding registerLocal(Symbol sym, Symbol tag) throws Exception{
+ int num = ((Number) NEXT_LOCAL_NUM.get()).intValue();
+ LocalBinding b = new LocalBinding(num, sym, tag);
+ NEXT_LOCAL_NUM.set(num + 1);
IPersistentMap localsMap = (IPersistentMap) LOCAL_ENV.get();
LOCAL_ENV.set(RT.assoc(b.sym, b, localsMap));
FnMethod method = (FnMethod) METHOD.get();
method.locals = (IPersistentMap) RT.assoc(method.locals, b, b);
+ return b;
}
private static Expr analyze(C context, Object form) throws Exception{
@@ -583,29 +667,32 @@ private static Expr analyze(C context, Object form, String name) throws Exceptio
private static Expr analyzeSeq(C context, ISeq form, String name) throws Exception{
Object op = RT.first(form);
- if(op == DEF)
+ if(op.equals(DEF))
return DefExpr.parse(context, form);
- else if(op == IF)
+ else if(op.equals(IF))
return IfExpr.parse(context, form);
- else if(op == FN)
+ else if(op.equals(FN))
return FnExpr.parse(context, form, name);
- else if(op == DO)
+ else if(op.equals(DO))
return BodyExpr.parse(context, form.rest());
- else if(op == LET)
+ else if(op.equals(LET))
return LetExpr.parse(context, form, false);
- else if(op == LOOP)
+ else if(op.equals(LOOP))
return LetExpr.parse(context, form, true);
+
+ throw new UnsupportedOperationException();
}
-Object eval(Object form) throws Exception{
+static Object eval(Object form) throws Exception{
Expr expr = analyze(C.EXPRESSION, form);
return expr.eval();
}
private static KeywordExpr registerKeyword(Keyword keyword){
- IPersistentMap keywordsMap = (IPersistentMap) KEYWORDS.get();
- if(keywordsMap == null) //not bound, no fn context
+ if(!KEYWORDS.isBound())
return new KeywordExpr(keyword);
+
+ IPersistentMap keywordsMap = (IPersistentMap) KEYWORDS.get();
KeywordExpr ke = (KeywordExpr) RT.get(keyword, keywordsMap);
if(ke == null)
KEYWORDS.set(RT.assoc(keyword, ke = new KeywordExpr(keyword), keywordsMap));
@@ -653,6 +740,8 @@ static DynamicVar lookupVar(Symbol sym) throws Exception{
}
private static void registerVar(DynamicVar var) throws Exception{
+ if(!VARS.isBound())
+ return;
IPersistentMap varsMap = (IPersistentMap) VARS.get();
if(varsMap != null && RT.get(var, varsMap) == null)
VARS.set(RT.assoc(varsMap, var, var));
@@ -672,6 +761,8 @@ static void closeOver(LocalBinding b, FnMethod method){
static LocalBinding referenceLocal(Symbol sym) throws Exception{
+ if(!LOCAL_ENV.isBound())
+ return null;
LocalBinding b = (LocalBinding) RT.get(sym, LOCAL_ENV.get());
if(b != null)
{
@@ -686,4 +777,28 @@ private static Symbol tagOf(Symbol sym){
return null;
}
+
+public static void main(String[] args){
+ //repl
+ LineNumberingPushbackReader rdr = new LineNumberingPushbackReader(new InputStreamReader(System.in));
+ OutputStreamWriter w = new OutputStreamWriter(System.out);
+ Object EOF = new Object();
+ for(; ;)
+ {
+ try
+ {
+ Object r = LispReader.read(rdr, false, EOF, false);
+ if(r == EOF)
+ break;
+ Object ret = eval(r);
+ RT.print(ret, w);
+ w.write('\n');
+ w.flush();
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+}
}
diff --git a/src/jvm/clojure/lang/DynamicClassLoader.java b/src/jvm/clojure/lang/DynamicClassLoader.java
index 237b9ca0..f3f8ab32 100644
--- a/src/jvm/clojure/lang/DynamicClassLoader.java
+++ b/src/jvm/clojure/lang/DynamicClassLoader.java
@@ -28,6 +28,10 @@ public DynamicClassLoader(ClassLoader parent){
super(parent);
}
+public Class defineClass(String name, byte[] bytes){
+ return defineClass(name, bytes, 0, bytes.length);
+}
+
public void addBytecode(String className, byte[] bytes){
if(map.containsKey(className))
throw new IllegalStateException(String.format("Class %s already present", className));
diff --git a/src/jvm/clojure/lang/DynamicVar.java b/src/jvm/clojure/lang/DynamicVar.java
index 61b13938..a7d4852d 100644
--- a/src/jvm/clojure/lang/DynamicVar.java
+++ b/src/jvm/clojure/lang/DynamicVar.java
@@ -74,7 +74,10 @@ public static DynamicVar intern(Symbol sym){
if(dvout != null)
return dvout;
- return table.putIfAbsent(sym, new DynamicVar(sym));
+ DynamicVar dvin = table.putIfAbsent(sym, dvout = new DynamicVar(sym));
+ if(dvin != null)
+ return dvin;
+ return dvout;
}
public static void unintern(Symbol sym){