/** * Copyright (c) Rich Hickey. All rights reserved. * The use and distribution terms for this software are covered by the * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) * which can be found in the file epl-v10.html at the root of this distribution. * By using this software in any fashion, you are agreeing to be bound by * the terms of this license. * You must not remove this notice, or any other, from this software. **/ /* rich Aug 21, 2007 */ package clojure.lang; //* import clojure.asm.*; import clojure.asm.commons.Method; import clojure.asm.commons.GeneratorAdapter; //*/ /* import org.objectweb.asm.*; import org.objectweb.asm.commons.Method; import org.objectweb.asm.commons.GeneratorAdapter; import org.objectweb.asm.util.TraceClassVisitor; import org.objectweb.asm.util.CheckClassAdapter; //*/ import java.io.*; import java.util.*; import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; public class Compiler implements Opcodes{ static final Symbol DEF = Symbol.create("def"); static final Symbol LOOP = Symbol.create("loop*"); static final Symbol RECUR = Symbol.create("recur"); static final Symbol IF = Symbol.create("if"); static final Symbol LET = Symbol.create("let*"); static final Symbol LETFN = Symbol.create("letfn*"); static final Symbol DO = Symbol.create("do"); static final Symbol FN = Symbol.create("fn*"); static final Symbol QUOTE = Symbol.create("quote"); static final Symbol THE_VAR = Symbol.create("var"); static final Symbol DOT = Symbol.create("."); static final Symbol ASSIGN = Symbol.create("set!"); //static final Symbol TRY_FINALLY = Symbol.create("try-finally"); static final Symbol TRY = Symbol.create("try"); static final Symbol CATCH = Symbol.create("catch"); static final Symbol FINALLY = Symbol.create("finally"); static final Symbol THROW = Symbol.create("throw"); static final Symbol MONITOR_ENTER = Symbol.create("monitor-enter"); static final Symbol MONITOR_EXIT = Symbol.create("monitor-exit"); static final Symbol IMPORT = Symbol.create("clojure.core", "import*"); //static final Symbol INSTANCE = Symbol.create("instance?"); //static final Symbol THISFN = Symbol.create("thisfn"); static final Symbol CLASS = Symbol.create("Class"); static final Symbol NEW = Symbol.create("new"); //static final Symbol UNQUOTE = Symbol.create("unquote"); //static final Symbol UNQUOTE_SPLICING = Symbol.create("unquote-splicing"); //static final Symbol SYNTAX_QUOTE = Symbol.create("clojure.core", "syntax-quote"); static final Symbol LIST = Symbol.create("clojure.core", "list"); static final Symbol HASHMAP = Symbol.create("clojure.core", "hash-map"); static final Symbol VECTOR = Symbol.create("clojure.core", "vector"); static final Symbol IDENTITY = Symbol.create("clojure.core", "identity"); static final Symbol _AMP_ = Symbol.create("&"); static final Symbol ISEQ = Symbol.create("clojure.lang.ISeq"); static final Keyword inlineKey = Keyword.intern(null, "inline"); static final Keyword inlineAritiesKey = Keyword.intern(null, "inline-arities"); static final Symbol NS = Symbol.create("ns"); static final Symbol IN_NS = Symbol.create("in-ns"); //static final Symbol IMPORT = Symbol.create("import"); //static final Symbol USE = Symbol.create("use"); //static final Symbol IFN = Symbol.create("clojure.lang", "IFn"); static final public IPersistentMap specials = PersistentHashMap.create( DEF, new DefExpr.Parser(), LOOP, new LetExpr.Parser(), RECUR, new RecurExpr.Parser(), IF, new IfExpr.Parser(), LET, new LetExpr.Parser(), LETFN, new LetFnExpr.Parser(), DO, new BodyExpr.Parser(), FN, null, QUOTE, new ConstantExpr.Parser(), THE_VAR, new TheVarExpr.Parser(), IMPORT, new ImportExpr.Parser(), DOT, new HostExpr.Parser(), ASSIGN, new AssignExpr.Parser(), // TRY_FINALLY, new TryFinallyExpr.Parser(), TRY, new TryExpr.Parser(), THROW, new ThrowExpr.Parser(), MONITOR_ENTER, new MonitorEnterExpr.Parser(), MONITOR_EXIT, new MonitorExitExpr.Parser(), // INSTANCE, new InstanceExpr.Parser(), // IDENTICAL, new IdenticalExpr.Parser(), //THISFN, null, CATCH, null, FINALLY, null, // CLASS, new ClassExpr.Parser(), NEW, new NewExpr.Parser(), // UNQUOTE, null, // UNQUOTE_SPLICING, null, // SYNTAX_QUOTE, null, _AMP_, null ); private static final int MAX_POSITIONAL_ARITY = 20; private static final Type OBJECT_TYPE; private static final Type KEYWORD_TYPE = Type.getType(Keyword.class); private static final Type VAR_TYPE = Type.getType(Var.class); private static final Type SYMBOL_TYPE = Type.getType(Symbol.class); //private static final Type NUM_TYPE = Type.getType(Num.class); private static final Type IFN_TYPE = Type.getType(IFn.class); private static final Type RT_TYPE = Type.getType(RT.class); final static Type CLASS_TYPE = Type.getType(Class.class); final static Type NS_TYPE = Type.getType(Namespace.class); final static Type REFLECTOR_TYPE = Type.getType(Reflector.class); final static Type THROWABLE_TYPE = Type.getType(Throwable.class); final static Type BOOLEAN_OBJECT_TYPE = Type.getType(Boolean.class); final static Type IPERSISTENTMAP_TYPE = Type.getType(IPersistentMap.class); final static Type IOBJ_TYPE = Type.getType(IObj.class); private static final Type[][] ARG_TYPES; private static final Type[] EXCEPTION_TYPES = {Type.getType(Exception.class)}; static { OBJECT_TYPE = Type.getType(Object.class); ARG_TYPES = new Type[MAX_POSITIONAL_ARITY + 2][]; 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; } Type[] a = new Type[MAX_POSITIONAL_ARITY + 1]; for(int j = 0; j < MAX_POSITIONAL_ARITY; j++) a[j] = OBJECT_TYPE; a[MAX_POSITIONAL_ARITY] = Type.getType("[Ljava/lang/Object;"); ARG_TYPES[MAX_POSITIONAL_ARITY + 1] = a; } //symbol->localbinding static final public Var LOCAL_ENV = Var.create(null); //vector<localbinding> static final public Var LOOP_LOCALS = Var.create(); //Label static final public Var LOOP_LABEL = Var.create(); //vector<object> static final public Var CONSTANTS = Var.create(); //keyword->constid static final public Var KEYWORDS = Var.create(); //var->constid static final public Var VARS = Var.create(); //FnFrame static final public Var METHOD = Var.create(null); //null or not static final public Var IN_CATCH_FINALLY = Var.create(null); //DynamicClassLoader static final public Var LOADER = Var.create(); //String static final public Var SOURCE = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")), Symbol.create("*source-path*"), "NO_SOURCE_FILE"); //String static final public Var SOURCE_PATH = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")), Symbol.create("*file*"), "NO_SOURCE_PATH"); //String static final public Var COMPILE_PATH = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")), Symbol.create("*compile-path*"), null); //boolean static final public Var COMPILE_FILES = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")), Symbol.create("*compile-files*"), Boolean.FALSE); //Integer static final public Var LINE = Var.create(0); //Integer static final public Var LINE_BEFORE = Var.create(0); static final public Var LINE_AFTER = Var.create(0); //Integer static final public Var NEXT_LOCAL_NUM = Var.create(0); //Integer static final public Var RET_LOCAL_NUM = Var.create(); public enum C{ STATEMENT, //value ignored EXPRESSION, //value required RETURN, //tail position relative to enclosing recur frame EVAL } interface Expr{ Object eval() throws Exception; void emit(C context, FnExpr fn, GeneratorAdapter gen); boolean hasJavaClass() throws Exception; Class getJavaClass() throws Exception; } public static abstract class UntypedExpr implements Expr{ public Class getJavaClass(){ throw new IllegalArgumentException("Has no Java class"); } public boolean hasJavaClass(){ return false; } } interface IParser{ Expr parse(C context, Object form) throws Exception; } static boolean isSpecial(Object sym){ return specials.containsKey(sym); } static Symbol resolveSymbol(Symbol sym){ //already qualified or classname? if(sym.name.indexOf('.') > 0) return sym; if(sym.ns != null) { Namespace ns = namespaceFor(sym); if(ns == null || ns.name.name == sym.ns) return sym; return Symbol.create(ns.name.name, sym.name); } Object o = currentNS().getMapping(sym); if(o == null) return Symbol.intern(currentNS().name.name, sym.name); else if(o instanceof Class) return Symbol.intern(null, ((Class) o).getName()); else if(o instanceof Var) { Var v = (Var) o; return Symbol.create(v.ns.name.name, v.sym.name); } return null; } static class DefExpr implements Expr{ public final Var var; public final Expr init; public final Expr meta; public final boolean initProvided; public final String source; public final int line; final static Method bindRootMethod = Method.getMethod("void bindRoot(Object)"); final static Method setTagMethod = Method.getMethod("void setTag(clojure.lang.Symbol)"); final static Method setMetaMethod = Method.getMethod("void setMeta(clojure.lang.IPersistentMap)"); final static Method symcreate = Method.getMethod("clojure.lang.Symbol create(String, String)"); public DefExpr(String source, int line, Var var, Expr init, Expr meta, boolean initProvided){ this.source = source; this.line = line; this.var = var; this.init = init; this.meta = meta; this.initProvided = initProvided; } public Object eval() throws Exception{ try { if(initProvided) { // if(init instanceof FnExpr && ((FnExpr) init).closes.count()==0) // var.bindRoot(new FnLoaderThunk((FnExpr) init,var)); // else var.bindRoot(init.eval()); } if(meta != null) { var.setMeta((IPersistentMap) meta.eval()); } return var; } catch(Throwable e) { if(!(e instanceof CompilerException)) throw new CompilerException(source, line, e); else throw (CompilerException) e; } } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ fn.emitVar(gen, var); if(initProvided) { gen.dup(); init.emit(C.EXPRESSION, fn, gen); gen.invokeVirtual(VAR_TYPE, bindRootMethod); } if(meta != null) { gen.dup(); meta.emit(C.EXPRESSION, fn, gen); gen.checkCast(IPERSISTENTMAP_TYPE); gen.invokeVirtual(VAR_TYPE, setMetaMethod); } if(context == C.STATEMENT) gen.pop(); } public boolean hasJavaClass(){ return true; } public Class getJavaClass(){ return Var.class; } static class Parser implements IParser{ public Expr parse(C context, Object form) throws Exception{ //(def x) or (def x initexpr) if(RT.count(form) > 3) throw new Exception("Too many arguments to def"); else if(RT.count(form) < 2) throw new Exception("Too few arguments to def"); else if(!(RT.second(form) instanceof Symbol)) throw new Exception("First argument to def must be a Symbol"); Symbol sym = (Symbol) RT.second(form); Var v = lookupVar(sym, true); if(v == null) throw new Exception("Can't refer to qualified var that doesn't exist"); if(!v.ns.equals(currentNS())) { if(sym.ns == null) throw new Exception("Name conflict, can't def " + sym + " because namespace: " + currentNS().name + " refers to:" + v); else throw new Exception("Can't create defs outside of current ns"); } IPersistentMap mm = sym.meta(); Object source_path = SOURCE_PATH.get(); source_path = source_path == null ? "NO_SOURCE_FILE" : source_path; mm = (IPersistentMap) RT.assoc(mm, RT.LINE_KEY, LINE.get()).assoc(RT.FILE_KEY, source_path); Expr meta = analyze(context == C.EVAL ? context : C.EXPRESSION, mm); return new DefExpr((String) SOURCE.deref(), (Integer) LINE.deref(), v, analyze(context == C.EVAL ? context : C.EXPRESSION, RT.third(form), v.sym.name), meta, RT.count(form) == 3); } } } public static class AssignExpr implements Expr{ public final AssignableExpr target; public final Expr val; public AssignExpr(AssignableExpr target, Expr val){ this.target = target; this.val = val; } public Object eval() throws Exception{ return target.evalAssign(val); } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ target.emitAssign(context, fn, gen, val); } public boolean hasJavaClass() throws Exception{ return val.hasJavaClass(); } public Class getJavaClass() throws Exception{ return val.getJavaClass(); } static class Parser implements IParser{ public Expr parse(C context, Object frm) throws Exception{ ISeq form = (ISeq) frm; if(RT.length(form) != 3) throw new IllegalArgumentException("Malformed assignment, expecting (set! target val)"); Expr target = analyze(C.EXPRESSION, RT.second(form)); if(!(target instanceof AssignableExpr)) throw new IllegalArgumentException("Invalid assignment target"); return new AssignExpr((AssignableExpr) target, analyze(C.EXPRESSION, RT.third(form))); } } } public static class VarExpr implements Expr, AssignableExpr{ public final Var var; public final Object tag; final static Method getMethod = Method.getMethod("Object get()"); final static Method setMethod = Method.getMethod("Object set(Object)"); public VarExpr(Var var, Symbol tag){ this.var = var; this.tag = tag != null ? tag : var.getTag(); } public Object eval() throws Exception{ return var.deref(); } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ fn.emitVar(gen, var); gen.invokeVirtual(VAR_TYPE, getMethod); if(context == C.STATEMENT) { gen.pop(); } } public boolean hasJavaClass(){ return tag != null; } public Class getJavaClass() throws Exception{ return HostExpr.tagToClass(tag); } public Object evalAssign(Expr val) throws Exception{ return var.set(val.eval()); } public void emitAssign(C context, FnExpr fn, GeneratorAdapter gen, Expr val){ fn.emitVar(gen, var); val.emit(C.EXPRESSION, fn, gen); gen.invokeVirtual(VAR_TYPE, setMethod); if(context == C.STATEMENT) gen.pop(); } } public static class TheVarExpr implements Expr{ public final Var var; public TheVarExpr(Var var){ this.var = var; } public Object eval() throws Exception{ return var; } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ fn.emitVar(gen, var); if(context == C.STATEMENT) gen.pop(); } public boolean hasJavaClass(){ return true; } public Class getJavaClass() throws ClassNotFoundException{ return Var.class; } static class Parser implements IParser{ public Expr parse(C context, Object form) throws Exception{ Symbol sym = (Symbol) RT.second(form); Var v = lookupVar(sym, false); if(v != null) return new TheVarExpr(v); throw new Exception("Unable to resolve var: " + sym + " in this context"); } } } public static class KeywordExpr implements Expr{ public final Keyword k; public KeywordExpr(Keyword k){ this.k = k; } public Object eval() throws Exception{ return k; } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ fn.emitKeyword(gen, k); if(context == C.STATEMENT) gen.pop(); } public boolean hasJavaClass(){ return true; } public Class getJavaClass() throws ClassNotFoundException{ return Keyword.class; } } public static class ImportExpr implements Expr{ public final String c; final static Method forNameMethod = Method.getMethod("Class forName(String)"); final static Method importClassMethod = Method.getMethod("Class importClass(Class)"); final static Method derefMethod = Method.getMethod("Object deref()"); public ImportExpr(String c){ this.c = c; } public Object eval() throws Exception{ Namespace ns = (Namespace) RT.CURRENT_NS.deref(); ns.importClass(RT.classForName(c)); return null; } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ gen.getStatic(RT_TYPE,"CURRENT_NS",VAR_TYPE); gen.invokeVirtual(VAR_TYPE, derefMethod); gen.checkCast(NS_TYPE); gen.push(c); gen.invokeStatic(CLASS_TYPE, forNameMethod); gen.invokeVirtual(NS_TYPE, importClassMethod); if(context == C.STATEMENT) gen.pop(); } public boolean hasJavaClass(){ return false; } public Class getJavaClass() throws ClassNotFoundException{ throw new IllegalArgumentException("ImportExpr has no Java class"); } static class Parser implements IParser{ public Expr parse(C context, Object form) throws Exception{ return new ImportExpr((String) RT.second(form)); } } } public static abstract class LiteralExpr implements Expr{ abstract Object val(); public Object eval(){ return val(); } } static interface AssignableExpr{ Object evalAssign(Expr val) throws Exception; void emitAssign(C context, FnExpr fn, GeneratorAdapter gen, Expr val); } static public interface MaybePrimitiveExpr{ public void emitUnboxed(C context, FnExpr fn, GeneratorAdapter gen); } static public abstract class HostExpr implements Expr, MaybePrimitiveExpr{ final static Type BOOLEAN_TYPE = Type.getType(Boolean.class); final static Type CHAR_TYPE = Type.getType(Character.class); final static Type INTEGER_TYPE = Type.getType(Integer.class); final static Type LONG_TYPE = Type.getType(Long.class); final static Type FLOAT_TYPE = Type.getType(Float.class); final static Type DOUBLE_TYPE = Type.getType(Double.class); final static Type SHORT_TYPE = Type.getType(Short.class); final static Type BYTE_TYPE = Type.getType(Byte.class); final static Type NUMBER_TYPE = Type.getType(Number.class); final static Method charValueMethod = Method.getMethod("char charValue()"); final static Method booleanValueMethod = Method.getMethod("boolean booleanValue()"); final static Method charValueOfMethod = Method.getMethod("Character valueOf(char)"); final static Method intValueOfMethod = Method.getMethod("Integer valueOf(int)"); final static Method longValueOfMethod = Method.getMethod("Long valueOf(long)"); final static Method floatValueOfMethod = Method.getMethod("Float valueOf(float)"); final static Method doubleValueOfMethod = Method.getMethod("Double valueOf(double)"); final static Method shortValueOfMethod = Method.getMethod("Short valueOf(short)"); final static Method byteValueOfMethod = Method.getMethod("Byte valueOf(byte)"); final static Method intValueMethod = Method.getMethod("int intValue()"); final static Method longValueMethod = Method.getMethod("long longValue()"); final static Method floatValueMethod = Method.getMethod("float floatValue()"); final static Method doubleValueMethod = Method.getMethod("double doubleValue()"); final static Method byteValueMethod = Method.getMethod("byte byteValue()"); final static Method shortValueMethod = Method.getMethod("short shortValue()"); final static Method fromIntMethod = Method.getMethod("clojure.lang.Num from(int)"); final static Method fromLongMethod = Method.getMethod("clojure.lang.Num from(long)"); final static Method fromDoubleMethod = Method.getMethod("clojure.lang.Num from(double)"); /* public static void emitBoxReturn(FnExpr fn, GeneratorAdapter gen, Class returnType){ if(returnType.isPrimitive()) { if(returnType == boolean.class) { Label falseLabel = gen.newLabel(); Label endLabel = gen.newLabel(); gen.ifZCmp(GeneratorAdapter.EQ, falseLabel); gen.getStatic(RT_TYPE, "T", KEYWORD_TYPE); gen.goTo(endLabel); gen.mark(falseLabel); NIL_EXPR.emit(C.EXPRESSION, fn, gen); gen.mark(endLabel); } else if(returnType == void.class) { NIL_EXPR.emit(C.EXPRESSION, fn, gen); } else if(returnType == char.class) { gen.invokeStatic(CHAR_TYPE, charValueOfMethod); } else if(returnType == int.class) gen.invokeStatic(INTEGER_TYPE, intValueOfMethod); //m = fromIntMethod; else { Method m = fromIntMethod; if(returnType == int.class) m = fromIntMethod; else if(returnType == float.class) { gen.visitInsn(F2D); m = fromDoubleMethod; } else if(returnType == double.class) m = fromDoubleMethod; else if(returnType == long.class) m = fromLongMethod; else if(returnType == byte.class) m = fromIntMethod; else if(returnType == short.class) m = fromIntMethod; gen.invokeStatic(NUM_TYPE, m); } } } */ //* public static void emitBoxReturn(FnExpr fn, GeneratorAdapter gen, Class returnType){ if(returnType.isPrimitive()) { if(returnType == boolean.class) { Label falseLabel = gen.newLabel(); Label endLabel = gen.newLabel(); gen.ifZCmp(GeneratorAdapter.EQ, falseLabel); gen.getStatic(BOOLEAN_OBJECT_TYPE, "TRUE", BOOLEAN_OBJECT_TYPE); gen.goTo(endLabel); gen.mark(falseLabel); gen.getStatic(BOOLEAN_OBJECT_TYPE, "FALSE", BOOLEAN_OBJECT_TYPE); // NIL_EXPR.emit(C.EXPRESSION, fn, gen); gen.mark(endLabel); } else if(returnType == void.class) { NIL_EXPR.emit(C.EXPRESSION, fn, gen); } else if(returnType == char.class) { gen.invokeStatic(CHAR_TYPE, charValueOfMethod); } else { if(returnType == int.class) //gen.invokeStatic(NUM_TYPE, fromIntMethod); gen.invokeStatic(INTEGER_TYPE, intValueOfMethod); else if(returnType == float.class) { //gen.visitInsn(F2D); gen.invokeStatic(FLOAT_TYPE, floatValueOfMethod); //m = floatValueOfMethod; } else if(returnType == double.class) gen.invokeStatic(DOUBLE_TYPE, doubleValueOfMethod); else if(returnType == long.class) gen.invokeStatic(LONG_TYPE, longValueOfMethod); else if(returnType == byte.class) gen.invokeStatic(BYTE_TYPE, byteValueOfMethod); else if(returnType == short.class) gen.invokeStatic(SHORT_TYPE, shortValueOfMethod); } } } //*/ public static void emitUnboxArg(FnExpr fn, GeneratorAdapter gen, Class paramType){ if(paramType.isPrimitive()) { if(paramType == boolean.class) { gen.checkCast(BOOLEAN_TYPE); gen.invokeVirtual(BOOLEAN_TYPE, booleanValueMethod); // Label falseLabel = gen.newLabel(); // Label endLabel = gen.newLabel(); // gen.ifNull(falseLabel); // gen.push(1); // gen.goTo(endLabel); // gen.mark(falseLabel); // gen.push(0); // gen.mark(endLabel); } else if(paramType == char.class) { gen.checkCast(CHAR_TYPE); gen.invokeVirtual(CHAR_TYPE, charValueMethod); } else { Method m = intValueMethod; gen.checkCast(NUMBER_TYPE); if(paramType == int.class) m = intValueMethod; else if(paramType == float.class) m = floatValueMethod; else if(paramType == double.class) m = doubleValueMethod; else if(paramType == long.class) m = longValueMethod; else if(paramType == byte.class) m = byteValueMethod; else if(paramType == short.class) m = shortValueMethod; gen.invokeVirtual(NUMBER_TYPE, m); } } else { gen.checkCast(Type.getType(paramType)); } } static class Parser implements IParser{ public Expr parse(C context, Object frm) throws Exception{ ISeq form = (ISeq) frm; //(. x fieldname-sym) or //(. x 0-ary-method) // (. x methodname-sym args+) // (. x (methodname-sym args?)) if(RT.length(form) < 3) throw new IllegalArgumentException("Malformed member expression, expecting (. target member ...)"); //determine static or instance //static target must be symbol, either fully.qualified.Classname or Classname that has been imported int line = (Integer) LINE.deref(); String source = (String) SOURCE.deref(); Class c = maybeClass(RT.second(form), false); //at this point c will be non-null if static Expr instance = null; if(c == null) instance = analyze(context == C.EVAL ? context : C.EXPRESSION, RT.second(form)); boolean maybeField = RT.length(form) == 3 && RT.third(form) instanceof Symbol; if(maybeField) { Symbol sym = (Symbol) RT.third(form); if(c != null) maybeField = Reflector.getMethods(c, 0, sym.name, true).size() == 0; else if(instance != null && instance.hasJavaClass() && instance.getJavaClass() != null) maybeField = Reflector.getMethods(instance.getJavaClass(), 0, sym.name, false).size() == 0; } if(maybeField) //field { Symbol sym = (Symbol) RT.third(form); Symbol tag = tagOf(form); if(c != null) { return new StaticFieldExpr(line, c, sym.name, tag); } else return new InstanceFieldExpr(line, instance, sym.name, tag); } else { ISeq call = (ISeq) ((RT.third(form) instanceof ISeq) ? RT.third(form) : RT.next(RT.next(form))); if(!(RT.first(call) instanceof Symbol)) throw new IllegalArgumentException("Malformed member expression"); Symbol sym = (Symbol) RT.first(call); Symbol tag = tagOf(form); PersistentVector args = PersistentVector.EMPTY; for(ISeq s = RT.next(call); s != null; s = s.next()) args = args.cons(analyze(context == C.EVAL ? context : C.EXPRESSION, s.first())); if(c != null) return new StaticMethodExpr(source, line, tag, c, sym.name, args); else return new InstanceMethodExpr(source, line, tag, instance, sym.name, args); } } } private static Class maybeClass(Object form, boolean stringOk) throws Exception{ if(form instanceof Class) return (Class) form; Class c = null; if(form instanceof Symbol) { Symbol sym = (Symbol) form; if(sym.ns == null) //if ns-qualified can't be classname { if(sym.name.indexOf('.') > 0 || sym.name.charAt(0) == '[') c = RT.classForName(sym.name); else { Object o = currentNS().getMapping(sym); if(o instanceof Class) c = (Class) o; } } } else if(stringOk && form instanceof String) c = RT.classForName((String) form); return c; } /* private static String maybeClassName(Object form, boolean stringOk){ String className = null; if(form instanceof Symbol) { Symbol sym = (Symbol) form; if(sym.ns == null) //if ns-qualified can't be classname { if(sym.name.indexOf('.') > 0 || sym.name.charAt(0) == '[') className = sym.name; else { IPersistentMap imports = (IPersistentMap) ((Var) RT.NS_IMPORTS.get()).get(); className = (String) imports.valAt(sym); } } } else if(stringOk && form instanceof String) className = (String) form; return className; } */ static Class tagToClass(Object tag) throws Exception{ Class c = maybeClass(tag, true); if(tag instanceof Symbol) { Symbol sym = (Symbol) tag; if(sym.ns == null) //if ns-qualified can't be classname { if(sym.name.equals("objects")) c = Object[].class; else if(sym.name.equals("ints")) c = int[].class; else if(sym.name.equals("longs")) c = long[].class; else if(sym.name.equals("floats")) c = float[].class; else if(sym.name.equals("doubles")) c = double[].class; else if(sym.name.equals("chars")) c = char[].class; else if(sym.name.equals("shorts")) c = short[].class; else if(sym.name.equals("bytes")) c = byte[].class; else if(sym.name.equals("booleans")) c = boolean[].class; } } if(c != null) return c; throw new IllegalArgumentException("Unable to resolve classname: " + tag); } } static abstract class FieldExpr extends HostExpr{ } static class InstanceFieldExpr extends FieldExpr implements AssignableExpr{ public final Expr target; public final Class targetClass; public final java.lang.reflect.Field field; public final String fieldName; public final int line; public final Symbol tag; final static Method invokeNoArgInstanceMember = Method.getMethod("Object invokeNoArgInstanceMember(Object,String)"); final static Method setInstanceFieldMethod = Method.getMethod("Object setInstanceField(Object,String,Object)"); public InstanceFieldExpr(int line, Expr target, String fieldName, Symbol tag) throws Exception{ this.target = target; this.targetClass = target.hasJavaClass() ? target.getJavaClass() : null; this.field = targetClass != null ? Reflector.getField(targetClass, fieldName, false) : null; this.fieldName = fieldName; this.line = line; this.tag = tag; if(field == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) { ((PrintWriter) RT.ERR.deref()) .format("Reflection warning, %s:%d - reference to field %s can't be resolved.\n", SOURCE_PATH.deref(), line, fieldName); } } public Object eval() throws Exception{ return Reflector.invokeNoArgInstanceMember(target.eval(), fieldName); } public void emitUnboxed(C context, FnExpr fn, GeneratorAdapter gen){ gen.visitLineNumber(line, gen.mark()); if(targetClass != null && field != null) { target.emit(C.EXPRESSION, fn, gen); gen.checkCast(Type.getType(targetClass)); gen.getField(Type.getType(targetClass), fieldName, Type.getType(field.getType())); } else throw new UnsupportedOperationException("Unboxed emit of unknown member"); } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ gen.visitLineNumber(line, gen.mark()); if(targetClass != null && field != null) { target.emit(C.EXPRESSION, fn, gen); gen.checkCast(Type.getType(targetClass)); gen.getField(Type.getType(targetClass), fieldName, Type.getType(field.getType())); //if(context != C.STATEMENT) HostExpr.emitBoxReturn(fn, gen, field.getType()); if(context == C.STATEMENT) { gen.pop(); } } else { target.emit(C.EXPRESSION, fn, gen); gen.push(fieldName); gen.invokeStatic(REFLECTOR_TYPE, invokeNoArgInstanceMember); if(context == C.STATEMENT) gen.pop(); } } public boolean hasJavaClass() throws Exception{ return field != null || tag != null; } public Class getJavaClass() throws Exception{ return tag != null ? HostExpr.tagToClass(tag) : field.getType(); } public Object evalAssign(Expr val) throws Exception{ return Reflector.setInstanceField(target.eval(), fieldName, val.eval()); } public void emitAssign(C context, FnExpr fn, GeneratorAdapter gen, Expr val){ gen.visitLineNumber(line, gen.mark()); if(targetClass != null && field != null) { target.emit(C.EXPRESSION, fn, gen); gen.checkCast(Type.getType(targetClass)); val.emit(C.EXPRESSION, fn, gen); gen.dupX1(); HostExpr.emitUnboxArg(fn, gen, field.getType()); gen.putField(Type.getType(targetClass), fieldName, Type.getType(field.getType())); } else { target.emit(C.EXPRESSION, fn, gen); gen.push(fieldName); val.emit(C.EXPRESSION, fn, gen); gen.invokeStatic(REFLECTOR_TYPE, setInstanceFieldMethod); } if(context == C.STATEMENT) gen.pop(); } } static class StaticFieldExpr extends FieldExpr implements AssignableExpr{ //final String className; public final String fieldName; public final Class c; public final java.lang.reflect.Field field; public final Symbol tag; // final static Method getStaticFieldMethod = Method.getMethod("Object getStaticField(String,String)"); // final static Method setStaticFieldMethod = Method.getMethod("Object setStaticField(String,String,Object)"); final int line; public StaticFieldExpr(int line, Class c, String fieldName, Symbol tag) throws Exception{ //this.className = className; this.fieldName = fieldName; this.line = line; //c = Class.forName(className); this.c = c; field = c.getField(fieldName); this.tag = tag; } public Object eval() throws Exception{ return Reflector.getStaticField(c, fieldName); } public void emitUnboxed(C context, FnExpr fn, GeneratorAdapter gen){ gen.visitLineNumber(line, gen.mark()); gen.getStatic(Type.getType(c), fieldName, Type.getType(field.getType())); } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ gen.visitLineNumber(line, gen.mark()); gen.getStatic(Type.getType(c), fieldName, Type.getType(field.getType())); //if(context != C.STATEMENT) HostExpr.emitBoxReturn(fn, gen, field.getType()); if(context == C.STATEMENT) { gen.pop(); } // gen.push(className); // gen.push(fieldName); // gen.invokeStatic(REFLECTOR_TYPE, getStaticFieldMethod); } public boolean hasJavaClass(){ return true; } public Class getJavaClass() throws Exception{ //Class c = Class.forName(className); //java.lang.reflect.Field field = c.getField(fieldName); return tag != null ? HostExpr.tagToClass(tag) : field.getType(); } public Object evalAssign(Expr val) throws Exception{ return Reflector.setStaticField(c, fieldName, val.eval()); } public void emitAssign(C context, FnExpr fn, GeneratorAdapter gen, Expr val){ gen.visitLineNumber(line, gen.mark()); val.emit(C.EXPRESSION, fn, gen); gen.dup(); HostExpr.emitUnboxArg(fn, gen, field.getType()); gen.putStatic(Type.getType(c), fieldName, Type.getType(field.getType())); if(context == C.STATEMENT) gen.pop(); } } static Class maybePrimitiveType(Expr e){ try { if(e instanceof MaybePrimitiveExpr && e.hasJavaClass()) { Class c = e.getJavaClass(); if(Util.isPrimitive(c)) return c; } } catch(Exception ex) { throw new RuntimeException(ex); } return null; } static abstract class MethodExpr extends HostExpr{ static void emitArgsAsArray(IPersistentVector args, FnExpr fn, GeneratorAdapter gen){ gen.push(args.count()); gen.newArray(OBJECT_TYPE); for(int i = 0; i < args.count(); i++) { gen.dup(); gen.push(i); ((Expr) args.nth(i)).emit(C.EXPRESSION, fn, gen); gen.arrayStore(OBJECT_TYPE); } } public static void emitTypedArgs(FnExpr fn, GeneratorAdapter gen, Class[] parameterTypes, IPersistentVector args){ for(int i = 0; i < parameterTypes.length; i++) { Expr e = (Expr) args.nth(i); try { if(maybePrimitiveType(e) == parameterTypes[i]) { ((MaybePrimitiveExpr) e).emitUnboxed(C.EXPRESSION, fn, gen); } else { e.emit(C.EXPRESSION, fn, gen); HostExpr.emitUnboxArg(fn, gen, parameterTypes[i]); } } catch(Exception e1) { e1.printStackTrace((PrintWriter) RT.ERR .deref()); //To change body of catch statement use File | Settings | File Templates. } } } } static class InstanceMethodExpr extends MethodExpr{ public final Expr target; public final String methodName; public final IPersistentVector args; public final String source; public final int line; public final Symbol tag; public final java.lang.reflect.Method method; final static Method invokeInstanceMethodMethod = Method.getMethod("Object invokeInstanceMethod(Object,String,Object[])"); public InstanceMethodExpr(String source, int line, Symbol tag, Expr target, String methodName, IPersistentVector args) throws Exception{ this.source = source; this.line = line; this.args = args; this.methodName = methodName; this.target = target; this.tag = tag; if(target.hasJavaClass() && target.getJavaClass() != null) { List methods = Reflector.getMethods(target.getJavaClass(), args.count(), methodName, false); if(methods.isEmpty()) method = null; //throw new IllegalArgumentException("No matching method found"); else { int methodidx = 0; if(methods.size() > 1) { ArrayList<Class[]> params = new ArrayList(); ArrayList<Class> rets = new ArrayList(); for(int i = 0; i < methods.size(); i++) { java.lang.reflect.Method m = (java.lang.reflect.Method) methods.get(i); params.add(m.getParameterTypes()); rets.add(m.getReturnType()); } methodidx = getMatchingParams(methodName, params, args, rets); } java.lang.reflect.Method m = (java.lang.reflect.Method) (methodidx >= 0 ? methods.get(methodidx) : null); if(m != null && !Modifier.isPublic(m.getDeclaringClass().getModifiers())) { //public method of non-public class, try to find it in hierarchy m = Reflector.getAsMethodOfPublicBase(m.getDeclaringClass(), m); } method = m; } } else method = null; if(method == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) { ((PrintWriter) RT.ERR.deref()) .format("Reflection warning, %s:%d - call to %s can't be resolved.\n", SOURCE_PATH.deref(), line, methodName); } } public Object eval() throws Exception{ try { Object targetval = target.eval(); Object[] argvals = new Object[args.count()]; for(int i = 0; i < args.count(); i++) argvals[i] = ((Expr) args.nth(i)).eval(); if(method != null) { LinkedList ms = new LinkedList(); ms.add(method); return Reflector.invokeMatchingMethod(methodName, ms, targetval, argvals); } return Reflector.invokeInstanceMethod(targetval, methodName, argvals); } catch(Throwable e) { if(!(e instanceof CompilerException)) throw new CompilerException(source, line, e); else throw (CompilerException) e; } } public void emitUnboxed(C context, FnExpr fn, GeneratorAdapter gen){ gen.visitLineNumber(line, gen.mark()); if(method != null) { Type type = Type.getType(method.getDeclaringClass()); target.emit(C.EXPRESSION, fn, gen); //if(!method.getDeclaringClass().isInterface()) gen.checkCast(type); MethodExpr.emitTypedArgs(fn, gen, method.getParameterTypes(), args); if(context == C.RETURN) { FnMethod method = (FnMethod) METHOD.deref(); method.emitClearLocals(gen); } Method m = new Method(methodName, Type.getReturnType(method), Type.getArgumentTypes(method)); if(method.getDeclaringClass().isInterface()) gen.invokeInterface(type, m); else gen.invokeVirtual(type, m); } else throw new UnsupportedOperationException("Unboxed emit of unknown member"); } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ gen.visitLineNumber(line, gen.mark()); if(method != null) { Type type = Type.getType(method.getDeclaringClass()); target.emit(C.EXPRESSION, fn, gen); //if(!method.getDeclaringClass().isInterface()) gen.checkCast(type); MethodExpr.emitTypedArgs(fn, gen, method.getParameterTypes(), args); if(context == C.RETURN) { FnMethod method = (FnMethod) METHOD.deref(); method.emitClearLocals(gen); } Method m = new Method(methodName, Type.getReturnType(method), Type.getArgumentTypes(method)); if(method.getDeclaringClass().isInterface()) gen.invokeInterface(type, m); else gen.invokeVirtual(type, m); //if(context != C.STATEMENT || method.getReturnType() == Void.TYPE) HostExpr.emitBoxReturn(fn, gen, method.getReturnType()); } else { target.emit(C.EXPRESSION, fn, gen); gen.push(methodName); emitArgsAsArray(args, fn, gen); if(context == C.RETURN) { FnMethod method = (FnMethod) METHOD.deref(); method.emitClearLocals(gen); } gen.invokeStatic(REFLECTOR_TYPE, invokeInstanceMethodMethod); } if(context == C.STATEMENT) gen.pop(); } public boolean hasJavaClass(){ return method != null || tag != null; } public Class getJavaClass() throws Exception{ return tag != null ? HostExpr.tagToClass(tag) : method.getReturnType(); } } static class StaticMethodExpr extends MethodExpr{ //final String className; public final Class c; public final String methodName; public final IPersistentVector args; public final String source; public final int line; public final java.lang.reflect.Method method; public final Symbol tag; final static Method forNameMethod = Method.getMethod("Class forName(String)"); final static Method invokeStaticMethodMethod = Method.getMethod("Object invokeStaticMethod(Class,String,Object[])"); public StaticMethodExpr(String source, int line, Symbol tag, Class c, String methodName, IPersistentVector args) throws Exception{ this.c = c; this.methodName = methodName; this.args = args; this.source = source; this.line = line; this.tag = tag; List methods = Reflector.getMethods(c, args.count(), methodName, true); if(methods.isEmpty()) throw new IllegalArgumentException("No matching method: " + methodName); int methodidx = 0; if(methods.size() > 1) { ArrayList<Class[]> params = new ArrayList(); ArrayList<Class> rets = new ArrayList(); for(int i = 0; i < methods.size(); i++) { java.lang.reflect.Method m = (java.lang.reflect.Method) methods.get(i); params.add(m.getParameterTypes()); rets.add(m.getReturnType()); } methodidx = getMatchingParams(methodName, params, args, rets); } method = (java.lang.reflect.Method) (methodidx >= 0 ? methods.get(methodidx) : null); if(method == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) { ((PrintWriter) RT.ERR.deref()) .format("Reflection warning, %s:%d - call to %s can't be resolved.\n", SOURCE_PATH.deref(), line, methodName); } } public Object eval() throws Exception{ try { Object[] argvals = new Object[args.count()]; for(int i = 0; i < args.count(); i++) argvals[i] = ((Expr) args.nth(i)).eval(); if(method != null) { LinkedList ms = new LinkedList(); ms.add(method); return Reflector.invokeMatchingMethod(methodName, ms, null, argvals); } return Reflector.invokeStaticMethod(c, methodName, argvals); } catch(Throwable e) { if(!(e instanceof CompilerException)) throw new CompilerException(source, line, e); else throw (CompilerException) e; } } public void emitUnboxed(C context, FnExpr fn, GeneratorAdapter gen){ gen.visitLineNumber(line, gen.mark()); if(method != null) { MethodExpr.emitTypedArgs(fn, gen, method.getParameterTypes(), args); //Type type = Type.getObjectType(className.replace('.', '/')); if(context == C.RETURN) { FnMethod method = (FnMethod) METHOD.deref(); method.emitClearLocals(gen); } Type type = Type.getType(c); Method m = new Method(methodName, Type.getReturnType(method), Type.getArgumentTypes(method)); gen.invokeStatic(type, m); } else throw new UnsupportedOperationException("Unboxed emit of unknown member"); } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ gen.visitLineNumber(line, gen.mark()); if(method != null) { MethodExpr.emitTypedArgs(fn, gen, method.getParameterTypes(), args); //Type type = Type.getObjectType(className.replace('.', '/')); if(context == C.RETURN) { FnMethod method = (FnMethod) METHOD.deref(); method.emitClearLocals(gen); } Type type = Type.getType(c); Method m = new Method(methodName, Type.getReturnType(method), Type.getArgumentTypes(method)); gen.invokeStatic(type, m); //if(context != C.STATEMENT || method.getReturnType() == Void.TYPE) HostExpr.emitBoxReturn(fn, gen, method.getReturnType()); } else { gen.push(c.getName()); gen.invokeStatic(CLASS_TYPE, forNameMethod); gen.push(methodName); emitArgsAsArray(args, fn, gen); if(context == C.RETURN) { FnMethod method = (FnMethod) METHOD.deref(); method.emitClearLocals(gen); } gen.invokeStatic(REFLECTOR_TYPE, invokeStaticMethodMethod); } if(context == C.STATEMENT) gen.pop(); } public boolean hasJavaClass(){ return method != null || tag != null; } public Class getJavaClass() throws Exception{ return tag != null ? HostExpr.tagToClass(tag) : method.getReturnType(); } } static class UnresolvedVarExpr implements Expr{ public final Symbol symbol; public UnresolvedVarExpr(Symbol symbol){ this.symbol = symbol; } public boolean hasJavaClass(){ return false; } public Class getJavaClass() throws Exception{ throw new IllegalArgumentException( "UnresolvedVarExpr has no Java class"); } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ } public Object eval() throws Exception{ throw new IllegalArgumentException( "UnresolvedVarExpr cannot be evalled"); } } static class ConstantExpr extends LiteralExpr{ //stuff quoted vals in classloader at compile time, pull out at runtime //this won't work for static compilation... public final Object v; public final int id; public ConstantExpr(Object v){ this.v = v; this.id = registerConstant(v); // this.id = RT.nextID(); // DynamicClassLoader loader = (DynamicClassLoader) LOADER.get(); // loader.registerQuotedVal(id, v); } Object val(){ return v; } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ fn.emitConstant(gen, id); if(context == C.STATEMENT) { gen.pop(); // gen.loadThis(); // gen.invokeVirtual(OBJECT_TYPE, getClassMethod); // gen.invokeVirtual(CLASS_TYPE, getClassLoaderMethod); // gen.checkCast(DYNAMIC_CLASSLOADER_TYPE); // gen.push(id); // gen.invokeVirtual(DYNAMIC_CLASSLOADER_TYPE, getQuotedValMethod); } } public boolean hasJavaClass(){ return Modifier.isPublic(v.getClass().getModifiers()); //return false; } public Class getJavaClass() throws Exception{ return v.getClass(); //throw new IllegalArgumentException("Has no Java class"); } static class Parser implements IParser{ public Expr parse(C context, Object form){ Object v = RT.second(form); if(v == null) return NIL_EXPR; // Class fclass = v.getClass(); // if(fclass == Keyword.class) // return registerKeyword((Keyword) v); // else if(v instanceof Num) // return new NumExpr((Num) v); // else if(fclass == String.class) // return new StringExpr((String) v); // else if(fclass == Character.class) // return new CharExpr((Character) v); // else if(v instanceof IPersistentCollection && ((IPersistentCollection) v).count() == 0) // return new EmptyExpr(v); else return new ConstantExpr(v); } } } static class NilExpr extends LiteralExpr{ Object val(){ return null; } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ gen.visitInsn(Opcodes.ACONST_NULL); if(context == C.STATEMENT) gen.pop(); } public boolean hasJavaClass(){ return true; } public Class getJavaClass() throws Exception{ return null; } } final static NilExpr NIL_EXPR = new NilExpr(); static class BooleanExpr extends LiteralExpr{ public final boolean val; public BooleanExpr(boolean val){ this.val = val; } Object val(){ return val ? RT.T : RT.F; } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ if(val) gen.getStatic(BOOLEAN_OBJECT_TYPE, "TRUE", BOOLEAN_OBJECT_TYPE); else gen.getStatic(BOOLEAN_OBJECT_TYPE, "FALSE", BOOLEAN_OBJECT_TYPE); if(context == C.STATEMENT) { gen.pop(); } } public boolean hasJavaClass(){ return true; } public Class getJavaClass() throws Exception{ return Boolean.class; } } final static BooleanExpr TRUE_EXPR = new BooleanExpr(true); final static BooleanExpr FALSE_EXPR = new BooleanExpr(false); static class StringExpr extends LiteralExpr{ public final String str; public StringExpr(String str){ this.str = str; } Object val(){ return str; } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ if(context != C.STATEMENT) gen.push(str); } public boolean hasJavaClass(){ return true; } public Class getJavaClass() throws Exception{ return String.class; } } /* 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; } 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"); } } public boolean hasJavaClass(){ return true; } public Class getJavaClass() throws Exception{ return Num.class; } static void emitBigInteger(GeneratorAdapter gen, Num num){ gen.newInstance(BIGINT_TYPE); gen.dup(); gen.push(num.toString()); gen.invokeConstructor(BIGINT_TYPE, bigintFromStringCtor); } } 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; } 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); } } public boolean hasJavaClass(){ return true; } public Class getJavaClass() throws Exception{ return Character.class; } } */ static class MonitorEnterExpr extends UntypedExpr{ final Expr target; public MonitorEnterExpr(Expr target){ this.target = target; } public Object eval() throws Exception{ throw new UnsupportedOperationException("Can't eval monitor-enter"); } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ target.emit(C.EXPRESSION, fn, gen); gen.monitorEnter(); NIL_EXPR.emit(context, fn, gen); } static class Parser implements IParser{ public Expr parse(C context, Object form) throws Exception{ return new MonitorEnterExpr(analyze(C.EXPRESSION, RT.second(form))); } } } static class MonitorExitExpr extends UntypedExpr{ final Expr target; public MonitorExitExpr(Expr target){ this.target = target; } public Object eval() throws Exception{ throw new UnsupportedOperationException("Can't eval monitor-exit"); } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ target.emit(C.EXPRESSION, fn, gen); gen.monitorExit(); NIL_EXPR.emit(context, fn, gen); } static class Parser implements IParser{ public Expr parse(C context, Object form) throws Exception{ return new MonitorExitExpr(analyze(C.EXPRESSION, RT.second(form))); } } } public static class TryExpr implements Expr{ public final Expr tryExpr; public final Expr finallyExpr; public final PersistentVector catchExprs; public final int retLocal; public final int finallyLocal; public static class CatchClause{ //final String className; public final Class c; public final LocalBinding lb; public final Expr handler; Label label; Label endLabel; public CatchClause(Class c, LocalBinding lb, Expr handler){ this.c = c; this.lb = lb; this.handler = handler; } } public TryExpr(Expr tryExpr, PersistentVector catchExprs, Expr finallyExpr, int retLocal, int finallyLocal){ this.tryExpr = tryExpr; this.catchExprs = catchExprs; this.finallyExpr = finallyExpr; this.retLocal = retLocal; this.finallyLocal = finallyLocal; } public Object eval() throws Exception{ throw new UnsupportedOperationException("Can't eval try"); } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ Label startTry = gen.newLabel(); Label endTry = gen.newLabel(); Label endTryCatch = gen.newLabel(); Label end = gen.newLabel(); Label ret = gen.newLabel(); Label finallyLabel = gen.newLabel(); for(int i = 0; i < catchExprs.count(); i++) { CatchClause clause = (CatchClause) catchExprs.nth(i); clause.label = gen.newLabel(); clause.endLabel = gen.newLabel(); } gen.mark(startTry); tryExpr.emit(context, fn, gen); if(context != C.STATEMENT) gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), retLocal); gen.mark(endTry); if(finallyExpr != null) finallyExpr.emit(C.STATEMENT, fn, gen); gen.goTo(ret); for(int i = 0; i < catchExprs.count(); i++) { CatchClause clause = (CatchClause) catchExprs.nth(i); gen.mark(clause.label); //exception should be on stack //put in clause local gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), clause.lb.idx); clause.handler.emit(context, fn, gen); if(context != C.STATEMENT) gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), retLocal); gen.mark(clause.endLabel); if(finallyExpr != null) finallyExpr.emit(C.STATEMENT, fn, gen); gen.goTo(ret); } gen.mark(endTryCatch); if(finallyExpr != null) { gen.mark(finallyLabel); //exception should be on stack gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), finallyLocal); finallyExpr.emit(C.STATEMENT, fn, gen); gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), finallyLocal); gen.throwException(); } gen.mark(ret); if(context != C.STATEMENT) gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), retLocal); gen.mark(end); for(int i = 0; i < catchExprs.count(); i++) { CatchClause clause = (CatchClause) catchExprs.nth(i); gen.visitTryCatchBlock(startTry, endTry, clause.label, clause.c.getName().replace('.', '/')); } if(finallyExpr != null) gen.visitTryCatchBlock(startTry, endTryCatch, finallyLabel, null); for(int i = 0; i < catchExprs.count(); i++) { CatchClause clause = (CatchClause) catchExprs.nth(i); gen.visitLocalVariable(clause.lb.name, "Ljava/lang/Object;", null, clause.label, clause.endLabel, clause.lb.idx); } } public boolean hasJavaClass() throws Exception{ return tryExpr.hasJavaClass(); } public Class getJavaClass() throws Exception{ return tryExpr.getJavaClass(); } static class Parser implements IParser{ public Expr parse(C context, Object frm) throws Exception{ ISeq form = (ISeq) frm; // if(context == C.EVAL || context == C.EXPRESSION) if(context != C.RETURN) return analyze(context, RT.list(RT.list(FN, PersistentVector.EMPTY, form))); //(try try-expr* catch-expr* finally-expr?) //catch-expr: (catch class sym expr*) //finally-expr: (finally expr*) PersistentVector body = PersistentVector.EMPTY; PersistentVector catches = PersistentVector.EMPTY; Expr finallyExpr = null; boolean caught = false; int retLocal = getAndIncLocalNum(); int finallyLocal = getAndIncLocalNum(); for(ISeq fs = form.next(); fs != null; fs = fs.next()) { Object f = fs.first(); Object op = (f instanceof ISeq) ? ((ISeq) f).first() : null; if(!Util.equals(op, CATCH) && !Util.equals(op, FINALLY)) { if(caught) throw new Exception("Only catch or finally clause can follow catch in try expression"); body = body.cons(f); } else { if(Util.equals(op, CATCH)) { Class c = HostExpr.maybeClass(RT.second(f), false); if(c == null) throw new IllegalArgumentException("Unable to resolve classname: " + RT.second(f)); if(!(RT.third(f) instanceof Symbol)) throw new IllegalArgumentException( "Bad binding form, expected symbol, got: " + RT.third(f)); Symbol sym = (Symbol) RT.third(f); if(sym.getNamespace() != null) throw new Exception("Can't bind qualified name:" + sym); IPersistentMap dynamicBindings = RT.map(LOCAL_ENV, LOCAL_ENV.deref(), NEXT_LOCAL_NUM, NEXT_LOCAL_NUM.deref(), IN_CATCH_FINALLY, RT.T); try { Var.pushThreadBindings(dynamicBindings); LocalBinding lb = registerLocal(sym, (Symbol) (RT.second(f) instanceof Symbol ? RT.second(f) : null), null); Expr handler = (new BodyExpr.Parser()).parse(context, RT.next(RT.next(RT.next(f)))); catches = catches.cons(new CatchClause(c, lb, handler)); } finally { Var.popThreadBindings(); } caught = true; } else //finally { if(fs.next() != null) throw new Exception("finally clause must be last in try expression"); try { Var.pushThreadBindings(RT.map(IN_CATCH_FINALLY, RT.T)); finallyExpr = (new BodyExpr.Parser()).parse(C.STATEMENT, RT.next(f)); } finally { Var.popThreadBindings(); } } } } return new TryExpr((new BodyExpr.Parser()).parse(context, RT.seq(body)), catches, finallyExpr, retLocal, finallyLocal); } } } //static class TryFinallyExpr implements Expr{ // final Expr tryExpr; // final Expr finallyExpr; // // // public TryFinallyExpr(Expr tryExpr, Expr finallyExpr){ // this.tryExpr = tryExpr; // this.finallyExpr = finallyExpr; // } // // public Object eval() throws Exception{ // throw new UnsupportedOperationException("Can't eval try"); // } // // public void emit(C context, FnExpr fn, GeneratorAdapter gen){ // Label startTry = gen.newLabel(); // Label endTry = gen.newLabel(); // Label end = gen.newLabel(); // Label finallyLabel = gen.newLabel(); // gen.visitTryCatchBlock(startTry, endTry, finallyLabel, null); // gen.mark(startTry); // tryExpr.emit(context, fn, gen); // gen.mark(endTry); // finallyExpr.emit(C.STATEMENT, fn, gen); // gen.goTo(end); // gen.mark(finallyLabel); // //exception should be on stack // finallyExpr.emit(C.STATEMENT, fn, gen); // gen.throwException(); // gen.mark(end); // } // // public boolean hasJavaClass() throws Exception{ // return tryExpr.hasJavaClass(); // } // // public Class getJavaClass() throws Exception{ // return tryExpr.getJavaClass(); // } // // static class Parser implements IParser{ // public Expr parse(C context, Object frm) throws Exception{ // ISeq form = (ISeq) frm; // //(try-finally try-expr finally-expr) // if(form.count() != 3) // throw new IllegalArgumentException( // "Wrong number of arguments, expecting: (try-finally try-expr finally-expr) "); // // if(context == C.EVAL || context == C.EXPRESSION) // return analyze(context, RT.list(RT.list(FN, PersistentVector.EMPTY, form))); // // return new TryFinallyExpr(analyze(context, RT.second(form)), // analyze(C.STATEMENT, RT.third(form))); // } // } //} static class ThrowExpr extends UntypedExpr{ public final Expr excExpr; public ThrowExpr(Expr excExpr){ this.excExpr = excExpr; } public Object eval() throws Exception{ throw new Exception("Can't eval throw"); } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ excExpr.emit(C.EXPRESSION, fn, gen); gen.checkCast(THROWABLE_TYPE); gen.throwException(); } static class Parser implements IParser{ public Expr parse(C context, Object form) throws Exception{ if(context == C.EVAL) return analyze(context, RT.list(RT.list(FN, PersistentVector.EMPTY, form))); return new ThrowExpr(analyze(C.EXPRESSION, RT.second(form))); } } } /* static class ClassExpr implements Expr{ final Class c; final static Method forNameMethod = Method.getMethod("Class forName(String)"); public ClassExpr(Class c){ this.c = c; } public Object eval() throws Exception{ return c; } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ if(context != C.STATEMENT) { gen.push(c.getName()); gen.invokeStatic(CLASS_TYPE, forNameMethod); } } public boolean hasJavaClass(){ return true; } public Class getJavaClass() throws Exception{ return Class.class; } static class Parser implements IParser{ public Expr parse(C context, Object frm) throws Exception{ ISeq form = (ISeq) frm; //(class Classname) if(form.count() != 2) throw new Exception("wrong number of arguments, expecting: (class Classname)"); Class c = HostExpr.maybeClass(RT.second(form), true); if(c == null) throw new IllegalArgumentException("Unable to resolve classname: " + RT.second(form)); return new ClassExpr(c); } } } */ static public boolean subsumes(Class[] c1, Class[] c2){ //presumes matching lengths Boolean better = false; for(int i = 0; i < c1.length; i++) { if(c1[i] != c2[i])// || c2[i].isPrimitive() && c1[i] == Object.class)) { if(!c1[i].isPrimitive() && c2[i].isPrimitive() //|| Number.class.isAssignableFrom(c1[i]) && c2[i].isPrimitive() || c2[i].isAssignableFrom(c1[i])) better = true; else return false; } } return better; } static int getMatchingParams(String methodName, ArrayList<Class[]> paramlists, IPersistentVector argexprs, List<Class> rets) throws Exception{ //presumes matching lengths int matchIdx = -1; boolean tied = false; boolean foundExact = false; for(int i = 0; i < paramlists.size(); i++) { boolean match = true; ISeq aseq = argexprs.seq(); int exact = 0; for(int p = 0; match && p < argexprs.count() && aseq != null; ++p, aseq = aseq.next()) { Expr arg = (Expr) aseq.first(); Class aclass = arg.hasJavaClass() ? arg.getJavaClass() : Object.class; Class pclass = paramlists.get(i)[p]; if(arg.hasJavaClass() && aclass == pclass) exact++; else match = Reflector.paramArgTypeMatch(pclass, aclass); } if(exact == argexprs.count()) { if(!foundExact || matchIdx == -1 || rets.get(matchIdx).isAssignableFrom(rets.get(i))) matchIdx = i; foundExact = true; } else if(match && !foundExact) { if(matchIdx == -1) matchIdx = i; else { if(subsumes(paramlists.get(i), paramlists.get(matchIdx))) { matchIdx = i; tied = false; } else if(Arrays.equals(paramlists.get(matchIdx), paramlists.get(i))) { if(rets.get(matchIdx).isAssignableFrom(rets.get(i))) matchIdx = i; } else if(!(subsumes(paramlists.get(matchIdx), paramlists.get(i)))) tied = true; } } } if(tied) throw new IllegalArgumentException("More than one matching method found: " + methodName); return matchIdx; } public static class NewExpr implements Expr{ public final IPersistentVector args; public final Constructor ctor; public final Class c; final static Method invokeConstructorMethod = Method.getMethod("Object invokeConstructor(Class,Object[])"); // final static Method forNameMethod = Method.getMethod("Class classForName(String)"); final static Method forNameMethod = Method.getMethod("Class forName(String)"); public NewExpr(Class c, IPersistentVector args, int line) throws Exception{ this.args = args; this.c = c; Constructor[] allctors = c.getConstructors(); ArrayList ctors = new ArrayList(); ArrayList<Class[]> params = new ArrayList(); ArrayList<Class> rets = new ArrayList(); for(int i = 0; i < allctors.length; i++) { Constructor ctor = allctors[i]; if(ctor.getParameterTypes().length == args.count()) { ctors.add(ctor); params.add(ctor.getParameterTypes()); rets.add(c); } } if(ctors.isEmpty()) throw new IllegalArgumentException("No matching ctor found for " + c); int ctoridx = 0; if(ctors.size() > 1) { ctoridx = getMatchingParams(c.getName(), params, args, rets); } this.ctor = ctoridx >= 0 ? (Constructor) ctors.get(ctoridx) : null; if(ctor == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) { ((PrintWriter) RT.ERR.deref()) .format("Reflection warning, %s:%d - call to %s ctor can't be resolved.\n", SOURCE_PATH.deref(), line, c.getName()); } } public Object eval() throws Exception{ Object[] argvals = new Object[args.count()]; for(int i = 0; i < args.count(); i++) argvals[i] = ((Expr) args.nth(i)).eval(); if(this.ctor != null) { return ctor.newInstance(Reflector.boxArgs(ctor.getParameterTypes(), argvals)); } return Reflector.invokeConstructor(c, argvals); } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ if(this.ctor != null) { Type type = Type.getType(c); gen.newInstance(type); gen.dup(); MethodExpr.emitTypedArgs(fn, gen, ctor.getParameterTypes(), args); if(context == C.RETURN) { FnMethod method = (FnMethod) METHOD.deref(); method.emitClearLocals(gen); } gen.invokeConstructor(type, new Method("<init>", Type.getConstructorDescriptor(ctor))); } else { gen.push(c.getName()); gen.invokeStatic(CLASS_TYPE, forNameMethod); MethodExpr.emitArgsAsArray(args, fn, gen); if(context == C.RETURN) { FnMethod method = (FnMethod) METHOD.deref(); method.emitClearLocals(gen); } gen.invokeStatic(REFLECTOR_TYPE, invokeConstructorMethod); } if(context == C.STATEMENT) gen.pop(); } public boolean hasJavaClass(){ return true; } public Class getJavaClass() throws Exception{ return c; } static class Parser implements IParser{ public Expr parse(C context, Object frm) throws Exception{ int line = (Integer) LINE.deref(); ISeq form = (ISeq) frm; //(new Classname args...) if(form.count() < 2) throw new Exception("wrong number of arguments, expecting: (new Classname args...)"); Class c = HostExpr.maybeClass(RT.second(form), false); if(c == null) throw new IllegalArgumentException("Unable to resolve classname: " + RT.second(form)); PersistentVector args = PersistentVector.EMPTY; for(ISeq s = RT.next(RT.next(form)); s != null; s = s.next()) args = args.cons(analyze(context == C.EVAL ? context : C.EXPRESSION, s.first())); return new NewExpr(c, args, line); } } } //static class IdenticalExpr implements Expr{ // final Expr expr1; // final Expr expr2; // // // public IdenticalExpr(Expr expr1, Expr expr2){ // this.expr1 = expr1; // this.expr2 = expr2; // } // // public boolean hasJavaClass(){ // return true; // } // // public Class getJavaClass(){ // return Boolean.class; // } // // public Object eval() throws Exception{ // return expr1.eval() == expr2.eval() ? // RT.T : RT.F; // } // // public void emit(C context, FnExpr fn, GeneratorAdapter gen){ // if(context != C.STATEMENT) // { // Label not = gen.newLabel(); // Label end = gen.newLabel(); // expr1.emit(C.EXPRESSION, fn, gen); // expr2.emit(C.EXPRESSION, fn, gen); // gen.visitJumpInsn(IF_ACMPNE, not); // gen.getStatic(BOOLEAN_OBJECT_TYPE, "TRUE", BOOLEAN_OBJECT_TYPE); //// gen.getStatic(RT_TYPE, "T", KEYWORD_TYPE); // gen.goTo(end); // gen.mark(not); // gen.getStatic(BOOLEAN_OBJECT_TYPE, "FALSE", BOOLEAN_OBJECT_TYPE); //// NIL_EXPR.emit(C.EXPRESSION, fn, gen); // gen.mark(end); // } // } // // static class Parser implements IParser{ // public Expr parse(C context, Object frm) throws Exception{ // ISeq form = (ISeq) frm; // if(form.count() != 3) // throw new Exception("wrong number of arguments, expecting: (identical? x y)"); // // return new IdenticalExpr(analyze(C.EXPRESSION, RT.second(form)), analyze(C.EXPRESSION, RT.third(form))); // } // } //} //static class InstanceExpr implements Expr{ // final Expr expr; // final Class c; // // // public InstanceExpr(Expr expr, Class c){ // this.expr = expr; // this.c = c; // } // // public Object eval() throws Exception{ // return c.isInstance(expr.eval()) ? // RT.T : RT.F; // } // // public boolean hasJavaClass(){ // return true; // } // // public Class getJavaClass(){ // return Boolean.class; // } // // public void emit(C context, FnExpr fn, GeneratorAdapter gen){ // if(context != C.STATEMENT) // { // Label not = gen.newLabel(); // Label end = gen.newLabel(); // expr.emit(C.EXPRESSION, fn, gen); // gen.instanceOf(Type.getType(c)); // gen.ifZCmp(GeneratorAdapter.EQ, not); // gen.getStatic(BOOLEAN_OBJECT_TYPE, "TRUE", BOOLEAN_OBJECT_TYPE); //// gen.getStatic(RT_TYPE, "T", KEYWORD_TYPE); // gen.goTo(end); // gen.mark(not); // gen.getStatic(BOOLEAN_OBJECT_TYPE, "FALSE", BOOLEAN_OBJECT_TYPE); //// NIL_EXPR.emit(C.EXPRESSION, fn, gen); // gen.mark(end); // } // } // // static class Parser implements IParser{ // public Expr parse(C context, Object frm) throws Exception{ // ISeq form = (ISeq) frm; // //(instance? x Classname) // if(form.count() != 3) // throw new Exception("wrong number of arguments, expecting: (instance? x Classname)"); // Class c = HostExpr.maybeClass(RT.third(form), true); // if(c == null) // throw new IllegalArgumentException("Unable to resolve classname: " + RT.third(form)); // return new InstanceExpr(analyze(C.EXPRESSION, RT.second(form)), c); // } // } //} public static class MetaExpr implements Expr{ public final Expr expr; public final MapExpr meta; final static Type IOBJ_TYPE = Type.getType(IObj.class); final static Method withMetaMethod = Method.getMethod("clojure.lang.IObj withMeta(clojure.lang.IPersistentMap)"); public MetaExpr(Expr expr, MapExpr meta){ this.expr = expr; this.meta = meta; } public Object eval() throws Exception{ return ((IObj) expr.eval()).withMeta((IPersistentMap) meta.eval()); } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ expr.emit(C.EXPRESSION, fn, gen); gen.checkCast(IOBJ_TYPE); meta.emit(C.EXPRESSION, fn, gen); gen.checkCast(IPERSISTENTMAP_TYPE); gen.invokeInterface(IOBJ_TYPE, withMetaMethod); if(context == C.STATEMENT) { gen.pop(); } } public boolean hasJavaClass() throws Exception{ return expr.hasJavaClass(); } public Class getJavaClass() throws Exception{ return expr.getJavaClass(); } } public static class IfExpr implements Expr{ public final Expr testExpr; public final Expr thenExpr; public final Expr elseExpr; public final int line; public IfExpr(int line, Expr testExpr, Expr thenExpr, Expr elseExpr){ this.testExpr = testExpr; this.thenExpr = thenExpr; this.elseExpr = elseExpr; this.line = line; } public Object eval() throws Exception{ Object t = testExpr.eval(); if(t != null && t != Boolean.FALSE) return thenExpr.eval(); return elseExpr.eval(); } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ Label nullLabel = gen.newLabel(); Label falseLabel = gen.newLabel(); Label endLabel = gen.newLabel(); gen.visitLineNumber(line, gen.mark()); try { if(testExpr instanceof MaybePrimitiveExpr && testExpr.hasJavaClass() && testExpr.getJavaClass() == boolean.class) { ((MaybePrimitiveExpr) testExpr).emitUnboxed(C.EXPRESSION, fn, gen); gen.ifZCmp(gen.EQ, falseLabel); } else { testExpr.emit(C.EXPRESSION, fn, gen); gen.dup(); gen.ifNull(nullLabel); gen.getStatic(BOOLEAN_OBJECT_TYPE, "FALSE", BOOLEAN_OBJECT_TYPE); gen.visitJumpInsn(IF_ACMPEQ, falseLabel); } } catch(Exception e) { throw new RuntimeException(e); } thenExpr.emit(context, fn, gen); gen.goTo(endLabel); gen.mark(nullLabel); gen.pop(); gen.mark(falseLabel); elseExpr.emit(context, fn, gen); gen.mark(endLabel); } public boolean hasJavaClass() throws Exception{ return thenExpr.hasJavaClass() && elseExpr.hasJavaClass() && (thenExpr.getJavaClass() == elseExpr.getJavaClass() || thenExpr.getJavaClass() == null || elseExpr.getJavaClass() == null); } public Class getJavaClass() throws Exception{ Class thenClass = thenExpr.getJavaClass(); if(thenClass != null) return thenClass; return elseExpr.getJavaClass(); } static class Parser implements IParser{ public Expr parse(C context, Object frm) throws Exception{ ISeq form = (ISeq) frm; //(if test then) or (if test then else) if(form.count() > 4) throw new Exception("Too many arguments to if"); else if(form.count() < 3) throw new Exception("Too few arguments to if"); return new IfExpr((Integer) LINE.deref(), analyze(context == C.EVAL ? context : C.EXPRESSION, RT.second(form)), analyze(context, RT.third(form)), analyze(context, RT.fourth(form))); } } } static final public IPersistentMap CHAR_MAP = PersistentHashMap.create('-', "_", // '.', "_DOT_", ':', "_COLON_", '+', "_PLUS_", '>', "_GT_", '<', "_LT_", '=', "_EQ_", '~', "_TILDE_", '!', "_BANG_", '@', "_CIRCA_", '#', "_SHARP_", '$', "_DOLLARSIGN_", '%', "_PERCENT_", '^', "_CARET_", '&', "_AMPERSAND_", '*', "_STAR_", '|', "_BAR_", '{', "_LBRACE_", '}', "_RBRACE_", '[', "_LBRACK_", ']', "_RBRACK_", '/', "_SLASH_", '\\', "_BSLASH_", '?', "_QMARK_"); static public String munge(String name){ StringBuilder sb = new StringBuilder(); for(char c : name.toCharArray()) { String sub = (String) CHAR_MAP.valAt(c); if(sub != null) sb.append(sub); else sb.append(c); } return sb.toString(); } public static class EmptyExpr implements Expr{ public final Object coll; final static Type HASHMAP_TYPE = Type.getType(PersistentArrayMap.class); final static Type HASHSET_TYPE = Type.getType(PersistentHashSet.class); final static Type VECTOR_TYPE = Type.getType(PersistentVector.class); final static Type LIST_TYPE = Type.getType(PersistentList.class); final static Type EMPTY_LIST_TYPE = Type.getType(PersistentList.EmptyList.class); public EmptyExpr(Object coll){ this.coll = coll; } public Object eval() throws Exception{ return coll; } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ if(coll instanceof IPersistentList) gen.getStatic(LIST_TYPE, "EMPTY", EMPTY_LIST_TYPE); else if(coll instanceof IPersistentVector) gen.getStatic(VECTOR_TYPE, "EMPTY", VECTOR_TYPE); else if(coll instanceof IPersistentMap) gen.getStatic(HASHMAP_TYPE, "EMPTY", HASHMAP_TYPE); else if(coll instanceof IPersistentSet) gen.getStatic(HASHSET_TYPE, "EMPTY", HASHSET_TYPE); else throw new UnsupportedOperationException("Unknown Collection type"); if(context == C.STATEMENT) { gen.pop(); } } public boolean hasJavaClass() throws Exception{ return true; } public Class getJavaClass() throws Exception{ if(coll instanceof IPersistentList) return IPersistentList.class; else if(coll instanceof IPersistentVector) return IPersistentVector.class; else if(coll instanceof IPersistentMap) return IPersistentMap.class; else if(coll instanceof IPersistentSet) return IPersistentSet.class; else throw new UnsupportedOperationException("Unknown Collection type"); } } public static class ListExpr implements Expr{ public final IPersistentVector args; final static Method arrayToListMethod = Method.getMethod("clojure.lang.ISeq arrayToList(Object[])"); public ListExpr(IPersistentVector args){ this.args = args; } public Object eval() throws Exception{ IPersistentVector ret = PersistentVector.EMPTY; for(int i = 0; i < args.count(); i++) ret = (IPersistentVector) ret.cons(((Expr) args.nth(i)).eval()); return ret.seq(); } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ MethodExpr.emitArgsAsArray(args, fn, gen); gen.invokeStatic(RT_TYPE, arrayToListMethod); if(context == C.STATEMENT) gen.pop(); } public boolean hasJavaClass() throws Exception{ return true; } public Class getJavaClass() throws Exception{ return IPersistentList.class; } } public static class MapExpr implements Expr{ public final IPersistentVector keyvals; final static Method mapMethod = Method.getMethod("clojure.lang.IPersistentMap map(Object[])"); public MapExpr(IPersistentVector keyvals){ this.keyvals = keyvals; } public Object eval() throws Exception{ Object[] ret = new Object[keyvals.count()]; for(int i = 0; i < keyvals.count(); i++) ret[i] = ((Expr) keyvals.nth(i)).eval(); return RT.map(ret); } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ MethodExpr.emitArgsAsArray(keyvals, fn, gen); gen.invokeStatic(RT_TYPE, mapMethod); if(context == C.STATEMENT) gen.pop(); } public boolean hasJavaClass() throws Exception{ return true; } public Class getJavaClass() throws Exception{ return IPersistentMap.class; } static public Expr parse(C context, IPersistentMap form) throws Exception{ IPersistentVector keyvals = PersistentVector.EMPTY; for(ISeq s = RT.seq(form); s != null; s = s.next()) { IMapEntry e = (IMapEntry) s.first(); keyvals = (IPersistentVector) keyvals.cons(analyze(context == C.EVAL ? context : C.EXPRESSION, e.key())); keyvals = (IPersistentVector) keyvals.cons(analyze(context == C.EVAL ? context : C.EXPRESSION, e.val())); } Expr ret = new MapExpr(keyvals); if(form instanceof IObj && ((IObj) form).meta() != null) return new MetaExpr(ret, (MapExpr) MapExpr .parse(context == C.EVAL ? context : C.EXPRESSION, ((IObj) form).meta())); else return ret; } } public static class SetExpr implements Expr{ public final IPersistentVector keys; final static Method setMethod = Method.getMethod("clojure.lang.IPersistentSet set(Object[])"); public SetExpr(IPersistentVector keys){ this.keys = keys; } public Object eval() throws Exception{ Object[] ret = new Object[keys.count()]; for(int i = 0; i < keys.count(); i++) ret[i] = ((Expr) keys.nth(i)).eval(); return RT.set(ret); } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ MethodExpr.emitArgsAsArray(keys, fn, gen); gen.invokeStatic(RT_TYPE, setMethod); if(context == C.STATEMENT) gen.pop(); } public boolean hasJavaClass() throws Exception{ return true; } public Class getJavaClass() throws Exception{ return IPersistentSet.class; } static public Expr parse(C context, IPersistentSet form) throws Exception{ IPersistentVector keys = PersistentVector.EMPTY; for(ISeq s = RT.seq(form); s != null; s = s.next()) { Object e = s.first(); keys = (IPersistentVector) keys.cons(analyze(context == C.EVAL ? context : C.EXPRESSION, e)); } Expr ret = new SetExpr(keys); if(form instanceof IObj && ((IObj) form).meta() != null) return new MetaExpr(ret, (MapExpr) MapExpr .parse(context == C.EVAL ? context : C.EXPRESSION, ((IObj) form).meta())); else return ret; } } public static class VectorExpr implements Expr{ public final IPersistentVector args; final static Method vectorMethod = Method.getMethod("clojure.lang.IPersistentVector vector(Object[])"); public VectorExpr(IPersistentVector args){ this.args = args; } public Object eval() throws Exception{ IPersistentVector ret = PersistentVector.EMPTY; for(int i = 0; i < args.count(); i++) ret = (IPersistentVector) ret.cons(((Expr) args.nth(i)).eval()); return ret; } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ MethodExpr.emitArgsAsArray(args, fn, gen); gen.invokeStatic(RT_TYPE, vectorMethod); if(context == C.STATEMENT) gen.pop(); } public boolean hasJavaClass() throws Exception{ return true; } public Class getJavaClass() throws Exception{ return IPersistentVector.class; } static public Expr parse(C context, IPersistentVector form) throws Exception{ IPersistentVector args = PersistentVector.EMPTY; for(int i = 0; i < form.count(); i++) args = (IPersistentVector) args.cons(analyze(context == C.EVAL ? context : C.EXPRESSION, form.nth(i))); Expr ret = new VectorExpr(args); if(form instanceof IObj && ((IObj) form).meta() != null) return new MetaExpr(ret, (MapExpr) MapExpr .parse(context == C.EVAL ? context : C.EXPRESSION, ((IObj) form).meta())); else return ret; } } static class InvokeExpr implements Expr{ public final Expr fexpr; public final Object tag; public final IPersistentVector args; public final int line; public final String source; public InvokeExpr(String source, int line, Symbol tag, Expr fexpr, IPersistentVector args){ this.source = source; this.fexpr = fexpr; this.args = args; this.line = line; this.tag = tag != null ? tag : (fexpr instanceof VarExpr ? ((VarExpr) fexpr).tag : null); } public Object eval() throws Exception{ try { 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)); } catch(Throwable e) { if(!(e instanceof CompilerException)) throw new CompilerException(source, line, e); else throw (CompilerException) e; } } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ gen.visitLineNumber(line, gen.mark()); fexpr.emit(C.EXPRESSION, fn, gen); gen.checkCast(IFN_TYPE); for(int i = 0; i < Math.min(MAX_POSITIONAL_ARITY, args.count()); i++) { Expr e = (Expr) args.nth(i); e.emit(C.EXPRESSION, fn, gen); } if(args.count() > MAX_POSITIONAL_ARITY) { PersistentVector restArgs = PersistentVector.EMPTY; for(int i = MAX_POSITIONAL_ARITY; i < args.count(); i++) { restArgs = restArgs.cons(args.nth(i)); } MethodExpr.emitArgsAsArray(restArgs, fn, gen); } if(context == C.RETURN) { FnMethod method = (FnMethod) METHOD.deref(); method.emitClearLocals(gen); } gen.invokeInterface(IFN_TYPE, new Method("invoke", OBJECT_TYPE, ARG_TYPES[Math.min(MAX_POSITIONAL_ARITY + 1, args.count())])); if(context == C.STATEMENT) gen.pop(); } public boolean hasJavaClass() throws Exception{ return tag != null; } public Class getJavaClass() throws Exception{ return HostExpr.tagToClass(tag); } static public Expr parse(C context, ISeq form) throws Exception{ if(context != C.EVAL) context = C.EXPRESSION; Expr fexpr = analyze(context, form.first()); PersistentVector args = PersistentVector.EMPTY; for(ISeq s = RT.seq(form.next()); s != null; s = s.next()) { args = args.cons(analyze(context, 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((String) SOURCE.deref(), (Integer) LINE.deref(), tagOf(form), fexpr, args); } } static class SourceDebugExtensionAttribute extends Attribute{ public SourceDebugExtensionAttribute(){ super("SourceDebugExtension"); } void writeSMAP(ClassWriter cw, String smap){ ByteVector bv = write(cw, null, -1, -1, -1); bv.putUTF8(smap); } } static class FnLoaderThunk extends RestFn{ FnExpr fx; Var v; IFn f; FnLoaderThunk(FnExpr fx, Var v){ super(0); this.fx = fx; this.v = v; } protected Object doInvoke(Object args) throws Exception{ IFn f = loadFn(); return f.applyTo((ISeq) args); } synchronized private IFn loadFn() throws Exception{ if(f == null) { Class fc = fx.getCompiledClass(); f = (IFn) fc.newInstance(); v.swapRoot(f); } return f; } } static public class FnExpr implements Expr{ static final String CONST_PREFIX = "const__"; IPersistentCollection methods; //if there is a variadic overload (there can only be one) it is stored here FnMethod variadicMethod = null; String name; String simpleName; String internalName; String thisName; Type fntype; public final Object tag; //localbinding->itself IPersistentMap closes = PersistentHashMap.EMPTY; //Keyword->KeywordExpr IPersistentMap keywords = PersistentHashMap.EMPTY; IPersistentMap vars = PersistentHashMap.EMPTY; Class compiledClass; int line; PersistentVector constants; int constantsID; boolean onceOnly = false; String superName = null; public final IPersistentCollection methods(){ return methods; } public final FnMethod variadicMethod(){ return variadicMethod; } public final String name(){ return name; } public final String simpleName(){ return simpleName; } public final String internalName(){ return internalName; } public final String thisName(){ return thisName; } public final Type fntype(){ return fntype; } public final IPersistentMap closes(){ return closes; } public final IPersistentMap keywords(){ return keywords; } public final IPersistentMap vars(){ return vars; } public final Class compiledClass(){ return compiledClass; } public final int line(){ return line; } public final PersistentVector constants(){ return constants; } public final int constantsID(){ return constantsID; } final static Method kwintern = Method.getMethod("clojure.lang.Keyword intern(String, String)"); final static Method symcreate = Method.getMethod("clojure.lang.Symbol create(String)"); final static Method varintern = Method.getMethod("clojure.lang.Var intern(clojure.lang.Symbol, 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(AFunction.class); final static Type restFnType = Type.getType(RestFn.class); final static Type DYNAMIC_CLASSLOADER_TYPE = Type.getType(DynamicClassLoader.class); final static Method getClassMethod = Method.getMethod("Class getClass()"); final static Method getClassLoaderMethod = Method.getMethod("ClassLoader getClassLoader()"); final static Method getConstantsMethod = Method.getMethod("Object[] getConstants(int)"); final static Method readStringMethod = Method.getMethod("Object readString(String)"); private DynamicClassLoader loader; private byte[] bytecode; public FnExpr(Object tag){ this.tag = tag; } static Expr parse(C context, ISeq form, String name) throws Exception{ FnExpr fn = new FnExpr(tagOf(form)); FnMethod enclosingMethod = (FnMethod) METHOD.deref(); if(((IMeta) form.first()).meta() != null) { fn.onceOnly = RT.booleanCast(RT.get(RT.meta(form.first()), Keyword.intern(null, "once"))); fn.superName = (String) RT.get(RT.meta(form.first()), Keyword.intern(null, "super-name")); } //fn.thisName = name; String basename = enclosingMethod != null ? (enclosingMethod.fn.name + "$") : //"clojure.fns." + (munge(currentNS().name.name) + "$"); if(RT.second(form) instanceof Symbol) name = ((Symbol) RT.second(form)).name; fn.simpleName = ((name != null ? munge(name).replace(".", "_DOT_") : "fn") + "__" + RT.nextID()); fn.name = basename + fn.simpleName; fn.internalName = fn.name.replace('.', '/'); fn.fntype = Type.getObjectType(fn.internalName); try { Var.pushThreadBindings( RT.map(CONSTANTS, PersistentVector.EMPTY, KEYWORDS, PersistentHashMap.EMPTY, VARS, PersistentHashMap.EMPTY)); //arglist might be preceded by symbol naming this fn if(RT.second(form) instanceof Symbol) { fn.thisName = ((Symbol) RT.second(form)).name; form = RT.cons(FN, RT.next(RT.next(form))); } //now (fn [args] body...) or (fn ([args] body...) ([args2] body2...) ...) //turn former into latter if(RT.second(form) instanceof IPersistentVector) form = RT.list(FN, RT.next(form)); fn.line = (Integer) LINE.deref(); FnMethod[] methodArray = new FnMethod[MAX_POSITIONAL_ARITY + 1]; FnMethod variadicMethod = null; for(ISeq s = RT.next(form); s != null; s = RT.next(s)) { FnMethod f = FnMethod.parse(fn, (ISeq) RT.first(s)); if(f.isVariadic()) { if(variadicMethod == null) variadicMethod = f; else throw new Exception("Can't have more than 1 variadic overload"); } else if(methodArray[f.reqParms.count()] == null) methodArray[f.reqParms.count()] = f; else throw new Exception("Can't have 2 overloads with same arity"); } if(variadicMethod != null) { for(int i = variadicMethod.reqParms.count() + 1; i <= MAX_POSITIONAL_ARITY; i++) if(methodArray[i] != null) throw new Exception( "Can't have fixed arity function with more params than variadic function"); } IPersistentCollection methods = null; for(int i = 0; i < methodArray.length; i++) if(methodArray[i] != null) methods = RT.conj(methods, methodArray[i]); if(variadicMethod != null) methods = RT.conj(methods, variadicMethod); fn.methods = methods; fn.variadicMethod = variadicMethod; fn.keywords = (IPersistentMap) KEYWORDS.deref(); fn.vars = (IPersistentMap) VARS.deref(); fn.constants = (PersistentVector) CONSTANTS.deref(); fn.constantsID = RT.nextID(); // DynamicClassLoader loader = (DynamicClassLoader) LOADER.get(); // loader.registerConstants(fn.constantsID, fn.constants.toArray()); } finally { Var.popThreadBindings(); } fn.compile(); return fn; } boolean isVariadic(){ return variadicMethod != null; } Type[] ctorTypes(){ if(closes.count() == 0) return ARG_TYPES[0]; PersistentVector tv = PersistentVector.EMPTY; for(ISeq s = RT.keys(closes); s != null; s = s.next()) { LocalBinding lb = (LocalBinding) s.first(); if(lb.getPrimitiveType() != null) tv = tv.cons(Type.getType(lb.getPrimitiveType())); else tv = tv.cons(OBJECT_TYPE); } Type[] ret = new Type[tv.count()]; for(int i = 0; i < tv.count(); i++) ret[i] = (Type) tv.nth(i); return ret; } private void compile() throws Exception{ //create bytecode for a class //with name current_ns.defname[$letname]+ //anonymous fns get names fn__id //derived from AFn/RestFn ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); // ClassWriter cw = new ClassWriter(0); ClassVisitor cv = cw; // ClassVisitor cv = new TraceClassVisitor(new CheckClassAdapter(cw), new PrintWriter(System.out)); //ClassVisitor cv = new TraceClassVisitor(cw, new PrintWriter(System.out)); cv.visit(V1_5, ACC_PUBLIC + ACC_SUPER, internalName, null, superName != null ? superName : (isVariadic() ? "clojure/lang/RestFn" : "clojure/lang/AFunction"), null); String source = (String) SOURCE.deref(); int lineBefore = (Integer) LINE_BEFORE.deref(); int lineAfter = (Integer) LINE_AFTER.deref() + 1; if(source != null && SOURCE_PATH.deref() != null) { //cv.visitSource(source, null); String smap = "SMAP\n" + ((source.lastIndexOf('.') > 0) ? source.substring(0, source.lastIndexOf('.')) : simpleName) + ".java\n" + "Clojure\n" + "*S Clojure\n" + "*F\n" + "+ 1 " + source + "\n" + (String) SOURCE_PATH.deref() + "\n" + "*L\n" + String.format("%d#1,%d:%d\n", lineBefore, lineAfter - lineBefore, lineBefore) + "*E"; cv.visitSource(source, smap); } //static fields for constants for(int i = 0; i < constants.count(); i++) { cv.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, constantName(i), constantType(i).getDescriptor(), null, null); } /* //static fields for keywords for(ISeq s = RT.keys(keywords); s != null; s = s.rest()) { Keyword k = (Keyword) s.first(); cv.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, munge(k.sym.toString()), KEYWORD_TYPE.getDescriptor(), null, null); } //static fields for vars for(ISeq s = RT.keys(vars); s != null; s = s.rest()) { Var v = (Var) s.first(); cv.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, munge(v.sym.toString()), VAR_TYPE.getDescriptor(), null, null); } */ //static init for constants, keywords and vars GeneratorAdapter clinitgen = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, Method.getMethod("void <clinit> ()"), null, null, cv); clinitgen.visitCode(); clinitgen.visitLineNumber(line, clinitgen.mark()); // Label begin = clinitgen.newLabel(); // Label end = clinitgen.newLabel(); if(constants.count() > 0) { emitConstants(clinitgen); } clinitgen.returnValue(); clinitgen.endMethod(); // clinitgen.visitMaxs(1, 1); //instance fields for closed-overs for(ISeq s = RT.keys(closes); s != null; s = s.next()) { LocalBinding lb = (LocalBinding) s.first(); if(lb.getPrimitiveType() != null) cv.visitField(ACC_PUBLIC //+ ACC_FINAL , lb.name, Type.getType(lb.getPrimitiveType()).getDescriptor(), null, null); else cv.visitField(ACC_PUBLIC //+ (onceOnly ? 0 : 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()]); Method m = new Method("<init>", Type.VOID_TYPE, ctorTypes()); GeneratorAdapter ctorgen = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cv); Label start = ctorgen.newLabel(); Label end = ctorgen.newLabel(); ctorgen.visitCode(); ctorgen.visitLineNumber(line, ctorgen.mark()); ctorgen.visitLabel(start); ctorgen.loadThis(); if(superName != null) ctorgen.invokeConstructor(Type.getObjectType(superName), afnctor); else if(isVariadic()) //RestFn ctor takes reqArity arg { ctorgen.push(variadicMethod.reqParms.count()); ctorgen.invokeConstructor(restFnType, restfnctor); } else ctorgen.invokeConstructor(aFnType, afnctor); int a = 1; for(ISeq s = RT.keys(closes); s != null; s = s.next(), ++a) { LocalBinding lb = (LocalBinding) s.first(); ctorgen.loadThis(); Class primc = lb.getPrimitiveType(); if(primc != null) { ctorgen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ILOAD), a); ctorgen.putField(fntype, lb.name, Type.getType(primc)); if(primc == Long.TYPE || primc == Double.TYPE) ++a; } else { ctorgen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), a); ctorgen.putField(fntype, lb.name, OBJECT_TYPE); } } ctorgen.visitLabel(end); // a = 1; // for(ISeq s = RT.keys(closes); s != null; s = s.rest(), ++a) // { // LocalBinding lb = (LocalBinding) s.first(); // Class primc = lb.getPrimitiveType(); // if(primc != null) // { // ctorgen.visitLocalVariable(lb.name, Type.getDescriptor(primc), null, start, end, a); // if(primc == Long.TYPE || primc == Double.TYPE) // ++a; // } // else // { // ctorgen.visitLocalVariable(lb.name, "Ljava/lang/Object;", null, start, end, a); // } // } ctorgen.returnValue(); // ctorgen.visitMaxs(1, 1); ctorgen.endMethod(); //override of invoke/doInvoke for each method for(ISeq s = RT.seq(methods); s != null; s = s.next()) { FnMethod method = (FnMethod) s.first(); method.emit(this, cv); } //end of class cv.visitEnd(); bytecode = cw.toByteArray(); if(RT.booleanCast(COMPILE_FILES.deref())) writeClassFile(internalName, bytecode); // else // getCompiledClass(); } void emitListAsObjectArray(Object value, GeneratorAdapter gen){ gen.push(((List) value).size()); gen.newArray(OBJECT_TYPE); int i = 0; for(Iterator it = ((List) value).iterator(); it.hasNext(); i++) { gen.dup(); gen.push(i); emitValue(it.next(), gen); gen.arrayStore(OBJECT_TYPE); } } void emitValue(Object value, GeneratorAdapter gen){ boolean partial = true; //System.out.println(value.getClass().toString()); if(value instanceof String) { gen.push((String) value); } else if(value instanceof Integer) { gen.push(((Integer) value).intValue()); gen.invokeStatic(Type.getType(Integer.class), Method.getMethod("Integer valueOf(int)")); } else if(value instanceof Double) { gen.push(((Double) value).doubleValue()); gen.invokeStatic(Type.getType(Double.class), Method.getMethod("Double valueOf(double)")); } else if(value instanceof Character) { gen.push(((Character) value).charValue()); gen.invokeStatic(Type.getType(Character.class), Method.getMethod("Character valueOf(char)")); } else if(value instanceof Class) { Class cc = (Class)value; if(cc.isPrimitive()) { Type bt; if ( cc == boolean.class ) bt = Type.getType(Boolean.class); else if ( cc == byte.class ) bt = Type.getType(Byte.class); else if ( cc == char.class ) bt = Type.getType(Character.class); else if ( cc == double.class ) bt = Type.getType(Double.class); else if ( cc == float.class ) bt = Type.getType(Float.class); else if ( cc == int.class ) bt = Type.getType(Integer.class); else if ( cc == long.class ) bt = Type.getType(Long.class); else if ( cc == short.class ) bt = Type.getType(Short.class); else throw new RuntimeException( "Can't embed unknown primitive in code: " + value); gen.getStatic( bt, "TYPE", Type.getType(Class.class) ); } else { gen.push(cc.getName()); gen.invokeStatic(Type.getType(Class.class), Method.getMethod("Class forName(String)")); } } else if(value instanceof Symbol) { gen.push(((Symbol) value).ns); gen.push(((Symbol) value).name); gen.invokeStatic(Type.getType(Symbol.class), Method.getMethod("clojure.lang.Symbol create(String,String)")); } else if(value instanceof Keyword) { emitValue(((Keyword) value).sym, gen); gen.invokeStatic(Type.getType(Keyword.class), Method.getMethod("clojure.lang.Keyword intern(clojure.lang.Symbol)")); } else if(value instanceof Var) { Var var = (Var) value; gen.push(var.ns.name.toString()); gen.push(var.sym.toString()); gen.invokeStatic(RT_TYPE, Method.getMethod("clojure.lang.Var var(String,String)")); } else if(value instanceof IPersistentMap) { List entries = new ArrayList(); for(Map.Entry entry : (Set<Map.Entry>) ((Map) value).entrySet()) { entries.add(entry.getKey()); entries.add(entry.getValue()); } emitListAsObjectArray(entries, gen); gen.invokeStatic(RT_TYPE, Method.getMethod("clojure.lang.IPersistentMap map(Object[])")); } else if(value instanceof IPersistentVector) { emitListAsObjectArray(value, gen); gen.invokeStatic(RT_TYPE, Method.getMethod( "clojure.lang.IPersistentVector vector(Object[])")); } else if(value instanceof ISeq || value instanceof IPersistentList) { emitListAsObjectArray(value, gen); gen.invokeStatic(Type.getType(java.util.Arrays.class), Method.getMethod("java.util.List asList(Object[])")); gen.invokeStatic(Type.getType(PersistentList.class), Method.getMethod( "clojure.lang.IPersistentList create(java.util.List)")); } else { String cs = null; try { cs = RT.printString(value); //System.out.println("WARNING SLOW CODE: " + value.getClass() + " -> " + cs); } catch(Exception e) { throw new RuntimeException( "Can't embed object in code, maybe print-dup not defined: " + value); } if(cs.length() == 0) throw new RuntimeException( "Can't embed unreadable object in code: " + value); if(cs.startsWith("#<")) throw new RuntimeException( "Can't embed unreadable object in code: " + cs); gen.push(cs); gen.invokeStatic(RT_TYPE, readStringMethod); partial = false; } if(partial) { if(value instanceof Obj && RT.count(((Obj) value).meta()) > 0) { gen.checkCast(IOBJ_TYPE); emitValue(((Obj) value).meta(), gen); gen.checkCast(IPERSISTENTMAP_TYPE); gen.invokeInterface(IOBJ_TYPE, Method.getMethod("clojure.lang.IObj withMeta(clojure.lang.IPersistentMap)")); } } } void emitConstants(GeneratorAdapter clinitgen){ try { Var.pushThreadBindings(RT.map(RT.PRINT_DUP, RT.T)); for(int i = 0; i < constants.count(); i++) { emitValue(constants.nth(i), clinitgen); clinitgen.checkCast(constantType(i)); clinitgen.putStatic(fntype, constantName(i), constantType(i)); } } finally { Var.popThreadBindings(); } } // void emitConstants(GeneratorAdapter clinitgen){ // try // { // Var.pushThreadBindings(RT.map(RT.PRINT_DUP, RT.T)); // // for(int i = 0; i < constants.count(); i++) // { // Object o = constants.nth(i); // if(o instanceof String) // { // clinitgen.push((String) constants.nth(i)); // } // else // { // String cs = null; // try // { // cs = RT.printString(o); // } // catch(Exception e) // { // throw new RuntimeException("Can't embed object in code, maybe print-dup not defined: " // + o); // } // if(cs.length() == 0) // throw new RuntimeException("Can't embed unreadable object in code: " + o); // // if(cs.startsWith("#<")) // throw new RuntimeException("Can't embed unreadable object in code: " + cs); // clinitgen.push(cs); // clinitgen.invokeStatic(RT_TYPE, readStringMethod); // clinitgen.checkCast(constantType(i)); // } //// clinitgen.dup(); //// clinitgen.push(i); //// clinitgen.arrayLoad(OBJECT_TYPE); // clinitgen.putStatic(fntype, constantName(i), constantType(i)); // } // } // finally // { // Var.popThreadBindings(); // } // } void emitClearCloses(GeneratorAdapter gen){ int a = 1; for(ISeq s = RT.keys(closes); s != null; s = s.next(), ++a) { LocalBinding lb = (LocalBinding) s.first(); Class primc = lb.getPrimitiveType(); if(primc == null) { gen.loadThis(); gen.visitInsn(Opcodes.ACONST_NULL); gen.putField(fntype, lb.name, OBJECT_TYPE); } } } synchronized Class getCompiledClass(){ if(compiledClass == null) try { if(RT.booleanCast(COMPILE_FILES.deref())) compiledClass = RT.classForName(name);//loader.defineClass(name, bytecode); else { loader = (DynamicClassLoader) LOADER.deref(); compiledClass = loader.defineClass(name, bytecode); } } catch(Exception e) { throw new RuntimeException(e); } return compiledClass; } public Object eval() throws Exception{ return getCompiledClass().newInstance(); } public void emitLetFnInits(GeneratorAdapter gen, FnExpr fn, IPersistentSet letFnLocals){ //fn arg is enclosing fn, not this gen.checkCast(fntype); for(ISeq s = RT.keys(closes); s != null; s = s.next()) { LocalBinding lb = (LocalBinding) s.first(); if(letFnLocals.contains(lb)) { Class primc = lb.getPrimitiveType(); gen.dup(); if(primc != null) { fn.emitUnboxedLocal(gen, lb); gen.putField(fntype, lb.name, Type.getType(primc)); } else { fn.emitLocal(gen, lb); gen.putField(fntype, lb.name, OBJECT_TYPE); } } } gen.pop(); } 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 getCompiledClass(); gen.newInstance(fntype); gen.dup(); for(ISeq s = RT.keys(closes); s != null; s = s.next()) { LocalBinding lb = (LocalBinding) s.first(); if(lb.getPrimitiveType() != null) fn.emitUnboxedLocal(gen, lb); else fn.emitLocal(gen, lb); } // gen.invokeConstructor(fntype, new Method("<init>", Type.VOID_TYPE, ARG_TYPES[closes.count()])); gen.invokeConstructor(fntype, new Method("<init>", Type.VOID_TYPE, ctorTypes())); if(context == C.STATEMENT) { gen.pop(); } } public boolean hasJavaClass() throws Exception{ return true; } public Class getJavaClass() throws Exception{ return (tag != null) ? HostExpr.tagToClass(tag) : IFn.class; } private void emitLocal(GeneratorAdapter gen, LocalBinding lb){ if(closes.containsKey(lb)) { Class primc = lb.getPrimitiveType(); gen.loadThis(); if(primc != null) { gen.getField(fntype, lb.name, Type.getType(primc)); HostExpr.emitBoxReturn(this, gen, primc); } else gen.getField(fntype, lb.name, OBJECT_TYPE); } else { Class primc = lb.getPrimitiveType(); if(primc != null) { gen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ILOAD), lb.idx); HostExpr.emitBoxReturn(this, gen, primc); } else gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), lb.idx); } } private void emitUnboxedLocal(GeneratorAdapter gen, LocalBinding lb){ Class primc = lb.getPrimitiveType(); if(closes.containsKey(lb)) { gen.loadThis(); gen.getField(fntype, lb.name, Type.getType(primc)); } else gen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ILOAD), lb.idx); } public void emitVar(GeneratorAdapter gen, Var var){ Integer i = (Integer) vars.valAt(var); emitConstant(gen, i); //gen.getStatic(fntype, munge(var.sym.toString()), VAR_TYPE); } public void emitKeyword(GeneratorAdapter gen, Keyword k){ Integer i = (Integer) keywords.valAt(k); emitConstant(gen, i); // gen.getStatic(fntype, munge(k.sym.toString()), KEYWORD_TYPE); } public void emitConstant(GeneratorAdapter gen, int id){ gen.getStatic(fntype, constantName(id), constantType(id)); } String constantName(int id){ return CONST_PREFIX + id; } Type constantType(int id){ Object o = constants.nth(id); Class c = o.getClass(); if(Modifier.isPublic(c.getModifiers())) { //can't emit derived fn types due to visibility if(LazySeq.class.isAssignableFrom(c)) return Type.getType(ISeq.class); else if(RestFn.class.isAssignableFrom(c)) return Type.getType(RestFn.class); else if(AFn.class.isAssignableFrom(c)) return Type.getType(AFn.class); else if(c == Var.class) return Type.getType(Var.class); else if(c == String.class) return Type.getType(String.class); // return Type.getType(c); } return OBJECT_TYPE; } } enum PSTATE{ REQ, REST, DONE } public static class FnMethod{ //when closures are defined inside other closures, //the closed over locals need to be propagated to the enclosing fn public final FnMethod parent; //localbinding->localbinding IPersistentMap locals = null; //num->localbinding IPersistentMap indexlocals = null; //localbinding->localbinding PersistentVector reqParms = PersistentVector.EMPTY; LocalBinding restParm = null; Expr body = null; FnExpr fn; PersistentVector argLocals; int maxLocal = 0; int line; PersistentHashSet localsUsedInCatchFinally = PersistentHashSet.EMPTY; public final IPersistentMap locals(){ return locals; } public final PersistentVector reqParms(){ return reqParms; } public final LocalBinding restParm(){ return restParm; } public final Expr body(){ return body; } public final FnExpr fn(){ return fn; } public final PersistentVector argLocals(){ return argLocals; } public final int maxLocal(){ return maxLocal; } public final int line(){ return line; } public FnMethod(FnExpr fn, FnMethod parent){ this.parent = parent; this.fn = fn; } boolean isVariadic(){ return restParm != null; } int numParams(){ return reqParms.count() + (isVariadic() ? 1 : 0); } private static FnMethod parse(FnExpr fn, ISeq form) throws Exception{ //([args] body...) IPersistentVector parms = (IPersistentVector) RT.first(form); ISeq body = RT.next(form); try { FnMethod method = new FnMethod(fn, (FnMethod) METHOD.deref()); method.line = (Integer) LINE.deref(); //register as the current method and set up a new env frame Var.pushThreadBindings( RT.map( METHOD, method, LOCAL_ENV, LOCAL_ENV.deref(), LOOP_LOCALS, null, NEXT_LOCAL_NUM, 0)); //register 'this' as local 0 //registerLocal(THISFN, null, null); registerLocal(Symbol.intern(fn.thisName != null ? fn.thisName : "fn__" + RT.nextID()), null, null); PSTATE state = PSTATE.REQ; PersistentVector argLocals = PersistentVector.EMPTY; for(int i = 0; i < parms.count(); i++) { if(!(parms.nth(i) instanceof Symbol)) throw new IllegalArgumentException("fn params must be Symbols"); Symbol p = (Symbol) parms.nth(i); if(p.getNamespace() != null) throw new Exception("Can't use qualified name as parameter: " + p); if(p.equals(_AMP_)) { if(state == PSTATE.REQ) state = PSTATE.REST; else throw new Exception("Invalid parameter list"); } else { LocalBinding lb = registerLocal(p, state == PSTATE.REST ? ISEQ : tagOf(p), null); argLocals = argLocals.cons(lb); switch(state) { case REQ: method.reqParms = method.reqParms.cons(lb); break; case REST: method.restParm = lb; state = PSTATE.DONE; break; default: throw new Exception("Unexpected parameter"); } } } if(method.reqParms.count() > MAX_POSITIONAL_ARITY) throw new Exception("Can't specify more than " + MAX_POSITIONAL_ARITY + " params"); LOOP_LOCALS.set(argLocals); method.argLocals = argLocals; method.body = (new BodyExpr.Parser()).parse(C.RETURN, body); return method; } finally { Var.popThreadBindings(); } } public void emit(FnExpr fn, ClassVisitor cv){ Method m = new Method(isVariadic() ? "doInvoke" : "invoke", OBJECT_TYPE, ARG_TYPES[numParams()]); GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC, m, null, EXCEPTION_TYPES, cv); gen.visitCode(); Label loopLabel = gen.mark(); gen.visitLineNumber(line, loopLabel); try { Var.pushThreadBindings(RT.map(LOOP_LABEL, loopLabel, METHOD, this)); body.emit(C.RETURN, fn, gen); Label end = gen.mark(); gen.visitLocalVariable("this", "Ljava/lang/Object;", null, loopLabel, end, 0); for(ISeq lbs = argLocals.seq(); lbs != null; lbs = lbs.next()) { LocalBinding lb = (LocalBinding) lbs.first(); gen.visitLocalVariable(lb.name, "Ljava/lang/Object;", null, loopLabel, end, lb.idx); } } finally { Var.popThreadBindings(); } gen.returnValue(); //gen.visitMaxs(1, 1); gen.endMethod(); } void emitClearLocals(GeneratorAdapter gen){ //this seems shaky given primitive locals - revisit for(int i = 1; i < numParams() + 1; i++) { if(!localsUsedInCatchFinally.contains(i)) { gen.visitInsn(Opcodes.ACONST_NULL); gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), i); // gen.storeArg(i); } } for(int i = numParams() + 1; i < maxLocal + 1; i++) { if(!localsUsedInCatchFinally.contains(i)) { LocalBinding b = (LocalBinding) RT.get(indexlocals, i); if(b == null || maybePrimitiveType(b.init) == null) { gen.visitInsn(Opcodes.ACONST_NULL); gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), i); } } } if(fn.onceOnly) { fn.emitClearCloses(gen); } } } public static class LocalBinding{ public final Symbol sym; public final Symbol tag; public Expr init; public final int idx; public final String name; public LocalBinding(int num, Symbol sym, Symbol tag, Expr init) throws Exception{ if(maybePrimitiveType(init) != null && tag != null) throw new UnsupportedOperationException("Can't type hint a local with a primitive initializer"); this.idx = num; this.sym = sym; this.tag = tag; this.init = init; name = munge(sym.name); } public boolean hasJavaClass() throws Exception{ if(init != null && init.hasJavaClass() && Util.isPrimitive(init.getJavaClass()) && !(init instanceof MaybePrimitiveExpr)) return false; return tag != null || (init != null && init.hasJavaClass()); } public Class getJavaClass() throws Exception{ return tag != null ? HostExpr.tagToClass(tag) : init.getJavaClass(); } public Class getPrimitiveType(){ return maybePrimitiveType(init); } } public static class LocalBindingExpr implements Expr, MaybePrimitiveExpr{ public final LocalBinding b; public final Symbol tag; public LocalBindingExpr(LocalBinding b, Symbol tag) throws Exception{ if(b.getPrimitiveType() != null && tag != null) throw new UnsupportedOperationException("Can't type hint a primitive local"); this.b = b; this.tag = tag; } public Object eval() throws Exception{ throw new UnsupportedOperationException("Can't eval locals"); } public void emitUnboxed(C context, FnExpr fn, GeneratorAdapter gen){ fn.emitUnboxedLocal(gen, b); } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ if(context != C.STATEMENT) fn.emitLocal(gen, b); } public boolean hasJavaClass() throws Exception{ return tag != null || b.hasJavaClass(); } public Class getJavaClass() throws Exception{ if(tag != null) return HostExpr.tagToClass(tag); return b.getJavaClass(); } } public static class BodyExpr implements Expr{ PersistentVector exprs; public final PersistentVector exprs(){ return exprs; } public BodyExpr(PersistentVector exprs){ this.exprs = exprs; } static class Parser implements IParser{ public Expr parse(C context, Object frms) throws Exception{ ISeq forms = (ISeq) frms; if(Util.equals(RT.first(forms), DO)) forms = RT.next(forms); PersistentVector exprs = PersistentVector.EMPTY; for(; forms != null; forms = forms.next()) { Expr e = (context != C.EVAL && (context == C.STATEMENT || forms.next() != null)) ? analyze(C.STATEMENT, forms.first()) : analyze(context, forms.first()); exprs = exprs.cons(e); } if(exprs.count() == 0) exprs = exprs.cons(NIL_EXPR); return new BodyExpr(exprs); } } public Object eval() throws Exception{ Object ret = null; for(Object o : exprs) { Expr e = (Expr) o; ret = e.eval(); } return ret; } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ 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); } public boolean hasJavaClass() throws Exception{ return lastExpr().hasJavaClass(); } public Class getJavaClass() throws Exception{ return lastExpr().getJavaClass(); } private Expr lastExpr(){ return (Expr) exprs.nth(exprs.count() - 1); } } public static class BindingInit{ LocalBinding binding; Expr init; public final LocalBinding binding(){ return binding; } public final Expr init(){ return init; } public BindingInit(LocalBinding binding, Expr init){ this.binding = binding; this.init = init; } } public static class LetFnExpr implements Expr{ public final PersistentVector bindingInits; public final Expr body; public LetFnExpr(PersistentVector bindingInits, Expr body){ this.bindingInits = bindingInits; this.body = body; } static class Parser implements IParser{ public Expr parse(C context, Object frm) throws Exception{ ISeq form = (ISeq) frm; //(letfns* [var (fn [args] body) ...] body...) if(!(RT.second(form) instanceof IPersistentVector)) throw new IllegalArgumentException("Bad binding form, expected vector"); IPersistentVector bindings = (IPersistentVector) RT.second(form); if((bindings.count() % 2) != 0) throw new IllegalArgumentException("Bad binding form, expected matched symbol expression pairs"); ISeq body = RT.next(RT.next(form)); if(context == C.EVAL) return analyze(context, RT.list(RT.list(FN, PersistentVector.EMPTY, form))); IPersistentMap dynamicBindings = RT.map(LOCAL_ENV, LOCAL_ENV.deref(), NEXT_LOCAL_NUM, NEXT_LOCAL_NUM.deref()); try { Var.pushThreadBindings(dynamicBindings); //pre-seed env (like Lisp labels) PersistentVector lbs = PersistentVector.EMPTY; for(int i = 0; i < bindings.count(); i += 2) { if(!(bindings.nth(i) instanceof Symbol)) throw new IllegalArgumentException( "Bad binding form, expected symbol, got: " + bindings.nth(i)); Symbol sym = (Symbol) bindings.nth(i); if(sym.getNamespace() != null) throw new Exception("Can't let qualified name: " + sym); LocalBinding lb = registerLocal(sym, tagOf(sym), null); lbs = lbs.cons(lb); } PersistentVector bindingInits = PersistentVector.EMPTY; for(int i = 0; i < bindings.count(); i += 2) { Symbol sym = (Symbol) bindings.nth(i); Expr init = analyze(C.EXPRESSION, bindings.nth(i + 1), sym.name); LocalBinding lb = (LocalBinding) lbs.nth(i / 2); lb.init = init; BindingInit bi = new BindingInit(lb, init); bindingInits = bindingInits.cons(bi); } return new LetFnExpr(bindingInits, (new BodyExpr.Parser()).parse(context, body)); } finally { Var.popThreadBindings(); } } } public Object eval() throws Exception{ throw new UnsupportedOperationException("Can't eval letfns"); } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ for(int i = 0; i < bindingInits.count(); i++) { BindingInit bi = (BindingInit) bindingInits.nth(i); gen.visitInsn(Opcodes.ACONST_NULL); gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), bi.binding.idx); } IPersistentSet lbset = PersistentHashSet.EMPTY; for(int i = 0; i < bindingInits.count(); i++) { BindingInit bi = (BindingInit) bindingInits.nth(i); lbset = (IPersistentSet) lbset.cons(bi.binding); bi.init.emit(C.EXPRESSION, fn, gen); gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), bi.binding.idx); } for(int i = 0; i < bindingInits.count(); i++) { BindingInit bi = (BindingInit) bindingInits.nth(i); FnExpr fe = (FnExpr) bi.init; gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), bi.binding.idx); fe.emitLetFnInits(gen, fn, lbset); } Label loopLabel = gen.mark(); body.emit(context, fn, gen); Label end = gen.mark(); // gen.visitLocalVariable("this", "Ljava/lang/Object;", null, loopLabel, end, 0); for(ISeq bis = bindingInits.seq(); bis != null; bis = bis.next()) { BindingInit bi = (BindingInit) bis.first(); String lname = bi.binding.name; if(lname.endsWith("__auto__")) lname += RT.nextID(); Class primc = maybePrimitiveType(bi.init); if(primc != null) gen.visitLocalVariable(lname, Type.getDescriptor(primc), null, loopLabel, end, bi.binding.idx); else gen.visitLocalVariable(lname, "Ljava/lang/Object;", null, loopLabel, end, bi.binding.idx); } } public boolean hasJavaClass() throws Exception{ return body.hasJavaClass(); } public Class getJavaClass() throws Exception{ return body.getJavaClass(); } } public static class LetExpr implements Expr{ public final PersistentVector bindingInits; public final Expr body; public final boolean isLoop; public LetExpr(PersistentVector bindingInits, Expr body, boolean isLoop){ this.bindingInits = bindingInits; this.body = body; this.isLoop = isLoop; } static class Parser implements IParser{ public Expr parse(C context, Object frm) throws Exception{ ISeq form = (ISeq) frm; //(let [var val var2 val2 ...] body...) boolean isLoop = RT.first(form).equals(LOOP); if(!(RT.second(form) instanceof IPersistentVector)) throw new IllegalArgumentException("Bad binding form, expected vector"); IPersistentVector bindings = (IPersistentVector) RT.second(form); if((bindings.count() % 2) != 0) throw new IllegalArgumentException("Bad binding form, expected matched symbol expression pairs"); ISeq body = RT.next(RT.next(form)); if(context == C.EVAL || (context == C.EXPRESSION && isLoop)) return analyze(context, RT.list(RT.list(FN, PersistentVector.EMPTY, form))); IPersistentMap dynamicBindings = RT.map(LOCAL_ENV, LOCAL_ENV.deref(), NEXT_LOCAL_NUM, NEXT_LOCAL_NUM.deref()); if(isLoop) dynamicBindings = dynamicBindings.assoc(LOOP_LOCALS, null); try { Var.pushThreadBindings(dynamicBindings); PersistentVector bindingInits = PersistentVector.EMPTY; PersistentVector loopLocals = PersistentVector.EMPTY; for(int i = 0; i < bindings.count(); i += 2) { if(!(bindings.nth(i) instanceof Symbol)) throw new IllegalArgumentException( "Bad binding form, expected symbol, got: " + bindings.nth(i)); Symbol sym = (Symbol) bindings.nth(i); if(sym.getNamespace() != null) throw new Exception("Can't let qualified name: " + sym); Expr init = analyze(C.EXPRESSION, bindings.nth(i + 1), sym.name); //sequential enhancement of env (like Lisp let*) LocalBinding lb = registerLocal(sym, tagOf(sym), init); BindingInit bi = new BindingInit(lb, init); bindingInits = bindingInits.cons(bi); if(isLoop) loopLocals = loopLocals.cons(lb); } if(isLoop) LOOP_LOCALS.set(loopLocals); return new LetExpr(bindingInits, (new BodyExpr.Parser()).parse(isLoop ? C.RETURN : context, body), isLoop); } finally { Var.popThreadBindings(); } } } 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); Class primc = maybePrimitiveType(bi.init); if(primc != null) { ((MaybePrimitiveExpr) bi.init).emitUnboxed(C.EXPRESSION, fn, gen); gen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ISTORE), bi.binding.idx); } else { bi.init.emit(C.EXPRESSION, fn, gen); gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), bi.binding.idx); } } Label loopLabel = gen.mark(); if(isLoop) { try { Var.pushThreadBindings(RT.map(LOOP_LABEL, loopLabel)); body.emit(context, fn, gen); } finally { Var.popThreadBindings(); } } else body.emit(context, fn, gen); Label end = gen.mark(); // gen.visitLocalVariable("this", "Ljava/lang/Object;", null, loopLabel, end, 0); for(ISeq bis = bindingInits.seq(); bis != null; bis = bis.next()) { BindingInit bi = (BindingInit) bis.first(); String lname = bi.binding.name; if(lname.endsWith("__auto__")) lname += RT.nextID(); Class primc = maybePrimitiveType(bi.init); if(primc != null) gen.visitLocalVariable(lname, Type.getDescriptor(primc), null, loopLabel, end, bi.binding.idx); else gen.visitLocalVariable(lname, "Ljava/lang/Object;", null, loopLabel, end, bi.binding.idx); } } public boolean hasJavaClass() throws Exception{ return body.hasJavaClass(); } public Class getJavaClass() throws Exception{ return body.getJavaClass(); } } public static class RecurExpr implements Expr{ public final IPersistentVector args; public final IPersistentVector loopLocals; public RecurExpr(IPersistentVector loopLocals, IPersistentVector args){ this.loopLocals = loopLocals; this.args = args; } public Object eval() throws Exception{ throw new UnsupportedOperationException("Can't eval recur"); } public void emit(C context, FnExpr fn, GeneratorAdapter gen){ Label loopLabel = (Label) LOOP_LABEL.deref(); if(loopLabel == null) throw new IllegalStateException(); for(int i = 0; i < loopLocals.count(); i++) { LocalBinding lb = (LocalBinding) loopLocals.nth(i); Expr arg = (Expr) args.nth(i); if(lb.getPrimitiveType() != null) { Class primc = lb.getPrimitiveType(); try { if(!(arg instanceof MaybePrimitiveExpr && arg.hasJavaClass() && arg.getJavaClass() == primc)) throw new IllegalArgumentException("recur arg for primitive local: " + lb.name + " must be matching primitive"); } catch(Exception e) { throw new RuntimeException(e); } ((MaybePrimitiveExpr) arg).emitUnboxed(C.EXPRESSION, fn, gen); } else { arg.emit(C.EXPRESSION, fn, gen); } } for(int i = loopLocals.count() - 1; i >= 0; i--) { LocalBinding lb = (LocalBinding) loopLocals.nth(i); Class primc = lb.getPrimitiveType(); if(primc != null) gen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ISTORE), lb.idx); else gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), lb.idx); } gen.goTo(loopLabel); } public boolean hasJavaClass() throws Exception{ return true; } public Class getJavaClass() throws Exception{ return null; } static class Parser implements IParser{ public Expr parse(C context, Object frm) throws Exception{ ISeq form = (ISeq) frm; IPersistentVector loopLocals = (IPersistentVector) LOOP_LOCALS.deref(); if(context != C.RETURN || loopLocals == null) throw new UnsupportedOperationException("Can only recur from tail position"); if(IN_CATCH_FINALLY.deref() != null) throw new UnsupportedOperationException("Cannot recur from catch/finally"); PersistentVector args = PersistentVector.EMPTY; for(ISeq s = RT.seq(form.next()); s != null; s = s.next()) { args = args.cons(analyze(C.EXPRESSION, s.first())); } if(args.count() != loopLocals.count()) throw new IllegalArgumentException( String.format("Mismatched argument count to recur, expected: %d args, got: %d", loopLocals.count(), args.count())); return new RecurExpr(loopLocals, args); } } } private static LocalBinding registerLocal(Symbol sym, Symbol tag, Expr init) throws Exception{ int num = getAndIncLocalNum(); LocalBinding b = new LocalBinding(num, sym, tag, init); IPersistentMap localsMap = (IPersistentMap) LOCAL_ENV.deref(); LOCAL_ENV.set(RT.assoc(localsMap, b.sym, b)); FnMethod method = (FnMethod) METHOD.deref(); method.locals = (IPersistentMap) RT.assoc(method.locals, b, b); method.indexlocals = (IPersistentMap) RT.assoc(method.indexlocals, num, b); return b; } private static int getAndIncLocalNum(){ int num = ((Number) NEXT_LOCAL_NUM.deref()).intValue(); FnMethod m = (FnMethod) METHOD.deref(); if(num > m.maxLocal) m.maxLocal = num; NEXT_LOCAL_NUM.set(num + 1); return num; } public static Expr analyze(C context, Object form) throws Exception{ return analyze(context, form, null); } private static Expr analyze(C context, Object form, String name) throws Exception{ //todo symbol macro expansion? try { if(form instanceof LazySeq) { form = RT.seq(form); if(form == null) form = PersistentList.EMPTY; } if(form == null) return NIL_EXPR; else if(form == Boolean.TRUE) return TRUE_EXPR; else if(form == Boolean.FALSE) return FALSE_EXPR; Class fclass = form.getClass(); if(fclass == Symbol.class) return analyzeSymbol((Symbol) form); else if(fclass == Keyword.class) return registerKeyword((Keyword) form); // else if(form instanceof Num) // return new NumExpr((Num) form); else if(fclass == String.class) return new StringExpr(((String) form).intern()); // else if(fclass == Character.class) // return new CharExpr((Character) form); else if(form instanceof IPersistentCollection && ((IPersistentCollection) form).count() == 0) { Expr ret = new EmptyExpr(form); if(RT.meta(form) != null) ret = new MetaExpr(ret, (MapExpr) MapExpr .parse(context == C.EVAL ? context : C.EXPRESSION, ((IObj) form).meta())); return ret; } else if(form instanceof ISeq) return analyzeSeq(context, (ISeq) form, name); else if(form instanceof IPersistentVector) return VectorExpr.parse(context, (IPersistentVector) form); else if(form instanceof IPersistentMap) return MapExpr.parse(context, (IPersistentMap) form); else if(form instanceof IPersistentSet) return SetExpr.parse(context, (IPersistentSet) form); // else //throw new UnsupportedOperationException(); return new ConstantExpr(form); } catch(Throwable e) { if(!(e instanceof CompilerException)) throw new CompilerException((String) SOURCE.deref(), (Integer) LINE.deref(), e); else throw (CompilerException) e; } } static public class CompilerException extends Exception{ public CompilerException(String source, int line, Throwable cause){ super(errorMsg(source, line, cause.toString()), cause); } public String toString(){ return getMessage(); } } static public Var isMacro(Object op) throws Exception{ //no local macros for now if(op instanceof Symbol && referenceLocal((Symbol) op) != null) return null; if(op instanceof Symbol || op instanceof Var) { Var v = (op instanceof Var) ? (Var) op : lookupVar((Symbol) op, false); if(v != null && v.isMacro()) { if(v.ns != currentNS() && !v.isPublic()) throw new IllegalStateException("var: " + v + " is not public"); return v; } } return null; } static public IFn isInline(Object op, int arity) throws Exception{ //no local inlines for now if(op instanceof Symbol && referenceLocal((Symbol) op) != null) return null; if(op instanceof Symbol || op instanceof Var) { Var v = (op instanceof Var) ? (Var) op : lookupVar((Symbol) op, false); if(v != null) { if(v.ns != currentNS() && !v.isPublic()) throw new IllegalStateException("var: " + v + " is not public"); IFn ret = (IFn) RT.get(v.meta(), inlineKey); if(ret != null) { IPersistentSet arities = (IPersistentSet) RT.get(v.meta(), inlineAritiesKey); if(arities == null || arities.contains(arity)) return ret; } } } return null; } public static boolean namesStaticMember(Symbol sym){ return sym.ns != null && namespaceFor(sym) == null; } public static Object preserveTag(ISeq src, Object dst) { Symbol tag = tagOf(src); if (tag != null && dst instanceof IObj) { IPersistentMap meta = RT.meta(dst); return ((IObj) dst).withMeta((IPersistentMap) RT.assoc(meta, RT.TAG_KEY, tag)); } return dst; } public static Object macroexpand1(Object x) throws Exception{ if(x instanceof ISeq) { ISeq form = (ISeq) x; Object op = RT.first(form); if(isSpecial(op)) return x; //macro expansion Var v = isMacro(op); if(v != null) { try { Var.pushThreadBindings(RT.map(RT.MACRO_META, RT.meta(form))); return v.applyTo(form.next()); } finally { Var.popThreadBindings(); } } else { if(op instanceof Symbol) { Symbol sym = (Symbol) op; String sname = sym.name; //(.substring s 2 5) => (. s substring 2 5) if(sym.name.charAt(0) == '.') { if(RT.length(form) < 2) throw new IllegalArgumentException( "Malformed member expression, expecting (.member target ...)"); Symbol meth = Symbol.intern(sname.substring(1)); Object target = RT.second(form); if(HostExpr.maybeClass(target, false) != null) { target = ((IObj)RT.list(IDENTITY, target)).withMeta(RT.map(RT.TAG_KEY,CLASS)); } return preserveTag(form, RT.listStar(DOT, target, meth, form.next().next())); } else if(namesStaticMember(sym)) { Symbol target = Symbol.intern(sym.ns); Class c = HostExpr.maybeClass(target, false); if(c != null) { Symbol meth = Symbol.intern(sym.name); return preserveTag(form, RT.listStar(DOT, target, meth, form.next())); } } else { //(s.substring 2 5) => (. s substring 2 5) //also (package.class.name ...) (. package.class name ...) int idx = sname.lastIndexOf('.'); // if(idx > 0 && idx < sname.length() - 1) // { // Symbol target = Symbol.intern(sname.substring(0, idx)); // Symbol meth = Symbol.intern(sname.substring(idx + 1)); // return RT.listStar(DOT, target, meth, form.rest()); // } //(StringBuilder. "foo") => (new StringBuilder "foo") //else if(idx == sname.length() - 1) return RT.listStar(NEW, Symbol.intern(sname.substring(0, idx)), form.next()); } } } } return x; } static Object macroexpand(Object form) throws Exception{ Object exf = macroexpand1(form); if(exf != form) return macroexpand(exf); return form; } private static Expr analyzeSeq(C context, ISeq form, String name) throws Exception{ Integer line = (Integer) LINE.deref(); if(RT.meta(form) != null && RT.meta(form).containsKey(RT.LINE_KEY)) line = (Integer) RT.meta(form).valAt(RT.LINE_KEY); Var.pushThreadBindings( RT.map(LINE, line)); try { Object me = macroexpand1(form); if(me != form) return analyze(context, me, name); Object op = RT.first(form); if(op == null) throw new IllegalArgumentException("Can't call nil"); IFn inline = isInline(op, RT.count(RT.next(form))); if(inline != null) return analyze(context, preserveTag(form, inline.applyTo(RT.next(form)))); IParser p; if(op.equals(FN)) return FnExpr.parse(context, form, name); else if((p = (IParser) specials.valAt(op)) != null) return p.parse(context, form); else return InvokeExpr.parse(context, form); } catch(Throwable e) { if(!(e instanceof CompilerException)) throw new CompilerException((String) SOURCE.deref(), (Integer) LINE.deref(), e); else throw (CompilerException) e; } finally { Var.popThreadBindings(); } } static String errorMsg(String source, int line, String s){ return String.format("%s (%s:%d)", s, source, line); } public static Object eval(Object form) throws Exception{ boolean createdLoader = false; if(true)//!LOADER.isBound()) { Var.pushThreadBindings(RT.map(LOADER, RT.makeClassLoader())); createdLoader = true; } try { Integer line = (Integer) LINE.deref(); if(RT.meta(form) != null && RT.meta(form).containsKey(RT.LINE_KEY)) line = (Integer) RT.meta(form).valAt(RT.LINE_KEY); Var.pushThreadBindings(RT.map(LINE, line)); try { form = macroexpand(form); if(form instanceof IPersistentCollection && Util.equals(RT.first(form), DO)) { ISeq s = RT.next(form); for(; RT.next(s) != null; s = RT.next(s)) eval(RT.first(s)); return eval(RT.first(s)); } else if(form instanceof IPersistentCollection && !(RT.first(form) instanceof Symbol && ((Symbol) RT.first(form)).name.startsWith("def"))) { FnExpr fexpr = (FnExpr) analyze(C.EXPRESSION, RT.list(FN, PersistentVector.EMPTY, form), "eval"); IFn fn = (IFn) fexpr.eval(); return fn.invoke(); } else { Expr expr = analyze(C.EVAL, form); return expr.eval(); } } finally { Var.popThreadBindings(); } } catch(Throwable e) { if(!(e instanceof CompilerException)) throw new CompilerException((String) SOURCE.deref(), (Integer) LINE.deref(), e); else throw (CompilerException) e; } finally { if(createdLoader) Var.popThreadBindings(); } } private static int registerConstant(Object o){ if(!CONSTANTS.isBound()) return -1; PersistentVector v = (PersistentVector) CONSTANTS.deref(); CONSTANTS.set(RT.conj(v, o)); return v.count(); } private static KeywordExpr registerKeyword(Keyword keyword){ if(!KEYWORDS.isBound()) return new KeywordExpr(keyword); IPersistentMap keywordsMap = (IPersistentMap) KEYWORDS.deref(); Object id = RT.get(keywordsMap, keyword); if(id == null) { KEYWORDS.set(RT.assoc(keywordsMap, keyword, registerConstant(keyword))); } return new KeywordExpr(keyword); // KeywordExpr ke = (KeywordExpr) RT.get(keywordsMap, keyword); // if(ke == null) // KEYWORDS.set(RT.assoc(keywordsMap, keyword, ke = new KeywordExpr(keyword))); // return ke; } private static Expr analyzeSymbol(Symbol sym) throws Exception{ Symbol tag = tagOf(sym); if(sym.ns == null) //ns-qualified syms are always Vars { LocalBinding b = referenceLocal(sym); if(b != null) return new LocalBindingExpr(b, tag); } else { if(namespaceFor(sym) == null) { Symbol nsSym = Symbol.create(sym.ns); Class c = HostExpr.maybeClass(nsSym, false); if(c != null) { if(Reflector.getField(c, sym.name, true) != null) return new StaticFieldExpr((Integer) LINE.deref(), c, sym.name, tag); throw new Exception("Unable to find static field: " + sym.name + " in " + c); } } } //Var v = lookupVar(sym, false); // Var v = lookupVar(sym, false); // if(v != null) // return new VarExpr(v, tag); Object o = resolve(sym); if(o instanceof Var) { Var v = (Var) o; if(isMacro(v) != null) throw new Exception("Can't take value of a macro: " + v); registerVar(v); return new VarExpr(v, tag); } else if(o instanceof Class) return new ConstantExpr(o); else if(o instanceof Symbol) return new UnresolvedVarExpr((Symbol) o); throw new Exception("Unable to resolve symbol: " + sym + " in this context"); } static Object resolve(Symbol sym, boolean allowPrivate) throws Exception{ return resolveIn(currentNS(), sym, allowPrivate); } static Object resolve(Symbol sym) throws Exception{ return resolveIn(currentNS(), sym, false); } static Namespace namespaceFor(Symbol sym){ return namespaceFor(currentNS(), sym); } static Namespace namespaceFor(Namespace inns, Symbol sym){ //note, presumes non-nil sym.ns // first check against currentNS' aliases... Symbol nsSym = Symbol.create(sym.ns); Namespace ns = inns.lookupAlias(nsSym); if(ns == null) { // ...otherwise check the Namespaces map. ns = Namespace.find(nsSym); } return ns; } static public Object resolveIn(Namespace n, Symbol sym, boolean allowPrivate) throws Exception{ //note - ns-qualified vars must already exist if(sym.ns != null) { Namespace ns = namespaceFor(n, sym); if(ns == null) throw new Exception("No such namespace: " + sym.ns); Var v = ns.findInternedVar(Symbol.create(sym.name)); if(v == null) throw new Exception("No such var: " + sym); else if(v.ns != currentNS() && !v.isPublic() && !allowPrivate) throw new IllegalStateException("var: " + sym + " is not public"); return v; } else if(sym.name.indexOf('.') > 0 || sym.name.charAt(0) == '[') { return RT.classForName(sym.name); } else if(sym.equals(NS)) return RT.NS_VAR; else if(sym.equals(IN_NS)) return RT.IN_NS_VAR; else { Object o = n.getMapping(sym); if(o == null) { if(RT.booleanCast(RT.ALLOW_UNRESOLVED_VARS.deref())) { return sym; } else { throw new Exception("Unable to resolve symbol: " + sym + " in this context"); } } return o; } } static public Object maybeResolveIn(Namespace n, Symbol sym) throws Exception{ //note - ns-qualified vars must already exist if(sym.ns != null) { Namespace ns = namespaceFor(n, sym); if(ns == null) return null; Var v = ns.findInternedVar(Symbol.create(sym.name)); if(v == null) return null; return v; } else if(sym.name.indexOf('.') > 0 || sym.name.charAt(0) == '[') { return RT.classForName(sym.name); } else if(sym.equals(NS)) return RT.NS_VAR; else if(sym.equals(IN_NS)) return RT.IN_NS_VAR; else { Object o = n.getMapping(sym); return o; } } static Var lookupVar(Symbol sym, boolean internNew) throws Exception{ Var var = null; //note - ns-qualified vars in other namespaces must already exist if(sym.ns != null) { Namespace ns = namespaceFor(sym); if(ns == null) return null; //throw new Exception("No such namespace: " + sym.ns); Symbol name = Symbol.create(sym.name); if(internNew && ns == currentNS()) var = currentNS().intern(name); else var = ns.findInternedVar(name); } else if(sym.equals(NS)) var = RT.NS_VAR; else if(sym.equals(IN_NS)) var = RT.IN_NS_VAR; else { //is it mapped? Object o = currentNS().getMapping(sym); if(o == null) { //introduce a new var in the current ns if(internNew) var = currentNS().intern(Symbol.create(sym.name)); } else if(o instanceof Var) { var = (Var) o; } else { throw new Exception("Expecting var, but " + sym + " is mapped to " + o); } } if(var != null) registerVar(var); return var; } private static void registerVar(Var var) throws Exception{ if(!VARS.isBound()) return; IPersistentMap varsMap = (IPersistentMap) VARS.deref(); Object id = RT.get(varsMap, var); if(id == null) { VARS.set(RT.assoc(varsMap, var, registerConstant(var))); } // if(varsMap != null && RT.get(varsMap, var) == null) // VARS.set(RT.assoc(varsMap, var, var)); } static Namespace currentNS(){ return (Namespace) RT.CURRENT_NS.deref(); } static void closeOver(LocalBinding b, FnMethod method){ if(b != null && method != null) { if(RT.get(method.locals, b) == null) { method.fn.closes = (IPersistentMap) RT.assoc(method.fn.closes, b, b); closeOver(b, method.parent); } else if(IN_CATCH_FINALLY.deref() != null) { method.localsUsedInCatchFinally = (PersistentHashSet) method.localsUsedInCatchFinally.cons(b.idx); } } } static LocalBinding referenceLocal(Symbol sym) throws Exception{ if(!LOCAL_ENV.isBound()) return null; LocalBinding b = (LocalBinding) RT.get(LOCAL_ENV.deref(), sym); if(b != null) { FnMethod method = (FnMethod) METHOD.deref(); closeOver(b, method); } return b; } private static Symbol tagOf(Object o){ Object tag = RT.get(RT.meta(o), RT.TAG_KEY); if(tag instanceof Symbol) return (Symbol) tag; else if(tag instanceof String) return Symbol.intern(null, (String) tag); return null; } public static Object loadFile(String file) throws Exception{ // File fo = new File(file); // if(!fo.exists()) // return null; FileInputStream f = new FileInputStream(file); try { return load(new InputStreamReader(f, RT.UTF8), new File(file).getAbsolutePath(), (new File(file)).getName()); } finally { f.close(); } } public static Object load(Reader rdr) throws Exception{ return load(rdr, null, "NO_SOURCE_FILE"); } public static Object load(Reader rdr, String sourcePath, String sourceName) throws Exception{ Object EOF = new Object(); Object ret = null; LineNumberingPushbackReader pushbackReader = (rdr instanceof LineNumberingPushbackReader) ? (LineNumberingPushbackReader) rdr : new LineNumberingPushbackReader(rdr); Var.pushThreadBindings( RT.map(LOADER, RT.makeClassLoader(), SOURCE_PATH, sourcePath, SOURCE, sourceName, RT.CURRENT_NS, RT.CURRENT_NS.deref(), LINE_BEFORE, pushbackReader.getLineNumber(), LINE_AFTER, pushbackReader.getLineNumber() )); try { for(Object r = LispReader.read(pushbackReader, false, EOF, false); r != EOF; r = LispReader.read(pushbackReader, false, EOF, false)) { LINE_AFTER.set(pushbackReader.getLineNumber()); ret = eval(r); LINE_BEFORE.set(pushbackReader.getLineNumber()); } } catch(LispReader.ReaderException e) { throw new CompilerException(sourceName, e.line, e.getCause()); } finally { Var.popThreadBindings(); } return ret; } static public void writeClassFile(String internalName, byte[] bytecode) throws Exception{ String genPath = (String) COMPILE_PATH.deref(); if(genPath == null) throw new Exception("*compile-path* not set"); String[] dirs = internalName.split("/"); String p = genPath; for(int i = 0; i < dirs.length - 1; i++) { p += File.separator + dirs[i]; (new File(p)).mkdir(); } String path = genPath + File.separator + internalName + ".class"; File cf = new File(path); cf.createNewFile(); FileOutputStream cfs = new FileOutputStream(cf); try { cfs.write(bytecode); cfs.flush(); cfs.getFD().sync(); } finally { cfs.close(); } } public static void pushNS(){ Var.pushThreadBindings(PersistentHashMap.create(Var.intern(Symbol.create("clojure.core"), Symbol.create("*ns*")), null)); } static void compile1(GeneratorAdapter gen, FnExpr fn, Object form) throws Exception{ Integer line = (Integer) LINE.deref(); if(RT.meta(form) != null && RT.meta(form).containsKey(RT.LINE_KEY)) line = (Integer) RT.meta(form).valAt(RT.LINE_KEY); Var.pushThreadBindings( RT.map(LINE, line)); try { form = macroexpand(form); if(form instanceof IPersistentCollection && Util.equals(RT.first(form), DO)) { for(ISeq s = RT.next(form); s != null; s = RT.next(s)) { compile1(gen, fn, RT.first(s)); } } else { Expr expr = analyze(C.EVAL, form); fn.keywords = (IPersistentMap) KEYWORDS.deref(); fn.vars = (IPersistentMap) VARS.deref(); fn.constants = (PersistentVector) CONSTANTS.deref(); expr.emit(C.EXPRESSION, fn, gen); expr.eval(); } } finally { Var.popThreadBindings(); } } public static Object compile(Reader rdr, String sourcePath, String sourceName) throws Exception{ if(COMPILE_PATH.deref() == null) throw new Exception("*compile-path* not set"); Object EOF = new Object(); Object ret = null; LineNumberingPushbackReader pushbackReader = (rdr instanceof LineNumberingPushbackReader) ? (LineNumberingPushbackReader) rdr : new LineNumberingPushbackReader(rdr); Var.pushThreadBindings( RT.map(SOURCE_PATH, sourcePath, SOURCE, sourceName, RT.CURRENT_NS, RT.CURRENT_NS.deref(), LINE_BEFORE, pushbackReader.getLineNumber(), LINE_AFTER, pushbackReader.getLineNumber(), CONSTANTS, PersistentVector.EMPTY, KEYWORDS, PersistentHashMap.EMPTY, VARS, PersistentHashMap.EMPTY )); try { //generate loader class FnExpr fn = new FnExpr(null); fn.internalName = sourcePath.replace(File.separator, "/").substring(0, sourcePath.lastIndexOf('.')) + RT.LOADER_SUFFIX; fn.fntype = Type.getObjectType(fn.internalName); ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); ClassVisitor cv = cw; cv.visit(V1_5, ACC_PUBLIC + ACC_SUPER, fn.internalName, null, "java/lang/Object", null); //static load method GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, Method.getMethod("void load ()"), null, null, cv); gen.visitCode(); for(Object r = LispReader.read(pushbackReader, false, EOF, false); r != EOF; r = LispReader.read(pushbackReader, false, EOF, false)) { LINE_AFTER.set(pushbackReader.getLineNumber()); compile1(gen, fn, r); LINE_BEFORE.set(pushbackReader.getLineNumber()); } //end of load gen.returnValue(); gen.endMethod(); //static fields for constants for(int i = 0; i < fn.constants.count(); i++) { cv.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, fn.constantName(i), fn.constantType(i).getDescriptor(), null, null); } //static init for constants, keywords and vars GeneratorAdapter clinitgen = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, Method.getMethod("void <clinit> ()"), null, null, cv); clinitgen.visitCode(); Label startTry = clinitgen.newLabel(); Label endTry = clinitgen.newLabel(); Label end = clinitgen.newLabel(); Label finallyLabel = clinitgen.newLabel(); if(fn.constants.count() > 0) { fn.emitConstants(clinitgen); } clinitgen.invokeStatic(Type.getType(Compiler.class), Method.getMethod("void pushNS()")); clinitgen.mark(startTry); clinitgen.invokeStatic(fn.fntype, Method.getMethod("void load()")); clinitgen.mark(endTry); clinitgen.invokeStatic(VAR_TYPE, Method.getMethod("void popThreadBindings()")); clinitgen.goTo(end); clinitgen.mark(finallyLabel); //exception should be on stack clinitgen.invokeStatic(VAR_TYPE, Method.getMethod("void popThreadBindings()")); clinitgen.throwException(); clinitgen.mark(end); clinitgen.visitTryCatchBlock(startTry, endTry, finallyLabel, null); //end of static init clinitgen.returnValue(); clinitgen.endMethod(); //end of class cv.visitEnd(); writeClassFile(fn.internalName, cw.toByteArray()); } catch(LispReader.ReaderException e) { throw new CompilerException(sourceName, e.line, e.getCause()); } finally { Var.popThreadBindings(); } return ret; } }