diff options
author | Rich Hickey <richhickey@gmail.com> | 2007-09-02 16:16:42 +0000 |
---|---|---|
committer | Rich Hickey <richhickey@gmail.com> | 2007-09-02 16:16:42 +0000 |
commit | 408738696c33290242941b7395c9c15166c1c28c (patch) | |
tree | 141235cdca8c9b231c7a672e990e215a352b2dd9 | |
parent | 89399efd04b3a276bd00a5219a8db2e0d329eecb (diff) |
interim checkin, basic repl and lambda compilation
-rw-r--r-- | src/jvm/clojure/lang/BytecodeCompiler.java | 244 |
1 files changed, 228 insertions, 16 deletions
diff --git a/src/jvm/clojure/lang/BytecodeCompiler.java b/src/jvm/clojure/lang/BytecodeCompiler.java index a410b559..2eec1f09 100644 --- a/src/jvm/clojure/lang/BytecodeCompiler.java +++ b/src/jvm/clojure/lang/BytecodeCompiler.java @@ -12,10 +12,7 @@ package clojure.lang; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.Type; +import org.objectweb.asm.*; import org.objectweb.asm.commons.Method; import org.objectweb.asm.commons.GeneratorAdapter; import org.objectweb.asm.util.TraceClassVisitor; @@ -23,6 +20,7 @@ import org.objectweb.asm.util.TraceClassVisitor; import java.io.PrintWriter; import java.io.InputStreamReader; import java.io.OutputStreamWriter; +import java.math.BigInteger; public class BytecodeCompiler implements Opcodes{ @@ -48,6 +46,8 @@ private static Type OBJECT_TYPE; private static Type KEYWORD_TYPE = Type.getType(Keyword.class); private static Type VAR_TYPE = Type.getType(Var.class); private static Type SYMBOL_TYPE = Type.getType(Symbol.class); +private static Type NUM_TYPE = Type.getType(Num.class); +private static Type IFN_TYPE = Type.getType(IFn.class); private static Type[][] ARG_TYPES; private static Type[] EXCEPTION_TYPES = {Type.getType(Exception.class)}; @@ -106,6 +106,7 @@ interface Expr{ static class DefExpr implements Expr{ final Var var; final Expr init; + final static Method bindRootMethod = Method.getMethod("void bindRoot(Object)"); public DefExpr(Var var, Expr init){ this.var = var; @@ -118,7 +119,11 @@ static class DefExpr implements Expr{ } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ - + fn.emitVar(gen, var); + init.emit(C.EXPRESSION, fn, gen); + gen.invokeVirtual(VAR_TYPE, bindRootMethod); + if(!(context == C.STATEMENT)) + fn.emitVar(gen, var); } public static Expr parse(C context, ISeq form) throws Exception{ @@ -148,6 +153,11 @@ static class VarExpr implements Expr{ public Object eval() throws Exception{ return var.get(); } + + public void emit(C context, FnExpr fn, GeneratorAdapter gen){ + if(context != C.STATEMENT) + fn.emitVar(gen, var); + } } static class KeywordExpr implements Expr{ @@ -160,6 +170,12 @@ static class KeywordExpr implements Expr{ public Object eval() throws Exception{ return k; } + + public void emit(C context, FnExpr fn, GeneratorAdapter gen){ + if(context != C.STATEMENT) + fn.emitKeyword(gen, k); + + } } static abstract class LiteralExpr implements Expr{ @@ -174,6 +190,11 @@ static class NilExpr extends LiteralExpr{ Object val(){ return null; } + + public void emit(C context, FnExpr fn, GeneratorAdapter gen){ + if(context != C.STATEMENT) + gen.visitInsn(Opcodes.ACONST_NULL); + } } static NilExpr NIL_EXPR = new NilExpr(); @@ -181,6 +202,13 @@ static NilExpr NIL_EXPR = new NilExpr(); static class NumExpr extends LiteralExpr{ final Num num; + final static Method numFromIntMethod = Method.getMethod("clojure.lang.Num from(int)"); + final static Method numFromDoubleMethod = Method.getMethod("clojure.lang.Num from(double)"); + final static Method numFromBigIntMethod = Method.getMethod("clojure.lang.Num from(java.math.BigInteger)"); + final static Method numDivideMethod = + Method.getMethod("clojure.lang.Num divide(java.math.BigInteger,java.math.BigInteger)"); + final static Type BIGINT_TYPE = Type.getType(BigInteger.class); + final static Method bigintFromStringCtor = Method.getMethod("void <init>(String)"); public NumExpr(Num num){ this.num = num; @@ -189,6 +217,44 @@ static class NumExpr extends LiteralExpr{ Object val(){ return num; } + + public void emit(C context, FnExpr fn, GeneratorAdapter gen){ + if(context != C.STATEMENT) + { + Class nclass = num.getClass(); + if(nclass == FixNum.class) + { + gen.push(num.intValue()); + gen.invokeStatic(NUM_TYPE, numFromIntMethod); + } + else if(nclass == DoubleNum.class) + { + gen.push(num.doubleValue()); + gen.invokeStatic(NUM_TYPE, numFromDoubleMethod); + } + else if(nclass == BigNum.class) + { + emitBigInteger(gen, num); + gen.invokeStatic(NUM_TYPE, numFromBigIntMethod); + } + else if(nclass == RatioNum.class) + { + RatioNum r = (RatioNum) num; + emitBigInteger(gen, r.numerator); + emitBigInteger(gen, r.denominator); + gen.invokeStatic(NUM_TYPE, numDivideMethod); + } + else + throw new UnsupportedOperationException("Unknown Num type"); + } + } + + static void emitBigInteger(GeneratorAdapter gen, Num num){ + gen.newInstance(BIGINT_TYPE); + gen.dup(); + gen.push(num.toString()); + gen.invokeConstructor(BIGINT_TYPE, bigintFromStringCtor); + } } static class StringExpr extends LiteralExpr{ @@ -201,10 +267,17 @@ static class StringExpr extends LiteralExpr{ Object val(){ return str; } + + public void emit(C context, FnExpr fn, GeneratorAdapter gen){ + if(context != C.STATEMENT) + gen.push(str); + } } static class CharExpr extends LiteralExpr{ final Character ch; + final static Type CHARACTER_TYPE = Type.getObjectType("java/lang/Character"); + final static Method charValueOfMethod = Method.getMethod("Character valueOf(char)"); public CharExpr(Character ch){ this.ch = ch; @@ -213,6 +286,14 @@ static class CharExpr extends LiteralExpr{ Object val(){ return ch; } + + public void emit(C context, FnExpr fn, GeneratorAdapter gen){ + if(context != C.STATEMENT) + { + gen.push(ch.charValue()); + gen.invokeStatic(CHARACTER_TYPE, charValueOfMethod); + } + } } static class IfExpr implements Expr{ @@ -233,6 +314,18 @@ static class IfExpr implements Expr{ return elseExpr.eval(); } + public void emit(C context, FnExpr fn, GeneratorAdapter gen){ + Label elseLabel = gen.newLabel(); + Label endLabel = gen.newLabel(); + testExpr.emit(C.EXPRESSION, fn, gen); + gen.ifNull(elseLabel); + thenExpr.emit(context, fn, gen); + gen.goTo(endLabel); + gen.mark(elseLabel); + elseExpr.emit(context, fn, gen); + gen.mark(endLabel); + } + public static Expr parse(C context, ISeq form) throws Exception{ //(if test then) or (if test then else) if(form.count() > 4) @@ -283,12 +376,57 @@ static String munge(String name){ return sb.toString(); } +static class InvokeExpr implements Expr{ + final Expr fexpr; + final IPersistentArray args; + + + public InvokeExpr(Expr fexpr, IPersistentArray args){ + this.fexpr = fexpr; + this.args = args; + } + + public Object eval() throws Exception{ + IFn fn = (IFn) fexpr.eval(); + PersistentVector argvs = PersistentVector.EMPTY; + for(int i = 0; i < args.count(); i++) + argvs = argvs.cons(((Expr) args.nth(i)).eval()); + return fn.applyTo(RT.seq(argvs)); + } + + public void emit(C context, FnExpr fn, GeneratorAdapter gen){ + fexpr.emit(C.EXPRESSION, fn, gen); + gen.checkCast(IFN_TYPE); + for(int i = 0; i < args.count(); i++) + { + Expr e = (Expr) args.nth(i); + e.emit(C.EXPRESSION, fn, gen); + } + gen.invokeVirtual(IFN_TYPE, new Method("invoke", OBJECT_TYPE, ARG_TYPES[args.count()])); + if(context == C.STATEMENT) + gen.pop(); + } + + public static Expr parse(C context, ISeq form) throws Exception{ + Expr fexpr = analyze(C.EXPRESSION, form.first()); + PersistentVector args = PersistentVector.EMPTY; + for(ISeq s = RT.seq(form.rest()); s != null; s = s.rest()) + { + args = args.cons(analyze(C.EXPRESSION, s.first())); + } + if(args.count() > MAX_POSITIONAL_ARITY) + throw new IllegalArgumentException(String.format("No more than %d args supported", MAX_POSITIONAL_ARITY)); + return new InvokeExpr(fexpr, args); + } +} + static class FnExpr implements Expr{ IPersistentCollection methods; //if there is a variadic overload (there can only be one) it is stored here FnMethod variadicMethod = null; String name; String internalName; + Type fntype; //localbinding->itself IPersistentMap closes = PersistentHashMap.EMPTY; //Keyword->KeywordExpr @@ -296,6 +434,14 @@ static class FnExpr implements Expr{ IPersistentMap vars = PersistentHashMap.EMPTY; Class compiledClass; + final static Method kwintern = Method.getMethod("clojure.lang.Keyword intern(String, String)"); + final static Method symcreate = Method.getMethod("clojure.lang.Symbol create(String, String)"); + final static Method varintern = Method.getMethod("clojure.lang.Var intern(clojure.lang.Symbol)"); + final static Method afnctor = Method.getMethod("void <init>()"); + final static Method restfnctor = Method.getMethod("void <init>(int)"); + final static Type aFnType = Type.getType(AFn.class); + final static Type restFnType = Type.getType(RestFn.class); + static Expr parse(C context, ISeq form, String name) throws Exception{ FnExpr fn = new FnExpr(); FnMethod enclosingMethod = (FnMethod) METHOD.get(); @@ -306,6 +452,7 @@ static class FnExpr implements Expr{ munge(name) : ("fn__" + RT.nextID())); fn.internalName = fn.name.replace('.', '/'); + fn.fntype = Type.getObjectType(fn.internalName); try { Var.pushThreadBindings( @@ -378,7 +525,6 @@ static class FnExpr implements Expr{ if(source != null) cv.visitSource(source, null); - Type fntype = Type.getObjectType(internalName); //static fields for keywords for(ISeq s = RT.keys(keywords); s != null; s = s.rest()) { @@ -399,9 +545,6 @@ static class FnExpr implements Expr{ null, null, cv); - Method kwintern = Method.getMethod("clojure.lang.Keyword intern(String, String)"); - Method symcreate = Method.getMethod("clojure.lang.Symbol create(String, String)"); - Method varintern = Method.getMethod("clojure.lang.Var intern(clojure.lang.Symbol)"); for(ISeq s = RT.keys(keywords); s != null; s = s.rest()) { Keyword k = (Keyword) s.first(); @@ -437,13 +580,17 @@ static class FnExpr implements Expr{ cv); ctorgen.loadThis(); if(isVariadic()) //RestFn ctor takes reqArity arg + { ctorgen.push(variadicMethod.reqParms.count()); - ctorgen.invokeConstructor(Type.getType(isVariadic() ? RestFn.class : AFn.class), m); + ctorgen.invokeConstructor(restFnType, restfnctor); + } + else + ctorgen.invokeConstructor(aFnType, afnctor); int a = 1; for(ISeq s = RT.keys(closes); s != null; s = s.rest(), ++a) { LocalBinding lb = (LocalBinding) s.first(); - ctorgen.loadLocal(a); + ctorgen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), a); ctorgen.putField(fntype, lb.name, OBJECT_TYPE); } ctorgen.returnValue(); @@ -467,6 +614,38 @@ static class FnExpr implements Expr{ public Object eval() throws Exception{ return compiledClass.newInstance(); } + + public void emit(C context, FnExpr fn, GeneratorAdapter gen){ + //emitting a Fn means constructing an instance, feeding closed-overs from enclosing scope, if any + //fn arg is enclosing fn, not this + if(context != C.STATEMENT) + { + gen.newInstance(fntype); + gen.dup(); + for(ISeq s = RT.keys(closes); s != null; s = s.rest()) + { + LocalBinding lb = (LocalBinding) s.first(); + fn.emitLocal(gen, lb); + } + gen.invokeConstructor(fntype, new Method("<init>", Type.VOID_TYPE, ARG_TYPES[closes.count()])); + } + } + + private void emitLocal(GeneratorAdapter gen, LocalBinding lb){ + if(closes.contains(lb)) + gen.getField(fntype, lb.name, OBJECT_TYPE); + else + gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), lb.idx); + } + + + public void emitVar(GeneratorAdapter gen, Var var){ + gen.getStatic(fntype, munge(var.sym.toString()), VAR_TYPE); + } + + public void emitKeyword(GeneratorAdapter gen, Keyword k){ + gen.getStatic(fntype, munge(k.sym.toString()), KEYWORD_TYPE); + } } enum PSTATE{ @@ -584,11 +763,11 @@ static class FnMethod{ static class LocalBinding{ final Symbol sym; final Symbol tag; - final int num; + final int idx; final String name; public LocalBinding(int num, Symbol sym, Symbol tag){ - this.num = num; + this.idx = num; this.sym = sym; this.tag = tag; name = munge(sym.name); @@ -607,6 +786,11 @@ static class LocalBindingExpr implements Expr{ public Object eval() throws Exception{ throw new UnsupportedOperationException("Can't eval locals"); } + + public void emit(C context, FnExpr fn, GeneratorAdapter gen){ + if(context != C.STATEMENT) + fn.emitLocal(gen, b); + } } static class BodyExpr implements Expr{ @@ -638,6 +822,23 @@ static class BodyExpr implements Expr{ } return ret; } + + public void emit(C context, FnExpr fn, GeneratorAdapter gen){ + if(exprs.count() > 0) + { + for(int i = 0; i < exprs.count() - 1; i++) + { + Expr e = (Expr) exprs.nth(i); + e.emit(C.STATEMENT, fn, gen); + } + Expr last = (Expr) exprs.nth(exprs.count() - 1); + last.emit(context, fn, gen); + } + else if(context != C.STATEMENT) + { + NIL_EXPR.emit(context, fn, gen); + } + } } static class BindingInit{ @@ -714,6 +915,16 @@ static class LetExpr implements Expr{ public Object eval() throws Exception{ throw new UnsupportedOperationException("Can't eval let/loop"); } + + public void emit(C context, FnExpr fn, GeneratorAdapter gen){ + for(int i = 0; i < bindingInits.count(); i++) + { + BindingInit bi = (BindingInit) bindingInits.nth(i); + bi.init.emit(C.EXPRESSION, fn, gen); + gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), bi.binding.idx); + } + body.emit(context, fn, gen); + } } private static LocalBinding registerLocal(Symbol sym, Symbol tag) throws Exception{ @@ -732,7 +943,7 @@ private static Expr analyze(C context, Object form) throws Exception{ } private static Expr analyze(C context, Object form, String name) throws Exception{ - //todo macro expansion + //todo symbol macro expansion if(form == null) return NIL_EXPR; Class fclass = form.getClass(); @@ -755,6 +966,7 @@ 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); + //todo macro expansion if(op.equals(DEF)) return DefExpr.parse(context, form); else if(op.equals(IF)) @@ -767,8 +979,8 @@ private static Expr analyzeSeq(C context, ISeq form, String name) throws Excepti return LetExpr.parse(context, form, false); else if(op.equals(LOOP)) return LetExpr.parse(context, form, true); - - throw new UnsupportedOperationException(); + else + return InvokeExpr.parse(context, form); } static Object eval(Object form) throws Exception{ |