Mercurial > lasercutter
view src/clojure/lang/Compiler.java @ 10:ef7dbbd6452c
added clojure source goodness
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 21 Aug 2010 06:25:44 -0400 |
parents | |
children |
line wrap: on
line source
1 /**2 * Copyright (c) Rich Hickey. All rights reserved.3 * The use and distribution terms for this software are covered by the4 * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)5 * which can be found in the file epl-v10.html at the root of this distribution.6 * By using this software in any fashion, you are agreeing to be bound by7 * the terms of this license.8 * You must not remove this notice, or any other, from this software.9 **/11 /* rich Aug 21, 2007 */13 package clojure.lang;15 //*17 import clojure.asm.*;18 import clojure.asm.commons.Method;19 import clojure.asm.commons.GeneratorAdapter;20 //*/21 /*23 import org.objectweb.asm.*;24 import org.objectweb.asm.commons.Method;25 import org.objectweb.asm.commons.GeneratorAdapter;26 import org.objectweb.asm.util.TraceClassVisitor;27 import org.objectweb.asm.util.CheckClassAdapter;28 //*/30 import java.io.*;31 import java.util.*;32 import java.lang.reflect.Constructor;33 import java.lang.reflect.Modifier;35 public class Compiler implements Opcodes{37 static final Symbol DEF = Symbol.create("def");38 static final Symbol LOOP = Symbol.create("loop*");39 static final Symbol RECUR = Symbol.create("recur");40 static final Symbol IF = Symbol.create("if");41 static final Symbol LET = Symbol.create("let*");42 static final Symbol LETFN = Symbol.create("letfn*");43 static final Symbol DO = Symbol.create("do");44 static final Symbol FN = Symbol.create("fn*");45 static final Symbol QUOTE = Symbol.create("quote");46 static final Symbol THE_VAR = Symbol.create("var");47 static final Symbol DOT = Symbol.create(".");48 static final Symbol ASSIGN = Symbol.create("set!");49 //static final Symbol TRY_FINALLY = Symbol.create("try-finally");50 static final Symbol TRY = Symbol.create("try");51 static final Symbol CATCH = Symbol.create("catch");52 static final Symbol FINALLY = Symbol.create("finally");53 static final Symbol THROW = Symbol.create("throw");54 static final Symbol MONITOR_ENTER = Symbol.create("monitor-enter");55 static final Symbol MONITOR_EXIT = Symbol.create("monitor-exit");56 static final Symbol IMPORT = Symbol.create("clojure.core", "import*");57 //static final Symbol INSTANCE = Symbol.create("instance?");58 static final Symbol DEFTYPE = Symbol.create("deftype*");59 static final Symbol CASE = Symbol.create("case*");61 //static final Symbol THISFN = Symbol.create("thisfn");62 static final Symbol CLASS = Symbol.create("Class");63 static final Symbol NEW = Symbol.create("new");64 static final Symbol THIS = Symbol.create("this");65 static final Symbol REIFY = Symbol.create("reify*");66 //static final Symbol UNQUOTE = Symbol.create("unquote");67 //static final Symbol UNQUOTE_SPLICING = Symbol.create("unquote-splicing");68 //static final Symbol SYNTAX_QUOTE = Symbol.create("clojure.core", "syntax-quote");69 static final Symbol LIST = Symbol.create("clojure.core", "list");70 static final Symbol HASHMAP = Symbol.create("clojure.core", "hash-map");71 static final Symbol VECTOR = Symbol.create("clojure.core", "vector");72 static final Symbol IDENTITY = Symbol.create("clojure.core", "identity");74 static final Symbol _AMP_ = Symbol.create("&");75 static final Symbol ISEQ = Symbol.create("clojure.lang.ISeq");77 static final Keyword inlineKey = Keyword.intern(null, "inline");78 static final Keyword inlineAritiesKey = Keyword.intern(null, "inline-arities");80 static final Keyword volatileKey = Keyword.intern(null, "volatile");81 static final Keyword implementsKey = Keyword.intern(null, "implements");82 static final String COMPILE_STUB_PREFIX = "compile__stub";84 static final Keyword protocolKey = Keyword.intern(null, "protocol");85 static final Keyword onKey = Keyword.intern(null, "on");87 static final Symbol NS = Symbol.create("ns");88 static final Symbol IN_NS = Symbol.create("in-ns");90 //static final Symbol IMPORT = Symbol.create("import");91 //static final Symbol USE = Symbol.create("use");93 //static final Symbol IFN = Symbol.create("clojure.lang", "IFn");95 static final public IPersistentMap specials = PersistentHashMap.create(96 DEF, new DefExpr.Parser(),97 LOOP, new LetExpr.Parser(),98 RECUR, new RecurExpr.Parser(),99 IF, new IfExpr.Parser(),100 CASE, new CaseExpr.Parser(),101 LET, new LetExpr.Parser(),102 LETFN, new LetFnExpr.Parser(),103 DO, new BodyExpr.Parser(),104 FN, null,105 QUOTE, new ConstantExpr.Parser(),106 THE_VAR, new TheVarExpr.Parser(),107 IMPORT, new ImportExpr.Parser(),108 DOT, new HostExpr.Parser(),109 ASSIGN, new AssignExpr.Parser(),110 DEFTYPE, new NewInstanceExpr.DeftypeParser(),111 REIFY, new NewInstanceExpr.ReifyParser(),112 // TRY_FINALLY, new TryFinallyExpr.Parser(),113 TRY, new TryExpr.Parser(),114 THROW, new ThrowExpr.Parser(),115 MONITOR_ENTER, new MonitorEnterExpr.Parser(),116 MONITOR_EXIT, new MonitorExitExpr.Parser(),117 // INSTANCE, new InstanceExpr.Parser(),118 // IDENTICAL, new IdenticalExpr.Parser(),119 //THISFN, null,120 CATCH, null,121 FINALLY, null,122 // CLASS, new ClassExpr.Parser(),123 NEW, new NewExpr.Parser(),124 // UNQUOTE, null,125 // UNQUOTE_SPLICING, null,126 // SYNTAX_QUOTE, null,127 _AMP_, null128 );130 private static final int MAX_POSITIONAL_ARITY = 20;131 private static final Type OBJECT_TYPE;132 private static final Type KEYWORD_TYPE = Type.getType(Keyword.class);133 private static final Type VAR_TYPE = Type.getType(Var.class);134 private static final Type SYMBOL_TYPE = Type.getType(Symbol.class);135 //private static final Type NUM_TYPE = Type.getType(Num.class);136 private static final Type IFN_TYPE = Type.getType(IFn.class);137 private static final Type AFUNCTION_TYPE = Type.getType(AFunction.class);138 private static final Type RT_TYPE = Type.getType(RT.class);139 final static Type CLASS_TYPE = Type.getType(Class.class);140 final static Type NS_TYPE = Type.getType(Namespace.class);141 final static Type UTIL_TYPE = Type.getType(Util.class);142 final static Type REFLECTOR_TYPE = Type.getType(Reflector.class);143 final static Type THROWABLE_TYPE = Type.getType(Throwable.class);144 final static Type BOOLEAN_OBJECT_TYPE = Type.getType(Boolean.class);145 final static Type IPERSISTENTMAP_TYPE = Type.getType(IPersistentMap.class);146 final static Type IOBJ_TYPE = Type.getType(IObj.class);148 private static final Type[][] ARG_TYPES;149 private static final Type[] EXCEPTION_TYPES = {Type.getType(Exception.class)};151 static152 {153 OBJECT_TYPE = Type.getType(Object.class);154 ARG_TYPES = new Type[MAX_POSITIONAL_ARITY + 2][];155 for(int i = 0; i <= MAX_POSITIONAL_ARITY; ++i)156 {157 Type[] a = new Type[i];158 for(int j = 0; j < i; j++)159 a[j] = OBJECT_TYPE;160 ARG_TYPES[i] = a;161 }162 Type[] a = new Type[MAX_POSITIONAL_ARITY + 1];163 for(int j = 0; j < MAX_POSITIONAL_ARITY; j++)164 a[j] = OBJECT_TYPE;165 a[MAX_POSITIONAL_ARITY] = Type.getType("[Ljava/lang/Object;");166 ARG_TYPES[MAX_POSITIONAL_ARITY + 1] = a;169 }172 //symbol->localbinding173 static final public Var LOCAL_ENV = Var.create(null);175 //vector<localbinding>176 static final public Var LOOP_LOCALS = Var.create();178 //Label179 static final public Var LOOP_LABEL = Var.create();181 //vector<object>182 static final public Var CONSTANTS = Var.create();184 //IdentityHashMap185 static final public Var CONSTANT_IDS = Var.create();187 //vector<keyword>188 static final public Var KEYWORD_CALLSITES = Var.create();190 //vector<var>191 static final public Var PROTOCOL_CALLSITES = Var.create();193 //vector<var>194 static final public Var VAR_CALLSITES = Var.create();196 //keyword->constid197 static final public Var KEYWORDS = Var.create();199 //var->constid200 static final public Var VARS = Var.create();202 //FnFrame203 static final public Var METHOD = Var.create(null);205 //null or not206 static final public Var IN_CATCH_FINALLY = Var.create(null);208 //DynamicClassLoader209 static final public Var LOADER = Var.create();211 //String212 static final public Var SOURCE = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")),213 Symbol.create("*source-path*"), "NO_SOURCE_FILE");215 //String216 static final public Var SOURCE_PATH = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")),217 Symbol.create("*file*"), "NO_SOURCE_PATH");219 //String220 static final public Var COMPILE_PATH = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")),221 Symbol.create("*compile-path*"), null);222 //boolean223 static final public Var COMPILE_FILES = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")),224 Symbol.create("*compile-files*"), Boolean.FALSE);226 static final public Var INSTANCE = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")),227 Symbol.create("instance?"));229 static final public Var ADD_ANNOTATIONS = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")),230 Symbol.create("add-annotations"));232 //Integer233 static final public Var LINE = Var.create(0);235 //Integer236 static final public Var LINE_BEFORE = Var.create(0);237 static final public Var LINE_AFTER = Var.create(0);239 //Integer240 static final public Var NEXT_LOCAL_NUM = Var.create(0);242 //Integer243 static final public Var RET_LOCAL_NUM = Var.create();246 static final public Var COMPILE_STUB_SYM = Var.create(null);247 static final public Var COMPILE_STUB_CLASS = Var.create(null);250 //PathNode chain251 static final public Var CLEAR_PATH = Var.create(null);253 //tail of PathNode chain254 static final public Var CLEAR_ROOT = Var.create(null);256 //LocalBinding -> Set<LocalBindingExpr>257 static final public Var CLEAR_SITES = Var.create(null);259 public enum C{260 STATEMENT, //value ignored261 EXPRESSION, //value required262 RETURN, //tail position relative to enclosing recur frame263 EVAL264 }266 interface Expr{267 Object eval() throws Exception;269 void emit(C context, ObjExpr objx, GeneratorAdapter gen);271 boolean hasJavaClass() throws Exception;273 Class getJavaClass() throws Exception;274 }276 public static abstract class UntypedExpr implements Expr{278 public Class getJavaClass(){279 throw new IllegalArgumentException("Has no Java class");280 }282 public boolean hasJavaClass(){283 return false;284 }285 }287 interface IParser{288 Expr parse(C context, Object form) throws Exception;289 }291 static boolean isSpecial(Object sym){292 return specials.containsKey(sym);293 }295 static Symbol resolveSymbol(Symbol sym){296 //already qualified or classname?297 if(sym.name.indexOf('.') > 0)298 return sym;299 if(sym.ns != null)300 {301 Namespace ns = namespaceFor(sym);302 if(ns == null || ns.name.name == sym.ns)303 return sym;304 return Symbol.create(ns.name.name, sym.name);305 }306 Object o = currentNS().getMapping(sym);307 if(o == null)308 return Symbol.intern(currentNS().name.name, sym.name);309 else if(o instanceof Class)310 return Symbol.intern(null, ((Class) o).getName());311 else if(o instanceof Var)312 {313 Var v = (Var) o;314 return Symbol.create(v.ns.name.name, v.sym.name);315 }316 return null;318 }320 static class DefExpr implements Expr{321 public final Var var;322 public final Expr init;323 public final Expr meta;324 public final boolean initProvided;325 public final String source;326 public final int line;327 final static Method bindRootMethod = Method.getMethod("void bindRoot(Object)");328 final static Method setTagMethod = Method.getMethod("void setTag(clojure.lang.Symbol)");329 final static Method setMetaMethod = Method.getMethod("void setMeta(clojure.lang.IPersistentMap)");330 final static Method symcreate = Method.getMethod("clojure.lang.Symbol create(String, String)");332 public DefExpr(String source, int line, Var var, Expr init, Expr meta, boolean initProvided){333 this.source = source;334 this.line = line;335 this.var = var;336 this.init = init;337 this.meta = meta;338 this.initProvided = initProvided;339 }341 private boolean includesExplicitMetadata(MapExpr expr) {342 for(int i=0; i < expr.keyvals.count(); i += 2)343 {344 Keyword k = ((KeywordExpr) expr.keyvals.nth(i)).k;345 if ((k != RT.FILE_KEY) &&346 (k != RT.DECLARED_KEY) &&347 (k != RT.LINE_KEY))348 return true;349 }350 return false;351 }353 public Object eval() throws Exception{354 try355 {356 if(initProvided)357 {358 // if(init instanceof FnExpr && ((FnExpr) init).closes.count()==0)359 // var.bindRoot(new FnLoaderThunk((FnExpr) init,var));360 // else361 var.bindRoot(init.eval());362 }363 if(meta != null)364 {365 IPersistentMap metaMap = (IPersistentMap) meta.eval();366 if (initProvided || includesExplicitMetadata((MapExpr) meta))367 var.setMeta((IPersistentMap) meta.eval());368 }369 return var;370 }371 catch(Throwable e)372 {373 if(!(e instanceof CompilerException))374 throw new CompilerException(source, line, e);375 else376 throw (CompilerException) e;377 }378 }380 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){381 objx.emitVar(gen, var);382 if(meta != null)383 {384 if (initProvided || includesExplicitMetadata((MapExpr) meta))385 {386 gen.dup();387 meta.emit(C.EXPRESSION, objx, gen);388 gen.checkCast(IPERSISTENTMAP_TYPE);389 gen.invokeVirtual(VAR_TYPE, setMetaMethod);390 }391 }392 if(initProvided)393 {394 gen.dup();395 init.emit(C.EXPRESSION, objx, gen);396 gen.invokeVirtual(VAR_TYPE, bindRootMethod);397 }399 if(context == C.STATEMENT)400 gen.pop();401 }403 public boolean hasJavaClass(){404 return true;405 }407 public Class getJavaClass(){408 return Var.class;409 }411 static class Parser implements IParser{412 public Expr parse(C context, Object form) throws Exception{413 //(def x) or (def x initexpr)414 if(RT.count(form) > 3)415 throw new Exception("Too many arguments to def");416 else if(RT.count(form) < 2)417 throw new Exception("Too few arguments to def");418 else if(!(RT.second(form) instanceof Symbol))419 throw new Exception("First argument to def must be a Symbol");420 Symbol sym = (Symbol) RT.second(form);421 Var v = lookupVar(sym, true);422 if(v == null)423 throw new Exception("Can't refer to qualified var that doesn't exist");424 if(!v.ns.equals(currentNS()))425 {426 if(sym.ns == null)427 v = currentNS().intern(sym);428 // throw new Exception("Name conflict, can't def " + sym + " because namespace: " + currentNS().name +429 // " refers to:" + v);430 else431 throw new Exception("Can't create defs outside of current ns");432 }433 IPersistentMap mm = sym.meta();434 Object source_path = SOURCE_PATH.get();435 source_path = source_path == null ? "NO_SOURCE_FILE" : source_path;436 mm = (IPersistentMap) RT.assoc(mm, RT.LINE_KEY, LINE.get()).assoc(RT.FILE_KEY, source_path);437 Expr meta = analyze(context == C.EVAL ? context : C.EXPRESSION, mm);438 return new DefExpr((String) SOURCE.deref(), (Integer) LINE.deref(),439 v, analyze(context == C.EVAL ? context : C.EXPRESSION, RT.third(form), v.sym.name),440 meta, RT.count(form) == 3);441 }442 }443 }445 public static class AssignExpr implements Expr{446 public final AssignableExpr target;447 public final Expr val;449 public AssignExpr(AssignableExpr target, Expr val){450 this.target = target;451 this.val = val;452 }454 public Object eval() throws Exception{455 return target.evalAssign(val);456 }458 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){459 target.emitAssign(context, objx, gen, val);460 }462 public boolean hasJavaClass() throws Exception{463 return val.hasJavaClass();464 }466 public Class getJavaClass() throws Exception{467 return val.getJavaClass();468 }470 static class Parser implements IParser{471 public Expr parse(C context, Object frm) throws Exception{472 ISeq form = (ISeq) frm;473 if(RT.length(form) != 3)474 throw new IllegalArgumentException("Malformed assignment, expecting (set! target val)");475 Expr target = analyze(C.EXPRESSION, RT.second(form));476 if(!(target instanceof AssignableExpr))477 throw new IllegalArgumentException("Invalid assignment target");478 return new AssignExpr((AssignableExpr) target, analyze(C.EXPRESSION, RT.third(form)));479 }480 }481 }483 public static class VarExpr implements Expr, AssignableExpr{484 public final Var var;485 public final Object tag;486 final static Method getMethod = Method.getMethod("Object get()");487 final static Method setMethod = Method.getMethod("Object set(Object)");489 public VarExpr(Var var, Symbol tag){490 this.var = var;491 this.tag = tag != null ? tag : var.getTag();492 }494 public Object eval() throws Exception{495 return var.deref();496 }498 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){499 objx.emitVar(gen, var);500 gen.invokeVirtual(VAR_TYPE, getMethod);501 if(context == C.STATEMENT)502 {503 gen.pop();504 }505 }507 public boolean hasJavaClass(){508 return tag != null;509 }511 public Class getJavaClass() throws Exception{512 return HostExpr.tagToClass(tag);513 }515 public Object evalAssign(Expr val) throws Exception{516 return var.set(val.eval());517 }519 public void emitAssign(C context, ObjExpr objx, GeneratorAdapter gen,520 Expr val){521 objx.emitVar(gen, var);522 val.emit(C.EXPRESSION, objx, gen);523 gen.invokeVirtual(VAR_TYPE, setMethod);524 if(context == C.STATEMENT)525 gen.pop();526 }527 }529 public static class TheVarExpr implements Expr{530 public final Var var;532 public TheVarExpr(Var var){533 this.var = var;534 }536 public Object eval() throws Exception{537 return var;538 }540 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){541 objx.emitVar(gen, var);542 if(context == C.STATEMENT)543 gen.pop();544 }546 public boolean hasJavaClass(){547 return true;548 }550 public Class getJavaClass() throws ClassNotFoundException{551 return Var.class;552 }554 static class Parser implements IParser{555 public Expr parse(C context, Object form) throws Exception{556 Symbol sym = (Symbol) RT.second(form);557 Var v = lookupVar(sym, false);558 if(v != null)559 return new TheVarExpr(v);560 throw new Exception("Unable to resolve var: " + sym + " in this context");561 }562 }563 }565 public static class KeywordExpr implements Expr{566 public final Keyword k;568 public KeywordExpr(Keyword k){569 this.k = k;570 }572 public Object eval() throws Exception{573 return k;574 }576 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){577 objx.emitKeyword(gen, k);578 if(context == C.STATEMENT)579 gen.pop();581 }583 public boolean hasJavaClass(){584 return true;585 }587 public Class getJavaClass() throws ClassNotFoundException{588 return Keyword.class;589 }590 }592 public static class ImportExpr implements Expr{593 public final String c;594 final static Method forNameMethod = Method.getMethod("Class forName(String)");595 final static Method importClassMethod = Method.getMethod("Class importClass(Class)");596 final static Method derefMethod = Method.getMethod("Object deref()");598 public ImportExpr(String c){599 this.c = c;600 }602 public Object eval() throws Exception{603 Namespace ns = (Namespace) RT.CURRENT_NS.deref();604 ns.importClass(RT.classForName(c));605 return null;606 }608 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){609 gen.getStatic(RT_TYPE,"CURRENT_NS",VAR_TYPE);610 gen.invokeVirtual(VAR_TYPE, derefMethod);611 gen.checkCast(NS_TYPE);612 gen.push(c);613 gen.invokeStatic(CLASS_TYPE, forNameMethod);614 gen.invokeVirtual(NS_TYPE, importClassMethod);615 if(context == C.STATEMENT)616 gen.pop();617 }619 public boolean hasJavaClass(){620 return false;621 }623 public Class getJavaClass() throws ClassNotFoundException{624 throw new IllegalArgumentException("ImportExpr has no Java class");625 }627 static class Parser implements IParser{628 public Expr parse(C context, Object form) throws Exception{629 return new ImportExpr((String) RT.second(form));630 }631 }632 }634 public static abstract class LiteralExpr implements Expr{635 abstract Object val();637 public Object eval(){638 return val();639 }640 }642 static interface AssignableExpr{643 Object evalAssign(Expr val) throws Exception;645 void emitAssign(C context, ObjExpr objx, GeneratorAdapter gen, Expr val);646 }648 static public interface MaybePrimitiveExpr extends Expr{649 public boolean canEmitPrimitive();650 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen);651 }653 static public abstract class HostExpr implements Expr, MaybePrimitiveExpr{654 final static Type BOOLEAN_TYPE = Type.getType(Boolean.class);655 final static Type CHAR_TYPE = Type.getType(Character.class);656 final static Type INTEGER_TYPE = Type.getType(Integer.class);657 final static Type LONG_TYPE = Type.getType(Long.class);658 final static Type FLOAT_TYPE = Type.getType(Float.class);659 final static Type DOUBLE_TYPE = Type.getType(Double.class);660 final static Type SHORT_TYPE = Type.getType(Short.class);661 final static Type BYTE_TYPE = Type.getType(Byte.class);662 final static Type NUMBER_TYPE = Type.getType(Number.class);664 final static Method charValueMethod = Method.getMethod("char charValue()");665 final static Method booleanValueMethod = Method.getMethod("boolean booleanValue()");667 final static Method charValueOfMethod = Method.getMethod("Character valueOf(char)");668 final static Method intValueOfMethod = Method.getMethod("Integer valueOf(int)");669 final static Method longValueOfMethod = Method.getMethod("Long valueOf(long)");670 final static Method floatValueOfMethod = Method.getMethod("Float valueOf(float)");671 final static Method doubleValueOfMethod = Method.getMethod("Double valueOf(double)");672 final static Method shortValueOfMethod = Method.getMethod("Short valueOf(short)");673 final static Method byteValueOfMethod = Method.getMethod("Byte valueOf(byte)");675 final static Method intValueMethod = Method.getMethod("int intValue()");676 final static Method longValueMethod = Method.getMethod("long longValue()");677 final static Method floatValueMethod = Method.getMethod("float floatValue()");678 final static Method doubleValueMethod = Method.getMethod("double doubleValue()");679 final static Method byteValueMethod = Method.getMethod("byte byteValue()");680 final static Method shortValueMethod = Method.getMethod("short shortValue()");682 final static Method fromIntMethod = Method.getMethod("clojure.lang.Num from(int)");683 final static Method fromLongMethod = Method.getMethod("clojure.lang.Num from(long)");684 final static Method fromDoubleMethod = Method.getMethod("clojure.lang.Num from(double)");687 //*688 public static void emitBoxReturn(ObjExpr objx, GeneratorAdapter gen, Class returnType){689 if(returnType.isPrimitive())690 {691 if(returnType == boolean.class)692 {693 Label falseLabel = gen.newLabel();694 Label endLabel = gen.newLabel();695 gen.ifZCmp(GeneratorAdapter.EQ, falseLabel);696 gen.getStatic(BOOLEAN_OBJECT_TYPE, "TRUE", BOOLEAN_OBJECT_TYPE);697 gen.goTo(endLabel);698 gen.mark(falseLabel);699 gen.getStatic(BOOLEAN_OBJECT_TYPE, "FALSE", BOOLEAN_OBJECT_TYPE);700 // NIL_EXPR.emit(C.EXPRESSION, fn, gen);701 gen.mark(endLabel);702 }703 else if(returnType == void.class)704 {705 NIL_EXPR.emit(C.EXPRESSION, objx, gen);706 }707 else if(returnType == char.class)708 {709 gen.invokeStatic(CHAR_TYPE, charValueOfMethod);710 }711 else712 {713 if(returnType == int.class)714 //gen.invokeStatic(NUM_TYPE, fromIntMethod);715 gen.invokeStatic(INTEGER_TYPE, intValueOfMethod);716 else if(returnType == float.class)717 {718 //gen.visitInsn(F2D);719 gen.invokeStatic(FLOAT_TYPE, floatValueOfMethod);720 //m = floatValueOfMethod;721 }722 else if(returnType == double.class)723 gen.invokeStatic(DOUBLE_TYPE, doubleValueOfMethod);724 else if(returnType == long.class)725 gen.invokeStatic(LONG_TYPE, longValueOfMethod);726 else if(returnType == byte.class)727 gen.invokeStatic(BYTE_TYPE, byteValueOfMethod);728 else if(returnType == short.class)729 gen.invokeStatic(SHORT_TYPE, shortValueOfMethod);730 }731 }732 }734 //*/735 public static void emitUnboxArg(ObjExpr objx, GeneratorAdapter gen, Class paramType){736 if(paramType.isPrimitive())737 {738 if(paramType == boolean.class)739 {740 gen.checkCast(BOOLEAN_TYPE);741 gen.invokeVirtual(BOOLEAN_TYPE, booleanValueMethod);742 // Label falseLabel = gen.newLabel();743 // Label endLabel = gen.newLabel();744 // gen.ifNull(falseLabel);745 // gen.push(1);746 // gen.goTo(endLabel);747 // gen.mark(falseLabel);748 // gen.push(0);749 // gen.mark(endLabel);750 }751 else if(paramType == char.class)752 {753 gen.checkCast(CHAR_TYPE);754 gen.invokeVirtual(CHAR_TYPE, charValueMethod);755 }756 else757 {758 Method m = intValueMethod;759 gen.checkCast(NUMBER_TYPE);760 if(paramType == int.class)761 m = intValueMethod;762 else if(paramType == float.class)763 m = floatValueMethod;764 else if(paramType == double.class)765 m = doubleValueMethod;766 else if(paramType == long.class)767 m = longValueMethod;768 else if(paramType == byte.class)769 m = byteValueMethod;770 else if(paramType == short.class)771 m = shortValueMethod;772 gen.invokeVirtual(NUMBER_TYPE, m);773 }774 }775 else776 {777 gen.checkCast(Type.getType(paramType));778 }779 }781 static class Parser implements IParser{782 public Expr parse(C context, Object frm) throws Exception{783 ISeq form = (ISeq) frm;784 //(. x fieldname-sym) or785 //(. x 0-ary-method)786 // (. x methodname-sym args+)787 // (. x (methodname-sym args?))788 if(RT.length(form) < 3)789 throw new IllegalArgumentException("Malformed member expression, expecting (. target member ...)");790 //determine static or instance791 //static target must be symbol, either fully.qualified.Classname or Classname that has been imported792 int line = (Integer) LINE.deref();793 String source = (String) SOURCE.deref();794 Class c = maybeClass(RT.second(form), false);795 //at this point c will be non-null if static796 Expr instance = null;797 if(c == null)798 instance = analyze(context == C.EVAL ? context : C.EXPRESSION, RT.second(form));799 boolean maybeField = RT.length(form) == 3 &&800 (RT.third(form) instanceof Symbol801 || RT.third(form) instanceof Keyword);802 if(maybeField && !(RT.third(form) instanceof Keyword))803 {804 Symbol sym = (Symbol) RT.third(form);805 if(c != null)806 maybeField = Reflector.getMethods(c, 0, munge(sym.name), true).size() == 0;807 else if(instance != null && instance.hasJavaClass() && instance.getJavaClass() != null)808 maybeField = Reflector.getMethods(instance.getJavaClass(), 0, munge(sym.name), false).size() == 0;809 }810 if(maybeField) //field811 {812 Symbol sym = (RT.third(form) instanceof Keyword)?813 ((Keyword)RT.third(form)).sym814 :(Symbol) RT.third(form);815 Symbol tag = tagOf(form);816 if(c != null) {817 return new StaticFieldExpr(line, c, munge(sym.name), tag);818 } else819 return new InstanceFieldExpr(line, instance, munge(sym.name), tag);820 }821 else822 {823 ISeq call = (ISeq) ((RT.third(form) instanceof ISeq) ? RT.third(form) : RT.next(RT.next(form)));824 if(!(RT.first(call) instanceof Symbol))825 throw new IllegalArgumentException("Malformed member expression");826 Symbol sym = (Symbol) RT.first(call);827 Symbol tag = tagOf(form);828 PersistentVector args = PersistentVector.EMPTY;829 for(ISeq s = RT.next(call); s != null; s = s.next())830 args = args.cons(analyze(context == C.EVAL ? context : C.EXPRESSION, s.first()));831 if(c != null)832 return new StaticMethodExpr(source, line, tag, c, munge(sym.name), args);833 else834 return new InstanceMethodExpr(source, line, tag, instance, munge(sym.name), args);835 }836 }837 }839 private static Class maybeClass(Object form, boolean stringOk) throws Exception{840 if(form instanceof Class)841 return (Class) form;842 Class c = null;843 if(form instanceof Symbol)844 {845 Symbol sym = (Symbol) form;846 if(sym.ns == null) //if ns-qualified can't be classname847 {848 if(Util.equals(sym,COMPILE_STUB_SYM.get()))849 return (Class) COMPILE_STUB_CLASS.get();850 if(sym.name.indexOf('.') > 0 || sym.name.charAt(0) == '[')851 c = RT.classForName(sym.name);852 else853 {854 Object o = currentNS().getMapping(sym);855 if(o instanceof Class)856 c = (Class) o;857 }858 }859 }860 else if(stringOk && form instanceof String)861 c = RT.classForName((String) form);862 return c;863 }865 /*866 private static String maybeClassName(Object form, boolean stringOk){867 String className = null;868 if(form instanceof Symbol)869 {870 Symbol sym = (Symbol) form;871 if(sym.ns == null) //if ns-qualified can't be classname872 {873 if(sym.name.indexOf('.') > 0 || sym.name.charAt(0) == '[')874 className = sym.name;875 else876 {877 IPersistentMap imports = (IPersistentMap) ((Var) RT.NS_IMPORTS.get()).get();878 className = (String) imports.valAt(sym);879 }880 }881 }882 else if(stringOk && form instanceof String)883 className = (String) form;884 return className;885 }886 */887 static Class tagToClass(Object tag) throws Exception{888 Class c = maybeClass(tag, true);889 if(tag instanceof Symbol)890 {891 Symbol sym = (Symbol) tag;892 if(sym.ns == null) //if ns-qualified can't be classname893 {894 if(sym.name.equals("objects"))895 c = Object[].class;896 else if(sym.name.equals("ints"))897 c = int[].class;898 else if(sym.name.equals("longs"))899 c = long[].class;900 else if(sym.name.equals("floats"))901 c = float[].class;902 else if(sym.name.equals("doubles"))903 c = double[].class;904 else if(sym.name.equals("chars"))905 c = char[].class;906 else if(sym.name.equals("shorts"))907 c = short[].class;908 else if(sym.name.equals("bytes"))909 c = byte[].class;910 else if(sym.name.equals("booleans"))911 c = boolean[].class;912 }913 }914 if(c != null)915 return c;916 throw new IllegalArgumentException("Unable to resolve classname: " + tag);917 }918 }920 static abstract class FieldExpr extends HostExpr{921 }923 static class InstanceFieldExpr extends FieldExpr implements AssignableExpr{924 public final Expr target;925 public final Class targetClass;926 public final java.lang.reflect.Field field;927 public final String fieldName;928 public final int line;929 public final Symbol tag;930 final static Method invokeNoArgInstanceMember = Method.getMethod("Object invokeNoArgInstanceMember(Object,String)");931 final static Method setInstanceFieldMethod = Method.getMethod("Object setInstanceField(Object,String,Object)");934 public InstanceFieldExpr(int line, Expr target, String fieldName, Symbol tag) throws Exception{935 this.target = target;936 this.targetClass = target.hasJavaClass() ? target.getJavaClass() : null;937 this.field = targetClass != null ? Reflector.getField(targetClass, fieldName, false) : null;938 this.fieldName = fieldName;939 this.line = line;940 this.tag = tag;941 if(field == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))942 {943 RT.errPrintWriter()944 .format("Reflection warning, %s:%d - reference to field %s can't be resolved.\n",945 SOURCE_PATH.deref(), line, fieldName);946 }947 }949 public Object eval() throws Exception{950 return Reflector.invokeNoArgInstanceMember(target.eval(), fieldName);951 }953 public boolean canEmitPrimitive(){954 return targetClass != null && field != null &&955 Util.isPrimitive(field.getType());956 }958 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){959 gen.visitLineNumber(line, gen.mark());960 if(targetClass != null && field != null)961 {962 target.emit(C.EXPRESSION, objx, gen);963 gen.checkCast(getType(targetClass));964 gen.getField(getType(targetClass), fieldName, Type.getType(field.getType()));965 }966 else967 throw new UnsupportedOperationException("Unboxed emit of unknown member");968 }970 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){971 gen.visitLineNumber(line, gen.mark());972 if(targetClass != null && field != null)973 {974 target.emit(C.EXPRESSION, objx, gen);975 gen.checkCast(getType(targetClass));976 gen.getField(getType(targetClass), fieldName, Type.getType(field.getType()));977 //if(context != C.STATEMENT)978 HostExpr.emitBoxReturn(objx, gen, field.getType());979 if(context == C.STATEMENT)980 {981 gen.pop();982 }983 }984 else985 {986 target.emit(C.EXPRESSION, objx, gen);987 gen.push(fieldName);988 gen.invokeStatic(REFLECTOR_TYPE, invokeNoArgInstanceMember);989 if(context == C.STATEMENT)990 gen.pop();991 }992 }994 public boolean hasJavaClass() throws Exception{995 return field != null || tag != null;996 }998 public Class getJavaClass() throws Exception{999 return tag != null ? HostExpr.tagToClass(tag) : field.getType();1000 }1002 public Object evalAssign(Expr val) throws Exception{1003 return Reflector.setInstanceField(target.eval(), fieldName, val.eval());1004 }1006 public void emitAssign(C context, ObjExpr objx, GeneratorAdapter gen,1007 Expr val){1008 gen.visitLineNumber(line, gen.mark());1009 if(targetClass != null && field != null)1010 {1011 target.emit(C.EXPRESSION, objx, gen);1012 gen.checkCast(Type.getType(targetClass));1013 val.emit(C.EXPRESSION, objx, gen);1014 gen.dupX1();1015 HostExpr.emitUnboxArg(objx, gen, field.getType());1016 gen.putField(Type.getType(targetClass), fieldName, Type.getType(field.getType()));1017 }1018 else1019 {1020 target.emit(C.EXPRESSION, objx, gen);1021 gen.push(fieldName);1022 val.emit(C.EXPRESSION, objx, gen);1023 gen.invokeStatic(REFLECTOR_TYPE, setInstanceFieldMethod);1024 }1025 if(context == C.STATEMENT)1026 gen.pop();1027 }1028 }1030 static class StaticFieldExpr extends FieldExpr implements AssignableExpr{1031 //final String className;1032 public final String fieldName;1033 public final Class c;1034 public final java.lang.reflect.Field field;1035 public final Symbol tag;1036 // final static Method getStaticFieldMethod = Method.getMethod("Object getStaticField(String,String)");1037 // final static Method setStaticFieldMethod = Method.getMethod("Object setStaticField(String,String,Object)");1038 final int line;1040 public StaticFieldExpr(int line, Class c, String fieldName, Symbol tag) throws Exception{1041 //this.className = className;1042 this.fieldName = fieldName;1043 this.line = line;1044 //c = Class.forName(className);1045 this.c = c;1046 field = c.getField(fieldName);1047 this.tag = tag;1048 }1050 public Object eval() throws Exception{1051 return Reflector.getStaticField(c, fieldName);1052 }1054 public boolean canEmitPrimitive(){1055 return Util.isPrimitive(field.getType());1056 }1058 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){1059 gen.visitLineNumber(line, gen.mark());1060 gen.getStatic(Type.getType(c), fieldName, Type.getType(field.getType()));1061 }1063 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){1064 gen.visitLineNumber(line, gen.mark());1066 gen.getStatic(Type.getType(c), fieldName, Type.getType(field.getType()));1067 //if(context != C.STATEMENT)1068 HostExpr.emitBoxReturn(objx, gen, field.getType());1069 if(context == C.STATEMENT)1070 {1071 gen.pop();1072 }1073 // gen.push(className);1074 // gen.push(fieldName);1075 // gen.invokeStatic(REFLECTOR_TYPE, getStaticFieldMethod);1076 }1078 public boolean hasJavaClass(){1079 return true;1080 }1082 public Class getJavaClass() throws Exception{1083 //Class c = Class.forName(className);1084 //java.lang.reflect.Field field = c.getField(fieldName);1085 return tag != null ? HostExpr.tagToClass(tag) : field.getType();1086 }1088 public Object evalAssign(Expr val) throws Exception{1089 return Reflector.setStaticField(c, fieldName, val.eval());1090 }1092 public void emitAssign(C context, ObjExpr objx, GeneratorAdapter gen,1093 Expr val){1094 gen.visitLineNumber(line, gen.mark());1095 val.emit(C.EXPRESSION, objx, gen);1096 gen.dup();1097 HostExpr.emitUnboxArg(objx, gen, field.getType());1098 gen.putStatic(Type.getType(c), fieldName, Type.getType(field.getType()));1099 if(context == C.STATEMENT)1100 gen.pop();1101 }1104 }1106 static Class maybePrimitiveType(Expr e){1107 try1108 {1109 if(e instanceof MaybePrimitiveExpr && e.hasJavaClass() && ((MaybePrimitiveExpr)e).canEmitPrimitive())1110 {1111 Class c = e.getJavaClass();1112 if(Util.isPrimitive(c))1113 return c;1114 }1115 }1116 catch(Exception ex)1117 {1118 throw new RuntimeException(ex);1119 }1120 return null;1121 }1123 static abstract class MethodExpr extends HostExpr{1124 static void emitArgsAsArray(IPersistentVector args, ObjExpr objx, GeneratorAdapter gen){1125 gen.push(args.count());1126 gen.newArray(OBJECT_TYPE);1127 for(int i = 0; i < args.count(); i++)1128 {1129 gen.dup();1130 gen.push(i);1131 ((Expr) args.nth(i)).emit(C.EXPRESSION, objx, gen);1132 gen.arrayStore(OBJECT_TYPE);1133 }1134 }1136 public static void emitTypedArgs(ObjExpr objx, GeneratorAdapter gen, Class[] parameterTypes, IPersistentVector args){1137 for(int i = 0; i < parameterTypes.length; i++)1138 {1139 Expr e = (Expr) args.nth(i);1140 try1141 {1142 if(maybePrimitiveType(e) == parameterTypes[i])1143 {1144 ((MaybePrimitiveExpr) e).emitUnboxed(C.EXPRESSION, objx, gen);1145 }1146 else1147 {1148 e.emit(C.EXPRESSION, objx, gen);1149 HostExpr.emitUnboxArg(objx, gen, parameterTypes[i]);1150 }1151 }1152 catch(Exception e1)1153 {1154 e1.printStackTrace(RT.errPrintWriter());1155 }1157 }1158 }1159 }1161 static class InstanceMethodExpr extends MethodExpr{1162 public final Expr target;1163 public final String methodName;1164 public final IPersistentVector args;1165 public final String source;1166 public final int line;1167 public final Symbol tag;1168 public final java.lang.reflect.Method method;1170 final static Method invokeInstanceMethodMethod =1171 Method.getMethod("Object invokeInstanceMethod(Object,String,Object[])");1174 public InstanceMethodExpr(String source, int line, Symbol tag, Expr target, String methodName, IPersistentVector args)1175 throws Exception{1176 this.source = source;1177 this.line = line;1178 this.args = args;1179 this.methodName = methodName;1180 this.target = target;1181 this.tag = tag;1182 if(target.hasJavaClass() && target.getJavaClass() != null)1183 {1184 List methods = Reflector.getMethods(target.getJavaClass(), args.count(), methodName, false);1185 if(methods.isEmpty())1186 method = null;1187 //throw new IllegalArgumentException("No matching method found");1188 else1189 {1190 int methodidx = 0;1191 if(methods.size() > 1)1192 {1193 ArrayList<Class[]> params = new ArrayList();1194 ArrayList<Class> rets = new ArrayList();1195 for(int i = 0; i < methods.size(); i++)1196 {1197 java.lang.reflect.Method m = (java.lang.reflect.Method) methods.get(i);1198 params.add(m.getParameterTypes());1199 rets.add(m.getReturnType());1200 }1201 methodidx = getMatchingParams(methodName, params, args, rets);1202 }1203 java.lang.reflect.Method m =1204 (java.lang.reflect.Method) (methodidx >= 0 ? methods.get(methodidx) : null);1205 if(m != null && !Modifier.isPublic(m.getDeclaringClass().getModifiers()))1206 {1207 //public method of non-public class, try to find it in hierarchy1208 m = Reflector.getAsMethodOfPublicBase(m.getDeclaringClass(), m);1209 }1210 method = m;1211 }1212 }1213 else1214 method = null;1216 if(method == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))1217 {1218 RT.errPrintWriter()1219 .format("Reflection warning, %s:%d - call to %s can't be resolved.\n",1220 SOURCE_PATH.deref(), line, methodName);1221 }1222 }1224 public Object eval() throws Exception{1225 try1226 {1227 Object targetval = target.eval();1228 Object[] argvals = new Object[args.count()];1229 for(int i = 0; i < args.count(); i++)1230 argvals[i] = ((Expr) args.nth(i)).eval();1231 if(method != null)1232 {1233 LinkedList ms = new LinkedList();1234 ms.add(method);1235 return Reflector.invokeMatchingMethod(methodName, ms, targetval, argvals);1236 }1237 return Reflector.invokeInstanceMethod(targetval, methodName, argvals);1238 }1239 catch(Throwable e)1240 {1241 if(!(e instanceof CompilerException))1242 throw new CompilerException(source, line, e);1243 else1244 throw (CompilerException) e;1245 }1246 }1248 public boolean canEmitPrimitive(){1249 return method != null && Util.isPrimitive(method.getReturnType());1250 }1252 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){1253 gen.visitLineNumber(line, gen.mark());1254 if(method != null)1255 {1256 Type type = Type.getType(method.getDeclaringClass());1257 target.emit(C.EXPRESSION, objx, gen);1258 //if(!method.getDeclaringClass().isInterface())1259 gen.checkCast(type);1260 MethodExpr.emitTypedArgs(objx, gen, method.getParameterTypes(), args);1261 if(context == C.RETURN)1262 {1263 ObjMethod method = (ObjMethod) METHOD.deref();1264 method.emitClearLocals(gen);1265 }1266 Method m = new Method(methodName, Type.getReturnType(method), Type.getArgumentTypes(method));1267 if(method.getDeclaringClass().isInterface())1268 gen.invokeInterface(type, m);1269 else1270 gen.invokeVirtual(type, m);1271 }1272 else1273 throw new UnsupportedOperationException("Unboxed emit of unknown member");1274 }1276 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){1277 gen.visitLineNumber(line, gen.mark());1278 if(method != null)1279 {1280 Type type = Type.getType(method.getDeclaringClass());1281 target.emit(C.EXPRESSION, objx, gen);1282 //if(!method.getDeclaringClass().isInterface())1283 gen.checkCast(type);1284 MethodExpr.emitTypedArgs(objx, gen, method.getParameterTypes(), args);1285 if(context == C.RETURN)1286 {1287 ObjMethod method = (ObjMethod) METHOD.deref();1288 method.emitClearLocals(gen);1289 }1290 Method m = new Method(methodName, Type.getReturnType(method), Type.getArgumentTypes(method));1291 if(method.getDeclaringClass().isInterface())1292 gen.invokeInterface(type, m);1293 else1294 gen.invokeVirtual(type, m);1295 //if(context != C.STATEMENT || method.getReturnType() == Void.TYPE)1296 HostExpr.emitBoxReturn(objx, gen, method.getReturnType());1297 }1298 else1299 {1300 target.emit(C.EXPRESSION, objx, gen);1301 gen.push(methodName);1302 emitArgsAsArray(args, objx, gen);1303 if(context == C.RETURN)1304 {1305 ObjMethod method = (ObjMethod) METHOD.deref();1306 method.emitClearLocals(gen);1307 }1308 gen.invokeStatic(REFLECTOR_TYPE, invokeInstanceMethodMethod);1309 }1310 if(context == C.STATEMENT)1311 gen.pop();1312 }1314 public boolean hasJavaClass(){1315 return method != null || tag != null;1316 }1318 public Class getJavaClass() throws Exception{1319 return tag != null ? HostExpr.tagToClass(tag) : method.getReturnType();1320 }1321 }1324 static class StaticMethodExpr extends MethodExpr{1325 //final String className;1326 public final Class c;1327 public final String methodName;1328 public final IPersistentVector args;1329 public final String source;1330 public final int line;1331 public final java.lang.reflect.Method method;1332 public final Symbol tag;1333 final static Method forNameMethod = Method.getMethod("Class forName(String)");1334 final static Method invokeStaticMethodMethod =1335 Method.getMethod("Object invokeStaticMethod(Class,String,Object[])");1338 public StaticMethodExpr(String source, int line, Symbol tag, Class c, String methodName, IPersistentVector args)1339 throws Exception{1340 this.c = c;1341 this.methodName = methodName;1342 this.args = args;1343 this.source = source;1344 this.line = line;1345 this.tag = tag;1347 List methods = Reflector.getMethods(c, args.count(), methodName, true);1348 if(methods.isEmpty())1349 throw new IllegalArgumentException("No matching method: " + methodName);1351 int methodidx = 0;1352 if(methods.size() > 1)1353 {1354 ArrayList<Class[]> params = new ArrayList();1355 ArrayList<Class> rets = new ArrayList();1356 for(int i = 0; i < methods.size(); i++)1357 {1358 java.lang.reflect.Method m = (java.lang.reflect.Method) methods.get(i);1359 params.add(m.getParameterTypes());1360 rets.add(m.getReturnType());1361 }1362 methodidx = getMatchingParams(methodName, params, args, rets);1363 }1364 method = (java.lang.reflect.Method) (methodidx >= 0 ? methods.get(methodidx) : null);1365 if(method == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))1366 {1367 RT.errPrintWriter()1368 .format("Reflection warning, %s:%d - call to %s can't be resolved.\n",1369 SOURCE_PATH.deref(), line, methodName);1370 }1371 }1373 public Object eval() throws Exception{1374 try1375 {1376 Object[] argvals = new Object[args.count()];1377 for(int i = 0; i < args.count(); i++)1378 argvals[i] = ((Expr) args.nth(i)).eval();1379 if(method != null)1380 {1381 LinkedList ms = new LinkedList();1382 ms.add(method);1383 return Reflector.invokeMatchingMethod(methodName, ms, null, argvals);1384 }1385 return Reflector.invokeStaticMethod(c, methodName, argvals);1386 }1387 catch(Throwable e)1388 {1389 if(!(e instanceof CompilerException))1390 throw new CompilerException(source, line, e);1391 else1392 throw (CompilerException) e;1393 }1394 }1396 public boolean canEmitPrimitive(){1397 return method != null && Util.isPrimitive(method.getReturnType());1398 }1400 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){1401 gen.visitLineNumber(line, gen.mark());1402 if(method != null)1403 {1404 MethodExpr.emitTypedArgs(objx, gen, method.getParameterTypes(), args);1405 //Type type = Type.getObjectType(className.replace('.', '/'));1406 if(context == C.RETURN)1407 {1408 ObjMethod method = (ObjMethod) METHOD.deref();1409 method.emitClearLocals(gen);1410 }1411 Type type = Type.getType(c);1412 Method m = new Method(methodName, Type.getReturnType(method), Type.getArgumentTypes(method));1413 gen.invokeStatic(type, m);1414 }1415 else1416 throw new UnsupportedOperationException("Unboxed emit of unknown member");1417 }1419 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){1420 gen.visitLineNumber(line, gen.mark());1421 if(method != null)1422 {1423 MethodExpr.emitTypedArgs(objx, gen, method.getParameterTypes(), args);1424 //Type type = Type.getObjectType(className.replace('.', '/'));1425 if(context == C.RETURN)1426 {1427 ObjMethod method = (ObjMethod) METHOD.deref();1428 method.emitClearLocals(gen);1429 }1430 Type type = Type.getType(c);1431 Method m = new Method(methodName, Type.getReturnType(method), Type.getArgumentTypes(method));1432 gen.invokeStatic(type, m);1433 //if(context != C.STATEMENT || method.getReturnType() == Void.TYPE)1434 HostExpr.emitBoxReturn(objx, gen, method.getReturnType());1435 }1436 else1437 {1438 gen.push(c.getName());1439 gen.invokeStatic(CLASS_TYPE, forNameMethod);1440 gen.push(methodName);1441 emitArgsAsArray(args, objx, gen);1442 if(context == C.RETURN)1443 {1444 ObjMethod method = (ObjMethod) METHOD.deref();1445 method.emitClearLocals(gen);1446 }1447 gen.invokeStatic(REFLECTOR_TYPE, invokeStaticMethodMethod);1448 }1449 if(context == C.STATEMENT)1450 gen.pop();1451 }1453 public boolean hasJavaClass(){1454 return method != null || tag != null;1455 }1457 public Class getJavaClass() throws Exception{1458 return tag != null ? HostExpr.tagToClass(tag) : method.getReturnType();1459 }1460 }1462 static class UnresolvedVarExpr implements Expr{1463 public final Symbol symbol;1465 public UnresolvedVarExpr(Symbol symbol){1466 this.symbol = symbol;1467 }1469 public boolean hasJavaClass(){1470 return false;1471 }1473 public Class getJavaClass() throws Exception{1474 throw new IllegalArgumentException(1475 "UnresolvedVarExpr has no Java class");1476 }1478 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){1479 }1481 public Object eval() throws Exception{1482 throw new IllegalArgumentException(1483 "UnresolvedVarExpr cannot be evalled");1484 }1485 }1487 static class ConstantExpr extends LiteralExpr{1488 //stuff quoted vals in classloader at compile time, pull out at runtime1489 //this won't work for static compilation...1490 public final Object v;1491 public final int id;1493 public ConstantExpr(Object v){1494 this.v = v;1495 this.id = registerConstant(v);1496 // this.id = RT.nextID();1497 // DynamicClassLoader loader = (DynamicClassLoader) LOADER.get();1498 // loader.registerQuotedVal(id, v);1499 }1501 Object val(){1502 return v;1503 }1505 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){1506 objx.emitConstant(gen, id);1507 if(context == C.STATEMENT)1508 {1509 gen.pop();1510 // gen.loadThis();1511 // gen.invokeVirtual(OBJECT_TYPE, getClassMethod);1512 // gen.invokeVirtual(CLASS_TYPE, getClassLoaderMethod);1513 // gen.checkCast(DYNAMIC_CLASSLOADER_TYPE);1514 // gen.push(id);1515 // gen.invokeVirtual(DYNAMIC_CLASSLOADER_TYPE, getQuotedValMethod);1516 }1517 }1519 public boolean hasJavaClass(){1520 return Modifier.isPublic(v.getClass().getModifiers());1521 //return false;1522 }1524 public Class getJavaClass() throws Exception{1525 return v.getClass();1526 //throw new IllegalArgumentException("Has no Java class");1527 }1529 static class Parser implements IParser{1530 public Expr parse(C context, Object form){1531 Object v = RT.second(form);1533 if(v == null)1534 return NIL_EXPR;1535 // Class fclass = v.getClass();1536 // if(fclass == Keyword.class)1537 // return registerKeyword((Keyword) v);1538 // else if(v instanceof Num)1539 // return new NumExpr((Num) v);1540 // else if(fclass == String.class)1541 // return new StringExpr((String) v);1542 // else if(fclass == Character.class)1543 // return new CharExpr((Character) v);1544 // else if(v instanceof IPersistentCollection && ((IPersistentCollection) v).count() == 0)1545 // return new EmptyExpr(v);1546 else1547 return new ConstantExpr(v);1548 }1549 }1550 }1552 static class NilExpr extends LiteralExpr{1553 Object val(){1554 return null;1555 }1557 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){1558 gen.visitInsn(Opcodes.ACONST_NULL);1559 if(context == C.STATEMENT)1560 gen.pop();1561 }1563 public boolean hasJavaClass(){1564 return true;1565 }1567 public Class getJavaClass() throws Exception{1568 return null;1569 }1570 }1572 final static NilExpr NIL_EXPR = new NilExpr();1574 static class BooleanExpr extends LiteralExpr{1575 public final boolean val;1578 public BooleanExpr(boolean val){1579 this.val = val;1580 }1582 Object val(){1583 return val ? RT.T : RT.F;1584 }1586 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){1587 if(val)1588 gen.getStatic(BOOLEAN_OBJECT_TYPE, "TRUE", BOOLEAN_OBJECT_TYPE);1589 else1590 gen.getStatic(BOOLEAN_OBJECT_TYPE, "FALSE", BOOLEAN_OBJECT_TYPE);1591 if(context == C.STATEMENT)1592 {1593 gen.pop();1594 }1595 }1597 public boolean hasJavaClass(){1598 return true;1599 }1601 public Class getJavaClass() throws Exception{1602 return Boolean.class;1603 }1604 }1606 final static BooleanExpr TRUE_EXPR = new BooleanExpr(true);1607 final static BooleanExpr FALSE_EXPR = new BooleanExpr(false);1609 static class StringExpr extends LiteralExpr{1610 public final String str;1612 public StringExpr(String str){1613 this.str = str;1614 }1616 Object val(){1617 return str;1618 }1620 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){1621 if(context != C.STATEMENT)1622 gen.push(str);1623 }1625 public boolean hasJavaClass(){1626 return true;1627 }1629 public Class getJavaClass() throws Exception{1630 return String.class;1631 }1632 }1635 static class MonitorEnterExpr extends UntypedExpr{1636 final Expr target;1638 public MonitorEnterExpr(Expr target){1639 this.target = target;1640 }1642 public Object eval() throws Exception{1643 throw new UnsupportedOperationException("Can't eval monitor-enter");1644 }1646 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){1647 target.emit(C.EXPRESSION, objx, gen);1648 gen.monitorEnter();1649 NIL_EXPR.emit(context, objx, gen);1650 }1652 static class Parser implements IParser{1653 public Expr parse(C context, Object form) throws Exception{1654 return new MonitorEnterExpr(analyze(C.EXPRESSION, RT.second(form)));1655 }1656 }1657 }1659 static class MonitorExitExpr extends UntypedExpr{1660 final Expr target;1662 public MonitorExitExpr(Expr target){1663 this.target = target;1664 }1666 public Object eval() throws Exception{1667 throw new UnsupportedOperationException("Can't eval monitor-exit");1668 }1670 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){1671 target.emit(C.EXPRESSION, objx, gen);1672 gen.monitorExit();1673 NIL_EXPR.emit(context, objx, gen);1674 }1676 static class Parser implements IParser{1677 public Expr parse(C context, Object form) throws Exception{1678 return new MonitorExitExpr(analyze(C.EXPRESSION, RT.second(form)));1679 }1680 }1682 }1684 public static class TryExpr implements Expr{1685 public final Expr tryExpr;1686 public final Expr finallyExpr;1687 public final PersistentVector catchExprs;1688 public final int retLocal;1689 public final int finallyLocal;1691 public static class CatchClause{1692 //final String className;1693 public final Class c;1694 public final LocalBinding lb;1695 public final Expr handler;1696 Label label;1697 Label endLabel;1700 public CatchClause(Class c, LocalBinding lb, Expr handler){1701 this.c = c;1702 this.lb = lb;1703 this.handler = handler;1704 }1705 }1707 public TryExpr(Expr tryExpr, PersistentVector catchExprs, Expr finallyExpr, int retLocal, int finallyLocal){1708 this.tryExpr = tryExpr;1709 this.catchExprs = catchExprs;1710 this.finallyExpr = finallyExpr;1711 this.retLocal = retLocal;1712 this.finallyLocal = finallyLocal;1713 }1715 public Object eval() throws Exception{1716 throw new UnsupportedOperationException("Can't eval try");1717 }1719 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){1720 Label startTry = gen.newLabel();1721 Label endTry = gen.newLabel();1722 Label end = gen.newLabel();1723 Label ret = gen.newLabel();1724 Label finallyLabel = gen.newLabel();1725 for(int i = 0; i < catchExprs.count(); i++)1726 {1727 CatchClause clause = (CatchClause) catchExprs.nth(i);1728 clause.label = gen.newLabel();1729 clause.endLabel = gen.newLabel();1730 }1732 gen.mark(startTry);1733 tryExpr.emit(context, objx, gen);1734 if(context != C.STATEMENT)1735 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), retLocal);1736 gen.mark(endTry);1737 if(finallyExpr != null)1738 finallyExpr.emit(C.STATEMENT, objx, gen);1739 gen.goTo(ret);1741 for(int i = 0; i < catchExprs.count(); i++)1742 {1743 CatchClause clause = (CatchClause) catchExprs.nth(i);1744 gen.mark(clause.label);1745 //exception should be on stack1746 //put in clause local1747 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), clause.lb.idx);1748 clause.handler.emit(context, objx, gen);1749 if(context != C.STATEMENT)1750 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), retLocal);1751 gen.mark(clause.endLabel);1753 if(finallyExpr != null)1754 finallyExpr.emit(C.STATEMENT, objx, gen);1755 gen.goTo(ret);1756 }1757 if(finallyExpr != null)1758 {1759 gen.mark(finallyLabel);1760 //exception should be on stack1761 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), finallyLocal);1762 finallyExpr.emit(C.STATEMENT, objx, gen);1763 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), finallyLocal);1764 gen.throwException();1765 }1766 gen.mark(ret);1767 if(context != C.STATEMENT)1768 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), retLocal);1769 gen.mark(end);1770 for(int i = 0; i < catchExprs.count(); i++)1771 {1772 CatchClause clause = (CatchClause) catchExprs.nth(i);1773 gen.visitTryCatchBlock(startTry, endTry, clause.label, clause.c.getName().replace('.', '/'));1774 }1775 if(finallyExpr != null)1776 {1777 gen.visitTryCatchBlock(startTry, endTry, finallyLabel, null);1778 for(int i = 0; i < catchExprs.count(); i++)1779 {1780 CatchClause clause = (CatchClause) catchExprs.nth(i);1781 gen.visitTryCatchBlock(clause.label, clause.endLabel, finallyLabel, null);1782 }1783 }1784 for(int i = 0; i < catchExprs.count(); i++)1785 {1786 CatchClause clause = (CatchClause) catchExprs.nth(i);1787 gen.visitLocalVariable(clause.lb.name, "Ljava/lang/Object;", null, clause.label, clause.endLabel,1788 clause.lb.idx);1789 }1790 }1792 public boolean hasJavaClass() throws Exception{1793 return tryExpr.hasJavaClass();1794 }1796 public Class getJavaClass() throws Exception{1797 return tryExpr.getJavaClass();1798 }1800 static class Parser implements IParser{1802 public Expr parse(C context, Object frm) throws Exception{1803 ISeq form = (ISeq) frm;1804 // if(context == C.EVAL || context == C.EXPRESSION)1805 if(context != C.RETURN)1806 return analyze(context, RT.list(RT.list(FN, PersistentVector.EMPTY, form)));1808 //(try try-expr* catch-expr* finally-expr?)1809 //catch-expr: (catch class sym expr*)1810 //finally-expr: (finally expr*)1812 PersistentVector body = PersistentVector.EMPTY;1813 PersistentVector catches = PersistentVector.EMPTY;1814 Expr bodyExpr = null;1815 Expr finallyExpr = null;1816 boolean caught = false;1818 int retLocal = getAndIncLocalNum();1819 int finallyLocal = getAndIncLocalNum();1820 for(ISeq fs = form.next(); fs != null; fs = fs.next())1821 {1822 Object f = fs.first();1823 Object op = (f instanceof ISeq) ? ((ISeq) f).first() : null;1824 if(!Util.equals(op, CATCH) && !Util.equals(op, FINALLY))1825 {1826 if(caught)1827 throw new Exception("Only catch or finally clause can follow catch in try expression");1828 body = body.cons(f);1829 }1830 else1831 {1832 if(bodyExpr == null)1833 bodyExpr = (new BodyExpr.Parser()).parse(context, RT.seq(body));1834 if(Util.equals(op, CATCH))1835 {1836 Class c = HostExpr.maybeClass(RT.second(f), false);1837 if(c == null)1838 throw new IllegalArgumentException("Unable to resolve classname: " + RT.second(f));1839 if(!(RT.third(f) instanceof Symbol))1840 throw new IllegalArgumentException(1841 "Bad binding form, expected symbol, got: " + RT.third(f));1842 Symbol sym = (Symbol) RT.third(f);1843 if(sym.getNamespace() != null)1844 throw new Exception("Can't bind qualified name:" + sym);1846 IPersistentMap dynamicBindings = RT.map(LOCAL_ENV, LOCAL_ENV.deref(),1847 NEXT_LOCAL_NUM, NEXT_LOCAL_NUM.deref(),1848 IN_CATCH_FINALLY, RT.T);1849 try1850 {1851 Var.pushThreadBindings(dynamicBindings);1852 LocalBinding lb = registerLocal(sym,1853 (Symbol) (RT.second(f) instanceof Symbol ? RT.second(f)1854 : null),1855 null,false);1856 Expr handler = (new BodyExpr.Parser()).parse(context, RT.next(RT.next(RT.next(f))));1857 catches = catches.cons(new CatchClause(c, lb, handler));1858 }1859 finally1860 {1861 Var.popThreadBindings();1862 }1863 caught = true;1864 }1865 else //finally1866 {1867 if(fs.next() != null)1868 throw new Exception("finally clause must be last in try expression");1869 try1870 {1871 Var.pushThreadBindings(RT.map(IN_CATCH_FINALLY, RT.T));1872 finallyExpr = (new BodyExpr.Parser()).parse(C.STATEMENT, RT.next(f));1873 }1874 finally1875 {1876 Var.popThreadBindings();1877 }1878 }1879 }1880 }1881 if(bodyExpr == null)1882 bodyExpr = (new BodyExpr.Parser()).parse(context, RT.seq(body));1884 return new TryExpr(bodyExpr, catches, finallyExpr, retLocal,1885 finallyLocal);1886 }1887 }1888 }1890 //static class TryFinallyExpr implements Expr{1891 // final Expr tryExpr;1892 // final Expr finallyExpr;1893 //1894 //1895 // public TryFinallyExpr(Expr tryExpr, Expr finallyExpr){1896 // this.tryExpr = tryExpr;1897 // this.finallyExpr = finallyExpr;1898 // }1899 //1900 // public Object eval() throws Exception{1901 // throw new UnsupportedOperationException("Can't eval try");1902 // }1903 //1904 // public void emit(C context, FnExpr fn, GeneratorAdapter gen){1905 // Label startTry = gen.newLabel();1906 // Label endTry = gen.newLabel();1907 // Label end = gen.newLabel();1908 // Label finallyLabel = gen.newLabel();1909 // gen.visitTryCatchBlock(startTry, endTry, finallyLabel, null);1910 // gen.mark(startTry);1911 // tryExpr.emit(context, fn, gen);1912 // gen.mark(endTry);1913 // finallyExpr.emit(C.STATEMENT, fn, gen);1914 // gen.goTo(end);1915 // gen.mark(finallyLabel);1916 // //exception should be on stack1917 // finallyExpr.emit(C.STATEMENT, fn, gen);1918 // gen.throwException();1919 // gen.mark(end);1920 // }1921 //1922 // public boolean hasJavaClass() throws Exception{1923 // return tryExpr.hasJavaClass();1924 // }1925 //1926 // public Class getJavaClass() throws Exception{1927 // return tryExpr.getJavaClass();1928 // }1929 //1930 // static class Parser implements IParser{1931 // public Expr parse(C context, Object frm) throws Exception{1932 // ISeq form = (ISeq) frm;1933 // //(try-finally try-expr finally-expr)1934 // if(form.count() != 3)1935 // throw new IllegalArgumentException(1936 // "Wrong number of arguments, expecting: (try-finally try-expr finally-expr) ");1937 //1938 // if(context == C.EVAL || context == C.EXPRESSION)1939 // return analyze(context, RT.list(RT.list(FN, PersistentVector.EMPTY, form)));1940 //1941 // return new TryFinallyExpr(analyze(context, RT.second(form)),1942 // analyze(C.STATEMENT, RT.third(form)));1943 // }1944 // }1945 //}1947 static class ThrowExpr extends UntypedExpr{1948 public final Expr excExpr;1950 public ThrowExpr(Expr excExpr){1951 this.excExpr = excExpr;1952 }1955 public Object eval() throws Exception{1956 throw new Exception("Can't eval throw");1957 }1959 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){1960 excExpr.emit(C.EXPRESSION, objx, gen);1961 gen.checkCast(THROWABLE_TYPE);1962 gen.throwException();1963 }1965 static class Parser implements IParser{1966 public Expr parse(C context, Object form) throws Exception{1967 if(context == C.EVAL)1968 return analyze(context, RT.list(RT.list(FN, PersistentVector.EMPTY, form)));1969 return new ThrowExpr(analyze(C.EXPRESSION, RT.second(form)));1970 }1971 }1972 }1975 static public boolean subsumes(Class[] c1, Class[] c2){1976 //presumes matching lengths1977 Boolean better = false;1978 for(int i = 0; i < c1.length; i++)1979 {1980 if(c1[i] != c2[i])// || c2[i].isPrimitive() && c1[i] == Object.class))1981 {1982 if(!c1[i].isPrimitive() && c2[i].isPrimitive()1983 //|| Number.class.isAssignableFrom(c1[i]) && c2[i].isPrimitive()1984 ||1985 c2[i].isAssignableFrom(c1[i]))1986 better = true;1987 else1988 return false;1989 }1990 }1991 return better;1992 }1994 static int getMatchingParams(String methodName, ArrayList<Class[]> paramlists, IPersistentVector argexprs,1995 List<Class> rets)1996 throws Exception{1997 //presumes matching lengths1998 int matchIdx = -1;1999 boolean tied = false;2000 boolean foundExact = false;2001 for(int i = 0; i < paramlists.size(); i++)2002 {2003 boolean match = true;2004 ISeq aseq = argexprs.seq();2005 int exact = 0;2006 for(int p = 0; match && p < argexprs.count() && aseq != null; ++p, aseq = aseq.next())2007 {2008 Expr arg = (Expr) aseq.first();2009 Class aclass = arg.hasJavaClass() ? arg.getJavaClass() : Object.class;2010 Class pclass = paramlists.get(i)[p];2011 if(arg.hasJavaClass() && aclass == pclass)2012 exact++;2013 else2014 match = Reflector.paramArgTypeMatch(pclass, aclass);2015 }2016 if(exact == argexprs.count())2017 {2018 if(!foundExact || matchIdx == -1 || rets.get(matchIdx).isAssignableFrom(rets.get(i)))2019 matchIdx = i;2020 foundExact = true;2021 }2022 else if(match && !foundExact)2023 {2024 if(matchIdx == -1)2025 matchIdx = i;2026 else2027 {2028 if(subsumes(paramlists.get(i), paramlists.get(matchIdx)))2029 {2030 matchIdx = i;2031 tied = false;2032 }2033 else if(Arrays.equals(paramlists.get(matchIdx), paramlists.get(i)))2034 {2035 if(rets.get(matchIdx).isAssignableFrom(rets.get(i)))2036 matchIdx = i;2037 }2038 else if(!(subsumes(paramlists.get(matchIdx), paramlists.get(i))))2039 tied = true;2040 }2041 }2042 }2043 if(tied)2044 throw new IllegalArgumentException("More than one matching method found: " + methodName);2046 return matchIdx;2047 }2049 public static class NewExpr implements Expr{2050 public final IPersistentVector args;2051 public final Constructor ctor;2052 public final Class c;2053 final static Method invokeConstructorMethod =2054 Method.getMethod("Object invokeConstructor(Class,Object[])");2055 // final static Method forNameMethod = Method.getMethod("Class classForName(String)");2056 final static Method forNameMethod = Method.getMethod("Class forName(String)");2059 public NewExpr(Class c, IPersistentVector args, int line) throws Exception{2060 this.args = args;2061 this.c = c;2062 Constructor[] allctors = c.getConstructors();2063 ArrayList ctors = new ArrayList();2064 ArrayList<Class[]> params = new ArrayList();2065 ArrayList<Class> rets = new ArrayList();2066 for(int i = 0; i < allctors.length; i++)2067 {2068 Constructor ctor = allctors[i];2069 if(ctor.getParameterTypes().length == args.count())2070 {2071 ctors.add(ctor);2072 params.add(ctor.getParameterTypes());2073 rets.add(c);2074 }2075 }2076 if(ctors.isEmpty())2077 throw new IllegalArgumentException("No matching ctor found for " + c);2079 int ctoridx = 0;2080 if(ctors.size() > 1)2081 {2082 ctoridx = getMatchingParams(c.getName(), params, args, rets);2083 }2085 this.ctor = ctoridx >= 0 ? (Constructor) ctors.get(ctoridx) : null;2086 if(ctor == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))2087 {2088 RT.errPrintWriter()2089 .format("Reflection warning, %s:%d - call to %s ctor can't be resolved.\n",2090 SOURCE_PATH.deref(), line, c.getName());2091 }2092 }2094 public Object eval() throws Exception{2095 Object[] argvals = new Object[args.count()];2096 for(int i = 0; i < args.count(); i++)2097 argvals[i] = ((Expr) args.nth(i)).eval();2098 if(this.ctor != null)2099 {2100 return ctor.newInstance(Reflector.boxArgs(ctor.getParameterTypes(), argvals));2101 }2102 return Reflector.invokeConstructor(c, argvals);2103 }2105 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){2106 if(this.ctor != null)2107 {2108 Type type = getType(c);2109 gen.newInstance(type);2110 gen.dup();2111 MethodExpr.emitTypedArgs(objx, gen, ctor.getParameterTypes(), args);2112 if(context == C.RETURN)2113 {2114 ObjMethod method = (ObjMethod) METHOD.deref();2115 method.emitClearLocals(gen);2116 }2117 gen.invokeConstructor(type, new Method("<init>", Type.getConstructorDescriptor(ctor)));2118 }2119 else2120 {2121 gen.push(destubClassName(c.getName()));2122 gen.invokeStatic(CLASS_TYPE, forNameMethod);2123 MethodExpr.emitArgsAsArray(args, objx, gen);2124 if(context == C.RETURN)2125 {2126 ObjMethod method = (ObjMethod) METHOD.deref();2127 method.emitClearLocals(gen);2128 }2129 gen.invokeStatic(REFLECTOR_TYPE, invokeConstructorMethod);2130 }2131 if(context == C.STATEMENT)2132 gen.pop();2133 }2135 public boolean hasJavaClass(){2136 return true;2137 }2139 public Class getJavaClass() throws Exception{2140 return c;2141 }2143 static class Parser implements IParser{2144 public Expr parse(C context, Object frm) throws Exception{2145 int line = (Integer) LINE.deref();2146 ISeq form = (ISeq) frm;2147 //(new Classname args...)2148 if(form.count() < 2)2149 throw new Exception("wrong number of arguments, expecting: (new Classname args...)");2150 Class c = HostExpr.maybeClass(RT.second(form), false);2151 if(c == null)2152 throw new IllegalArgumentException("Unable to resolve classname: " + RT.second(form));2153 PersistentVector args = PersistentVector.EMPTY;2154 for(ISeq s = RT.next(RT.next(form)); s != null; s = s.next())2155 args = args.cons(analyze(context == C.EVAL ? context : C.EXPRESSION, s.first()));2156 return new NewExpr(c, args, line);2157 }2158 }2160 }2162 public static class MetaExpr implements Expr{2163 public final Expr expr;2164 public final MapExpr meta;2165 final static Type IOBJ_TYPE = Type.getType(IObj.class);2166 final static Method withMetaMethod = Method.getMethod("clojure.lang.IObj withMeta(clojure.lang.IPersistentMap)");2169 public MetaExpr(Expr expr, MapExpr meta){2170 this.expr = expr;2171 this.meta = meta;2172 }2174 public Object eval() throws Exception{2175 return ((IObj) expr.eval()).withMeta((IPersistentMap) meta.eval());2176 }2178 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){2179 expr.emit(C.EXPRESSION, objx, gen);2180 gen.checkCast(IOBJ_TYPE);2181 meta.emit(C.EXPRESSION, objx, gen);2182 gen.checkCast(IPERSISTENTMAP_TYPE);2183 gen.invokeInterface(IOBJ_TYPE, withMetaMethod);2184 if(context == C.STATEMENT)2185 {2186 gen.pop();2187 }2188 }2190 public boolean hasJavaClass() throws Exception{2191 return expr.hasJavaClass();2192 }2194 public Class getJavaClass() throws Exception{2195 return expr.getJavaClass();2196 }2197 }2199 public static class IfExpr implements Expr, MaybePrimitiveExpr{2200 public final Expr testExpr;2201 public final Expr thenExpr;2202 public final Expr elseExpr;2203 public final int line;2206 public IfExpr(int line, Expr testExpr, Expr thenExpr, Expr elseExpr){2207 this.testExpr = testExpr;2208 this.thenExpr = thenExpr;2209 this.elseExpr = elseExpr;2210 this.line = line;2211 }2213 public Object eval() throws Exception{2214 Object t = testExpr.eval();2215 if(t != null && t != Boolean.FALSE)2216 return thenExpr.eval();2217 return elseExpr.eval();2218 }2220 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){2221 doEmit(context, objx, gen,false);2222 }2224 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){2225 doEmit(context, objx, gen, true);2226 }2228 public void doEmit(C context, ObjExpr objx, GeneratorAdapter gen, boolean emitUnboxed){2229 Label nullLabel = gen.newLabel();2230 Label falseLabel = gen.newLabel();2231 Label endLabel = gen.newLabel();2233 gen.visitLineNumber(line, gen.mark());2235 try2236 {2237 if(maybePrimitiveType(testExpr) == boolean.class)2238 {2239 ((MaybePrimitiveExpr) testExpr).emitUnboxed(C.EXPRESSION, objx, gen);2240 gen.ifZCmp(gen.EQ, falseLabel);2241 }2242 else2243 {2244 testExpr.emit(C.EXPRESSION, objx, gen);2245 gen.dup();2246 gen.ifNull(nullLabel);2247 gen.getStatic(BOOLEAN_OBJECT_TYPE, "FALSE", BOOLEAN_OBJECT_TYPE);2248 gen.visitJumpInsn(IF_ACMPEQ, falseLabel);2249 }2250 }2251 catch(Exception e)2252 {2253 throw new RuntimeException(e);2254 }2255 if(emitUnboxed)2256 ((MaybePrimitiveExpr)thenExpr).emitUnboxed(context, objx, gen);2257 else2258 thenExpr.emit(context, objx, gen);2259 gen.goTo(endLabel);2260 gen.mark(nullLabel);2261 gen.pop();2262 gen.mark(falseLabel);2263 if(emitUnboxed)2264 ((MaybePrimitiveExpr)elseExpr).emitUnboxed(context, objx, gen);2265 else2266 elseExpr.emit(context, objx, gen);2267 gen.mark(endLabel);2268 }2270 public boolean hasJavaClass() throws Exception{2271 return thenExpr.hasJavaClass()2272 && elseExpr.hasJavaClass()2273 &&2274 (thenExpr.getJavaClass() == elseExpr.getJavaClass()2275 || (thenExpr.getJavaClass() == null && !elseExpr.getJavaClass().isPrimitive())2276 || (elseExpr.getJavaClass() == null && !thenExpr.getJavaClass().isPrimitive()));2277 }2279 public boolean canEmitPrimitive(){2280 try2281 {2282 return thenExpr instanceof MaybePrimitiveExpr2283 && elseExpr instanceof MaybePrimitiveExpr2284 && thenExpr.getJavaClass() == elseExpr.getJavaClass()2285 && ((MaybePrimitiveExpr)thenExpr).canEmitPrimitive()2286 && ((MaybePrimitiveExpr)elseExpr).canEmitPrimitive();2287 }2288 catch(Exception e)2289 {2290 return false;2291 }2292 }2294 public Class getJavaClass() throws Exception{2295 Class thenClass = thenExpr.getJavaClass();2296 if(thenClass != null)2297 return thenClass;2298 return elseExpr.getJavaClass();2299 }2301 static class Parser implements IParser{2302 public Expr parse(C context, Object frm) throws Exception{2303 ISeq form = (ISeq) frm;2304 //(if test then) or (if test then else)2305 if(form.count() > 4)2306 throw new Exception("Too many arguments to if");2307 else if(form.count() < 3)2308 throw new Exception("Too few arguments to if");2309 PathNode branch = new PathNode(PATHTYPE.BRANCH, (PathNode) CLEAR_PATH.get());2310 Expr testexpr = analyze(context == C.EVAL ? context : C.EXPRESSION, RT.second(form));2311 Expr thenexpr, elseexpr;2312 try {2313 Var.pushThreadBindings(2314 RT.map(CLEAR_PATH, new PathNode(PATHTYPE.PATH,branch)));2315 thenexpr = analyze(context, RT.third(form));2316 }2317 finally{2318 Var.popThreadBindings();2319 }2320 try {2321 Var.pushThreadBindings(2322 RT.map(CLEAR_PATH, new PathNode(PATHTYPE.PATH,branch)));2323 elseexpr = analyze(context, RT.fourth(form));2324 }2325 finally{2326 Var.popThreadBindings();2327 }2328 return new IfExpr((Integer) LINE.deref(),2329 testexpr,2330 thenexpr,2331 elseexpr);2332 }2333 }2334 }2336 static final public IPersistentMap CHAR_MAP =2337 PersistentHashMap.create('-', "_",2338 // '.', "_DOT_",2339 ':', "_COLON_",2340 '+', "_PLUS_",2341 '>', "_GT_",2342 '<', "_LT_",2343 '=', "_EQ_",2344 '~', "_TILDE_",2345 '!', "_BANG_",2346 '@', "_CIRCA_",2347 '#', "_SHARP_",2348 '$', "_DOLLARSIGN_",2349 '%', "_PERCENT_",2350 '^', "_CARET_",2351 '&', "_AMPERSAND_",2352 '*', "_STAR_",2353 '|', "_BAR_",2354 '{', "_LBRACE_",2355 '}', "_RBRACE_",2356 '[', "_LBRACK_",2357 ']', "_RBRACK_",2358 '/', "_SLASH_",2359 '\\', "_BSLASH_",2360 '?', "_QMARK_");2362 static public String munge(String name){2363 StringBuilder sb = new StringBuilder();2364 for(char c : name.toCharArray())2365 {2366 String sub = (String) CHAR_MAP.valAt(c);2367 if(sub != null)2368 sb.append(sub);2369 else2370 sb.append(c);2371 }2372 return sb.toString();2373 }2375 public static class EmptyExpr implements Expr{2376 public final Object coll;2377 final static Type HASHMAP_TYPE = Type.getType(PersistentArrayMap.class);2378 final static Type HASHSET_TYPE = Type.getType(PersistentHashSet.class);2379 final static Type VECTOR_TYPE = Type.getType(PersistentVector.class);2380 final static Type LIST_TYPE = Type.getType(PersistentList.class);2381 final static Type EMPTY_LIST_TYPE = Type.getType(PersistentList.EmptyList.class);2384 public EmptyExpr(Object coll){2385 this.coll = coll;2386 }2388 public Object eval() throws Exception{2389 return coll;2390 }2392 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){2393 if(coll instanceof IPersistentList)2394 gen.getStatic(LIST_TYPE, "EMPTY", EMPTY_LIST_TYPE);2395 else if(coll instanceof IPersistentVector)2396 gen.getStatic(VECTOR_TYPE, "EMPTY", VECTOR_TYPE);2397 else if(coll instanceof IPersistentMap)2398 gen.getStatic(HASHMAP_TYPE, "EMPTY", HASHMAP_TYPE);2399 else if(coll instanceof IPersistentSet)2400 gen.getStatic(HASHSET_TYPE, "EMPTY", HASHSET_TYPE);2401 else2402 throw new UnsupportedOperationException("Unknown Collection type");2403 if(context == C.STATEMENT)2404 {2405 gen.pop();2406 }2407 }2409 public boolean hasJavaClass() throws Exception{2410 return true;2411 }2413 public Class getJavaClass() throws Exception{2414 if(coll instanceof IPersistentList)2415 return IPersistentList.class;2416 else if(coll instanceof IPersistentVector)2417 return IPersistentVector.class;2418 else if(coll instanceof IPersistentMap)2419 return IPersistentMap.class;2420 else if(coll instanceof IPersistentSet)2421 return IPersistentSet.class;2422 else2423 throw new UnsupportedOperationException("Unknown Collection type");2424 }2425 }2427 public static class ListExpr implements Expr{2428 public final IPersistentVector args;2429 final static Method arrayToListMethod = Method.getMethod("clojure.lang.ISeq arrayToList(Object[])");2432 public ListExpr(IPersistentVector args){2433 this.args = args;2434 }2436 public Object eval() throws Exception{2437 IPersistentVector ret = PersistentVector.EMPTY;2438 for(int i = 0; i < args.count(); i++)2439 ret = (IPersistentVector) ret.cons(((Expr) args.nth(i)).eval());2440 return ret.seq();2441 }2443 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){2444 MethodExpr.emitArgsAsArray(args, objx, gen);2445 gen.invokeStatic(RT_TYPE, arrayToListMethod);2446 if(context == C.STATEMENT)2447 gen.pop();2448 }2450 public boolean hasJavaClass() throws Exception{2451 return true;2452 }2454 public Class getJavaClass() throws Exception{2455 return IPersistentList.class;2456 }2458 }2460 public static class MapExpr implements Expr{2461 public final IPersistentVector keyvals;2462 final static Method mapMethod = Method.getMethod("clojure.lang.IPersistentMap map(Object[])");2465 public MapExpr(IPersistentVector keyvals){2466 this.keyvals = keyvals;2467 }2469 public Object eval() throws Exception{2470 Object[] ret = new Object[keyvals.count()];2471 for(int i = 0; i < keyvals.count(); i++)2472 ret[i] = ((Expr) keyvals.nth(i)).eval();2473 return RT.map(ret);2474 }2476 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){2477 MethodExpr.emitArgsAsArray(keyvals, objx, gen);2478 gen.invokeStatic(RT_TYPE, mapMethod);2479 if(context == C.STATEMENT)2480 gen.pop();2481 }2483 public boolean hasJavaClass() throws Exception{2484 return true;2485 }2487 public Class getJavaClass() throws Exception{2488 return IPersistentMap.class;2489 }2492 static public Expr parse(C context, IPersistentMap form) throws Exception{2493 IPersistentVector keyvals = PersistentVector.EMPTY;2494 for(ISeq s = RT.seq(form); s != null; s = s.next())2495 {2496 IMapEntry e = (IMapEntry) s.first();2497 keyvals = (IPersistentVector) keyvals.cons(analyze(context == C.EVAL ? context : C.EXPRESSION, e.key()));2498 keyvals = (IPersistentVector) keyvals.cons(analyze(context == C.EVAL ? context : C.EXPRESSION, e.val()));2499 }2500 Expr ret = new MapExpr(keyvals);2501 if(form instanceof IObj && ((IObj) form).meta() != null)2502 return new MetaExpr(ret, (MapExpr) MapExpr2503 .parse(context == C.EVAL ? context : C.EXPRESSION, ((IObj) form).meta()));2504 else2505 return ret;2506 }2507 }2509 public static class SetExpr implements Expr{2510 public final IPersistentVector keys;2511 final static Method setMethod = Method.getMethod("clojure.lang.IPersistentSet set(Object[])");2514 public SetExpr(IPersistentVector keys){2515 this.keys = keys;2516 }2518 public Object eval() throws Exception{2519 Object[] ret = new Object[keys.count()];2520 for(int i = 0; i < keys.count(); i++)2521 ret[i] = ((Expr) keys.nth(i)).eval();2522 return RT.set(ret);2523 }2525 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){2526 MethodExpr.emitArgsAsArray(keys, objx, gen);2527 gen.invokeStatic(RT_TYPE, setMethod);2528 if(context == C.STATEMENT)2529 gen.pop();2530 }2532 public boolean hasJavaClass() throws Exception{2533 return true;2534 }2536 public Class getJavaClass() throws Exception{2537 return IPersistentSet.class;2538 }2541 static public Expr parse(C context, IPersistentSet form) throws Exception{2542 IPersistentVector keys = PersistentVector.EMPTY;2543 for(ISeq s = RT.seq(form); s != null; s = s.next())2544 {2545 Object e = s.first();2546 keys = (IPersistentVector) keys.cons(analyze(context == C.EVAL ? context : C.EXPRESSION, e));2547 }2548 Expr ret = new SetExpr(keys);2549 if(form instanceof IObj && ((IObj) form).meta() != null)2550 return new MetaExpr(ret, (MapExpr) MapExpr2551 .parse(context == C.EVAL ? context : C.EXPRESSION, ((IObj) form).meta()));2552 else2553 return ret;2554 }2555 }2557 public static class VectorExpr implements Expr{2558 public final IPersistentVector args;2559 final static Method vectorMethod = Method.getMethod("clojure.lang.IPersistentVector vector(Object[])");2562 public VectorExpr(IPersistentVector args){2563 this.args = args;2564 }2566 public Object eval() throws Exception{2567 IPersistentVector ret = PersistentVector.EMPTY;2568 for(int i = 0; i < args.count(); i++)2569 ret = (IPersistentVector) ret.cons(((Expr) args.nth(i)).eval());2570 return ret;2571 }2573 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){2574 MethodExpr.emitArgsAsArray(args, objx, gen);2575 gen.invokeStatic(RT_TYPE, vectorMethod);2576 if(context == C.STATEMENT)2577 gen.pop();2578 }2580 public boolean hasJavaClass() throws Exception{2581 return true;2582 }2584 public Class getJavaClass() throws Exception{2585 return IPersistentVector.class;2586 }2588 static public Expr parse(C context, IPersistentVector form) throws Exception{2589 IPersistentVector args = PersistentVector.EMPTY;2590 for(int i = 0; i < form.count(); i++)2591 args = (IPersistentVector) args.cons(analyze(context == C.EVAL ? context : C.EXPRESSION, form.nth(i)));2592 Expr ret = new VectorExpr(args);2593 if(form instanceof IObj && ((IObj) form).meta() != null)2594 return new MetaExpr(ret, (MapExpr) MapExpr2595 .parse(context == C.EVAL ? context : C.EXPRESSION, ((IObj) form).meta()));2596 else2597 return ret;2598 }2600 }2602 static class KeywordInvokeExpr implements Expr{2603 public final KeywordExpr kw;2604 public final Object tag;2605 public final Expr target;2606 public final int line;2607 public final int siteIndex;2608 public final String source;2609 static Type ILOOKUP_TYPE = Type.getType(ILookup.class);2611 public KeywordInvokeExpr(String source, int line, Symbol tag, KeywordExpr kw, Expr target){2612 this.source = source;2613 this.kw = kw;2614 this.target = target;2615 this.line = line;2616 this.tag = tag;2617 this.siteIndex = registerKeywordCallsite(kw.k);2618 }2620 public Object eval() throws Exception{2621 try2622 {2623 return kw.k.invoke(target.eval());2624 }2625 catch(Throwable e)2626 {2627 if(!(e instanceof CompilerException))2628 throw new CompilerException(source, line, e);2629 else2630 throw (CompilerException) e;2631 }2632 }2634 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){2635 Label endLabel = gen.newLabel();2636 Label faultLabel = gen.newLabel();2638 gen.visitLineNumber(line, gen.mark());2639 gen.getStatic(objx.objtype, objx.thunkNameStatic(siteIndex),ObjExpr.ILOOKUP_THUNK_TYPE);2640 gen.dup();2641 target.emit(C.EXPRESSION, objx, gen);2642 gen.dupX2();2643 gen.invokeInterface(ObjExpr.ILOOKUP_THUNK_TYPE, Method.getMethod("Object get(Object)"));2644 gen.dupX2();2645 gen.visitJumpInsn(IF_ACMPEQ, faultLabel);2646 gen.pop();2647 gen.goTo(endLabel);2649 gen.mark(faultLabel);2650 gen.swap();2651 gen.pop();2652 gen.getStatic(objx.objtype, objx.siteNameStatic(siteIndex),ObjExpr.KEYWORD_LOOKUPSITE_TYPE);2653 gen.swap();2654 gen.loadThis();2655 gen.invokeInterface(ObjExpr.ILOOKUP_SITE_TYPE,2656 Method.getMethod("Object fault(Object, clojure.lang.ILookupHost)"));2658 gen.mark(endLabel);2659 if(context == C.STATEMENT)2660 gen.pop();2661 }2663 public void emit2(C context, ObjExpr objx, GeneratorAdapter gen){2664 Label endLabel = gen.newLabel();2665 Label faultLabel = gen.newLabel();2667 gen.visitLineNumber(line, gen.mark());2668 target.emit(C.EXPRESSION, objx, gen);2669 gen.dup();2670 gen.getStatic(objx.objtype, objx.thunkNameStatic(siteIndex),ObjExpr.ILOOKUP_THUNK_TYPE);2671 gen.swap();2672 gen.getStatic(objx.objtype, objx.siteNameStatic(siteIndex),ObjExpr.KEYWORD_LOOKUPSITE_TYPE);2673 /// gen.loadThis();2674 gen.invokeInterface(ObjExpr.ILOOKUP_THUNK_TYPE,2675 Method.getMethod("Object get(Object,clojure.lang.ILookupSite)"));2676 // gen.invokeInterface(ObjExpr.ILOOKUP_THUNK_TYPE,2677 // Method.getMethod("Object get(Object,clojure.lang.ILookupSite,clojure.lang.ILookupHost)"));2678 gen.dup();2679 gen.getStatic(objx.objtype, objx.siteNameStatic(siteIndex),ObjExpr.KEYWORD_LOOKUPSITE_TYPE);2680 gen.visitJumpInsn(IF_ACMPEQ, faultLabel);2681 gen.swap();2682 gen.pop();2683 gen.goTo(endLabel);2685 gen.mark(faultLabel);2686 gen.swap();2687 gen.loadThis();2688 gen.invokeInterface(ObjExpr.ILOOKUP_SITE_TYPE,2689 Method.getMethod("Object fault(Object, clojure.lang.ILookupHost)"));2691 gen.mark(endLabel);2692 if(context == C.STATEMENT)2693 gen.pop();2694 }2696 public void emitInstance(C context, ObjExpr objx, GeneratorAdapter gen){2697 gen.visitLineNumber(line, gen.mark());2698 gen.loadThis();2699 gen.getField(objx.objtype, objx.thunkName(siteIndex),ObjExpr.ILOOKUP_THUNK_TYPE);2700 target.emit(C.EXPRESSION, objx, gen);2701 gen.loadThis();2702 gen.getField(objx.objtype, objx.siteName(siteIndex),ObjExpr.ILOOKUP_SITE_TYPE);2703 gen.loadThis();2704 gen.checkCast(Type.getType(ILookupHost.class));2705 gen.invokeInterface(ObjExpr.ILOOKUP_THUNK_TYPE,2706 Method.getMethod("Object get(Object,clojure.lang.ILookupSite,clojure.lang.ILookupHost)"));2707 if(context == C.STATEMENT)2708 gen.pop();2709 }2711 public void emitNormal(C context, ObjExpr objx, GeneratorAdapter gen){2712 Label slowLabel = gen.newLabel();2713 Label endLabel = gen.newLabel();2715 gen.visitLineNumber(line, gen.mark());2716 target.emit(C.EXPRESSION, objx, gen);2717 gen.dup();2718 gen.instanceOf(ILOOKUP_TYPE);2719 gen.ifZCmp(GeneratorAdapter.EQ, slowLabel);2720 kw.emit(C.EXPRESSION, objx, gen);2721 gen.invokeInterface(ILOOKUP_TYPE, new Method("valAt", OBJECT_TYPE, ARG_TYPES[1]));2722 gen.goTo(endLabel);2724 gen.mark(slowLabel);2725 kw.emit(C.EXPRESSION, objx, gen);2726 gen.invokeStatic(RT_TYPE, new Method("get", OBJECT_TYPE, ARG_TYPES[2]));2728 gen.mark(endLabel);2730 if(context == C.STATEMENT)2731 gen.pop();2732 }2734 public boolean hasJavaClass() throws Exception{2735 return tag != null;2736 }2738 public Class getJavaClass() throws Exception{2739 return HostExpr.tagToClass(tag);2740 }2742 }2743 //static class KeywordSiteInvokeExpr implements Expr{2744 // public final Expr site;2745 // public final Object tag;2746 // public final Expr target;2747 // public final int line;2748 // public final String source;2749 //2750 // public KeywordSiteInvokeExpr(String source, int line, Symbol tag, Expr site, Expr target){2751 // this.source = source;2752 // this.site = site;2753 // this.target = target;2754 // this.line = line;2755 // this.tag = tag;2756 // }2757 //2758 // public Object eval() throws Exception{2759 // try2760 // {2761 // KeywordCallSite s = (KeywordCallSite) site.eval();2762 // return s.thunk.invoke(s,target.eval());2763 // }2764 // catch(Throwable e)2765 // {2766 // if(!(e instanceof CompilerException))2767 // throw new CompilerException(source, line, e);2768 // else2769 // throw (CompilerException) e;2770 // }2771 // }2772 //2773 // public void emit(C context, ObjExpr objx, GeneratorAdapter gen){2774 // gen.visitLineNumber(line, gen.mark());2775 // site.emit(C.EXPRESSION, objx, gen);2776 // gen.dup();2777 // gen.getField(Type.getType(KeywordCallSite.class),"thunk",IFN_TYPE);2778 // gen.swap();2779 // target.emit(C.EXPRESSION, objx, gen);2780 //2781 // gen.invokeInterface(IFN_TYPE, new Method("invoke", OBJECT_TYPE, ARG_TYPES[2]));2782 // if(context == C.STATEMENT)2783 // gen.pop();2784 // }2785 //2786 // public boolean hasJavaClass() throws Exception{2787 // return tag != null;2788 // }2789 //2790 // public Class getJavaClass() throws Exception{2791 // return HostExpr.tagToClass(tag);2792 // }2793 //2794 //}2796 public static class InstanceOfExpr implements Expr, MaybePrimitiveExpr{2797 Expr expr;2798 Class c;2800 public InstanceOfExpr(Class c, Expr expr){2801 this.expr = expr;2802 this.c = c;2803 }2805 public Object eval() throws Exception{2806 if(c.isInstance(expr.eval()))2807 return RT.T;2808 return RT.F;2809 }2811 public boolean canEmitPrimitive(){2812 return true;2813 }2815 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){2816 expr.emit(C.EXPRESSION,objx,gen);2817 gen.instanceOf(Type.getType(c));2818 }2820 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){2821 emitUnboxed(context,objx,gen);2822 HostExpr.emitBoxReturn(objx,gen,Boolean.TYPE);2823 if(context == C.STATEMENT)2824 gen.pop();2825 }2827 public boolean hasJavaClass() throws Exception{2828 return true;2829 }2831 public Class getJavaClass() throws Exception{2832 return Boolean.TYPE;2833 }2835 }2837 static class InvokeExpr implements Expr{2838 public final Expr fexpr;2839 public final Object tag;2840 public final IPersistentVector args;2841 public final int line;2842 public final String source;2843 public boolean isProtocol = false;2844 public boolean isDirect = false;2845 public int siteIndex = -1;2846 public Class protocolOn;2847 public java.lang.reflect.Method onMethod;2848 static Keyword onKey = Keyword.intern("on");2849 static Keyword methodMapKey = Keyword.intern("method-map");2850 static Keyword dynamicKey = Keyword.intern("dynamic");2852 public InvokeExpr(String source, int line, Symbol tag, Expr fexpr, IPersistentVector args) throws Exception{2853 this.source = source;2854 this.fexpr = fexpr;2855 this.args = args;2856 this.line = line;2857 if(fexpr instanceof VarExpr)2858 {2859 Var fvar = ((VarExpr)fexpr).var;2860 Var pvar = (Var)RT.get(fvar.meta(), protocolKey);2861 if(pvar != null && PROTOCOL_CALLSITES.isBound())2862 {2863 this.isProtocol = true;2864 this.siteIndex = registerProtocolCallsite(((VarExpr)fexpr).var);2865 Object pon = RT.get(pvar.get(), onKey);2866 this.protocolOn = HostExpr.maybeClass(pon,false);2867 if(this.protocolOn != null)2868 {2869 IPersistentMap mmap = (IPersistentMap) RT.get(pvar.get(), methodMapKey);2870 Keyword mmapVal = (Keyword) mmap.valAt(Keyword.intern(fvar.sym));2871 if (mmapVal == null) {2872 throw new IllegalArgumentException(2873 "No method of interface: " + protocolOn.getName() +2874 " found for function: " + fvar.sym + " of protocol: " + pvar.sym +2875 " (The protocol method may have been defined before and removed.)");2876 }2877 String mname = munge(mmapVal.sym.toString());2878 List methods = Reflector.getMethods(protocolOn, args.count() - 1, mname, false);2879 if(methods.size() != 1)2880 throw new IllegalArgumentException(2881 "No single method: " + mname + " of interface: " + protocolOn.getName() +2882 " found for function: " + fvar.sym + " of protocol: " + pvar.sym);2883 this.onMethod = (java.lang.reflect.Method) methods.get(0);2884 }2885 }2886 // else if(pvar == null && VAR_CALLSITES.isBound()2887 // && fvar.ns.name.name.startsWith("clojure")2888 // && !RT.booleanCast(RT.get(RT.meta(fvar),dynamicKey))2889 // )2890 // {2891 // //todo - more specific criteria for binding these2892 // this.isDirect = true;2893 // this.siteIndex = registerVarCallsite(((VarExpr) fexpr).var);2894 // }2895 }2896 this.tag = tag != null ? tag : (fexpr instanceof VarExpr ? ((VarExpr) fexpr).tag : null);2897 }2899 public Object eval() throws Exception{2900 try2901 {2902 IFn fn = (IFn) fexpr.eval();2903 PersistentVector argvs = PersistentVector.EMPTY;2904 for(int i = 0; i < args.count(); i++)2905 argvs = argvs.cons(((Expr) args.nth(i)).eval());2906 return fn.applyTo(RT.seq(argvs));2907 }2908 catch(Throwable e)2909 {2910 if(!(e instanceof CompilerException))2911 throw new CompilerException(source, line, e);2912 else2913 throw (CompilerException) e;2914 }2915 }2917 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){2918 gen.visitLineNumber(line, gen.mark());2919 if(isProtocol)2920 {2921 emitProto(context,objx,gen);2922 }2923 else if(isDirect)2924 {2925 Label callLabel = gen.newLabel();2927 gen.getStatic(objx.objtype, objx.varCallsiteName(siteIndex), IFN_TYPE);2928 gen.dup();2929 gen.ifNonNull(callLabel);2931 gen.pop();2932 fexpr.emit(C.EXPRESSION, objx, gen);2933 gen.checkCast(IFN_TYPE);2934 // gen.dup();2935 // gen.putStatic(objx.objtype, objx.varCallsiteName(siteIndex), IFN_TYPE);2937 gen.mark(callLabel);2938 emitArgsAndCall(0, context,objx,gen);2939 }2940 else2941 {2942 fexpr.emit(C.EXPRESSION, objx, gen);2943 gen.checkCast(IFN_TYPE);2944 emitArgsAndCall(0, context,objx,gen);2945 }2946 if(context == C.STATEMENT)2947 gen.pop();2948 }2950 public void emitProto(C context, ObjExpr objx, GeneratorAdapter gen){2951 Label onLabel = gen.newLabel();2952 Label callLabel = gen.newLabel();2953 Label endLabel = gen.newLabel();2955 Var v = ((VarExpr)fexpr).var;2957 Expr e = (Expr) args.nth(0);2958 e.emit(C.EXPRESSION, objx, gen);2959 gen.dup(); //target, target2960 gen.invokeStatic(UTIL_TYPE,Method.getMethod("Class classOf(Object)")); //target,class2961 gen.loadThis();2962 gen.getField(objx.objtype, objx.cachedClassName(siteIndex),CLASS_TYPE); //target,class,cached-class2963 gen.visitJumpInsn(IF_ACMPEQ, callLabel); //target2964 if(protocolOn != null)2965 {2966 gen.dup(); //target, target2967 gen.instanceOf(Type.getType(protocolOn));2968 gen.ifZCmp(GeneratorAdapter.NE, onLabel);2969 }2971 gen.mark(callLabel); //target2972 gen.dup(); //target, target2973 gen.invokeStatic(UTIL_TYPE,Method.getMethod("Class classOf(Object)")); //target,class2974 gen.loadThis();2975 gen.swap();2976 gen.putField(objx.objtype, objx.cachedClassName(siteIndex),CLASS_TYPE); //target2977 objx.emitVar(gen, v);2978 gen.invokeVirtual(VAR_TYPE, Method.getMethod("Object getRawRoot()")); //target, proto-fn2979 gen.swap();2980 emitArgsAndCall(1, context,objx,gen);2981 gen.goTo(endLabel);2983 gen.mark(onLabel); //target2984 if(protocolOn != null)2985 {2986 MethodExpr.emitTypedArgs(objx, gen, onMethod.getParameterTypes(), RT.subvec(args,1,args.count()));2987 if(context == C.RETURN)2988 {2989 ObjMethod method = (ObjMethod) METHOD.deref();2990 method.emitClearLocals(gen);2991 }2992 Method m = new Method(onMethod.getName(), Type.getReturnType(onMethod), Type.getArgumentTypes(onMethod));2993 gen.invokeInterface(Type.getType(protocolOn), m);2994 HostExpr.emitBoxReturn(objx, gen, onMethod.getReturnType());2995 }2996 gen.mark(endLabel);2997 }2999 void emitArgsAndCall(int firstArgToEmit, C context, ObjExpr objx, GeneratorAdapter gen){3000 for(int i = firstArgToEmit; i < Math.min(MAX_POSITIONAL_ARITY, args.count()); i++)3001 {3002 Expr e = (Expr) args.nth(i);3003 e.emit(C.EXPRESSION, objx, gen);3004 }3005 if(args.count() > MAX_POSITIONAL_ARITY)3006 {3007 PersistentVector restArgs = PersistentVector.EMPTY;3008 for(int i = MAX_POSITIONAL_ARITY; i < args.count(); i++)3009 {3010 restArgs = restArgs.cons(args.nth(i));3011 }3012 MethodExpr.emitArgsAsArray(restArgs, objx, gen);3013 }3015 if(context == C.RETURN)3016 {3017 ObjMethod method = (ObjMethod) METHOD.deref();3018 method.emitClearLocals(gen);3019 }3021 gen.invokeInterface(IFN_TYPE, new Method("invoke", OBJECT_TYPE, ARG_TYPES[Math.min(MAX_POSITIONAL_ARITY + 1,3022 args.count())]));3023 }3025 public boolean hasJavaClass() throws Exception{3026 return tag != null;3027 }3029 public Class getJavaClass() throws Exception{3030 return HostExpr.tagToClass(tag);3031 }3033 static public Expr parse(C context, ISeq form) throws Exception{3034 if(context != C.EVAL)3035 context = C.EXPRESSION;3036 Expr fexpr = analyze(context, form.first());3037 if(fexpr instanceof VarExpr && ((VarExpr)fexpr).var.equals(INSTANCE))3038 {3039 if(RT.second(form) instanceof Symbol)3040 {3041 Class c = HostExpr.maybeClass(RT.second(form),false);3042 if(c != null)3043 return new InstanceOfExpr(c, analyze(context, RT.third(form)));3044 }3045 }3047 if(fexpr instanceof KeywordExpr && RT.count(form) == 2 && KEYWORD_CALLSITES.isBound())3048 {3049 // fexpr = new ConstantExpr(new KeywordCallSite(((KeywordExpr)fexpr).k));3050 Expr target = analyze(context, RT.second(form));3051 return new KeywordInvokeExpr((String) SOURCE.deref(), (Integer) LINE.deref(), tagOf(form),3052 (KeywordExpr) fexpr, target);3053 }3054 PersistentVector args = PersistentVector.EMPTY;3055 for(ISeq s = RT.seq(form.next()); s != null; s = s.next())3056 {3057 args = args.cons(analyze(context, s.first()));3058 }3059 // if(args.count() > MAX_POSITIONAL_ARITY)3060 // throw new IllegalArgumentException(3061 // String.format("No more than %d args supported", MAX_POSITIONAL_ARITY));3063 return new InvokeExpr((String) SOURCE.deref(), (Integer) LINE.deref(), tagOf(form), fexpr, args);3064 }3065 }3067 static class SourceDebugExtensionAttribute extends Attribute{3068 public SourceDebugExtensionAttribute(){3069 super("SourceDebugExtension");3070 }3072 void writeSMAP(ClassWriter cw, String smap){3073 ByteVector bv = write(cw, null, -1, -1, -1);3074 bv.putUTF8(smap);3075 }3076 }3078 static public class FnExpr extends ObjExpr{3079 final static Type aFnType = Type.getType(AFunction.class);3080 final static Type restFnType = Type.getType(RestFn.class);3081 //if there is a variadic overload (there can only be one) it is stored here3082 FnMethod variadicMethod = null;3083 IPersistentCollection methods;3084 // String superName = null;3086 public FnExpr(Object tag){3087 super(tag);3088 }3090 public boolean hasJavaClass() throws Exception{3091 return true;3092 }3094 public Class getJavaClass() throws Exception{3095 return AFunction.class;3096 }3098 protected void emitMethods(ClassVisitor cv){3099 //override of invoke/doInvoke for each method3100 for(ISeq s = RT.seq(methods); s != null; s = s.next())3101 {3102 ObjMethod method = (ObjMethod) s.first();3103 method.emit(this, cv);3104 }3106 if(isVariadic())3107 {3108 GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC,3109 Method.getMethod("int getRequiredArity()"),3110 null,3111 null,3112 cv);3113 gen.visitCode();3114 gen.push(variadicMethod.reqParms.count());3115 gen.returnValue();3116 gen.endMethod();3117 }3118 }3120 static Expr parse(C context, ISeq form, String name) throws Exception{3121 ISeq origForm = form;3122 FnExpr fn = new FnExpr(tagOf(form));3123 fn.src = form;3124 ObjMethod enclosingMethod = (ObjMethod) METHOD.deref();3125 if(((IMeta) form.first()).meta() != null)3126 {3127 fn.onceOnly = RT.booleanCast(RT.get(RT.meta(form.first()), Keyword.intern(null, "once")));3128 // fn.superName = (String) RT.get(RT.meta(form.first()), Keyword.intern(null, "super-name"));3129 }3130 //fn.thisName = name;3131 String basename = enclosingMethod != null ?3132 (enclosingMethod.objx.name + "$")3133 : //"clojure.fns." +3134 (munge(currentNS().name.name) + "$");3135 if(RT.second(form) instanceof Symbol)3136 name = ((Symbol) RT.second(form)).name;3137 String simpleName = name != null ?3138 (munge(name).replace(".", "_DOT_")3139 + (enclosingMethod != null ? "__" + RT.nextID() : ""))3140 : ("fn"3141 + "__" + RT.nextID());3142 fn.name = basename + simpleName;3143 fn.internalName = fn.name.replace('.', '/');3144 fn.objtype = Type.getObjectType(fn.internalName);3145 try3146 {3147 Var.pushThreadBindings(3148 RT.map(CONSTANTS, PersistentVector.EMPTY,3149 CONSTANT_IDS, new IdentityHashMap(),3150 KEYWORDS, PersistentHashMap.EMPTY,3151 VARS, PersistentHashMap.EMPTY,3152 KEYWORD_CALLSITES, PersistentVector.EMPTY,3153 PROTOCOL_CALLSITES, PersistentVector.EMPTY,3154 VAR_CALLSITES, PersistentVector.EMPTY3155 ));3157 //arglist might be preceded by symbol naming this fn3158 if(RT.second(form) instanceof Symbol)3159 {3160 fn.thisName = ((Symbol) RT.second(form)).name;3161 form = RT.cons(FN, RT.next(RT.next(form)));3162 }3164 //now (fn [args] body...) or (fn ([args] body...) ([args2] body2...) ...)3165 //turn former into latter3166 if(RT.second(form) instanceof IPersistentVector)3167 form = RT.list(FN, RT.next(form));3168 fn.line = (Integer) LINE.deref();3169 FnMethod[] methodArray = new FnMethod[MAX_POSITIONAL_ARITY + 1];3170 FnMethod variadicMethod = null;3171 for(ISeq s = RT.next(form); s != null; s = RT.next(s))3172 {3173 FnMethod f = FnMethod.parse(fn, (ISeq) RT.first(s));3174 if(f.isVariadic())3175 {3176 if(variadicMethod == null)3177 variadicMethod = f;3178 else3179 throw new Exception("Can't have more than 1 variadic overload");3180 }3181 else if(methodArray[f.reqParms.count()] == null)3182 methodArray[f.reqParms.count()] = f;3183 else3184 throw new Exception("Can't have 2 overloads with same arity");3185 }3186 if(variadicMethod != null)3187 {3188 for(int i = variadicMethod.reqParms.count() + 1; i <= MAX_POSITIONAL_ARITY; i++)3189 if(methodArray[i] != null)3190 throw new Exception(3191 "Can't have fixed arity function with more params than variadic function");3192 }3194 IPersistentCollection methods = null;3195 for(int i = 0; i < methodArray.length; i++)3196 if(methodArray[i] != null)3197 methods = RT.conj(methods, methodArray[i]);3198 if(variadicMethod != null)3199 methods = RT.conj(methods, variadicMethod);3201 fn.methods = methods;3202 fn.variadicMethod = variadicMethod;3203 fn.keywords = (IPersistentMap) KEYWORDS.deref();3204 fn.vars = (IPersistentMap) VARS.deref();3205 fn.constants = (PersistentVector) CONSTANTS.deref();3206 fn.keywordCallsites = (IPersistentVector) KEYWORD_CALLSITES.deref();3207 fn.protocolCallsites = (IPersistentVector) PROTOCOL_CALLSITES.deref();3208 fn.varCallsites = (IPersistentVector) VAR_CALLSITES.deref();3210 fn.constantsID = RT.nextID();3211 // DynamicClassLoader loader = (DynamicClassLoader) LOADER.get();3212 // loader.registerConstants(fn.constantsID, fn.constants.toArray());3213 }3214 finally3215 {3216 Var.popThreadBindings();3217 }3218 fn.compile(fn.isVariadic() ? "clojure/lang/RestFn" : "clojure/lang/AFunction",null,fn.onceOnly);3219 fn.getCompiledClass();3221 if(origForm instanceof IObj && ((IObj) origForm).meta() != null)3222 return new MetaExpr(fn, (MapExpr) MapExpr3223 .parse(context == C.EVAL ? context : C.EXPRESSION, ((IObj) origForm).meta()));3224 else3225 return fn;3226 }3228 public final ObjMethod variadicMethod(){3229 return variadicMethod;3230 }3232 boolean isVariadic(){3233 return variadicMethod != null;3234 }3236 public final IPersistentCollection methods(){3237 return methods;3238 }3239 }3241 static public class ObjExpr implements Expr{3242 static final String CONST_PREFIX = "const__";3243 String name;3244 //String simpleName;3245 String internalName;3246 String thisName;3247 Type objtype;3248 public final Object tag;3249 //localbinding->itself3250 IPersistentMap closes = PersistentHashMap.EMPTY;3251 //localbndingexprs3252 IPersistentVector closesExprs = PersistentVector.EMPTY;3253 //symbols3254 IPersistentSet volatiles = PersistentHashSet.EMPTY;3256 //symbol->lb3257 IPersistentMap fields = null;3259 //Keyword->KeywordExpr3260 IPersistentMap keywords = PersistentHashMap.EMPTY;3261 IPersistentMap vars = PersistentHashMap.EMPTY;3262 Class compiledClass;3263 int line;3264 PersistentVector constants;3265 int constantsID;3266 int altCtorDrops = 0;3268 IPersistentVector keywordCallsites;3269 IPersistentVector protocolCallsites;3270 IPersistentVector varCallsites;3271 boolean onceOnly = false;3273 Object src;3275 final static Method voidctor = Method.getMethod("void <init>()");3276 protected IPersistentMap classMeta;3278 public final String name(){3279 return name;3280 }3282 // public final String simpleName(){3283 // return simpleName;3284 // }3286 public final String internalName(){3287 return internalName;3288 }3290 public final String thisName(){3291 return thisName;3292 }3294 public final Type objtype(){3295 return objtype;3296 }3298 public final IPersistentMap closes(){3299 return closes;3300 }3302 public final IPersistentMap keywords(){3303 return keywords;3304 }3306 public final IPersistentMap vars(){3307 return vars;3308 }3310 public final Class compiledClass(){3311 return compiledClass;3312 }3314 public final int line(){3315 return line;3316 }3318 public final PersistentVector constants(){3319 return constants;3320 }3322 public final int constantsID(){3323 return constantsID;3324 }3326 final static Method kwintern = Method.getMethod("clojure.lang.Keyword intern(String, String)");3327 final static Method symcreate = Method.getMethod("clojure.lang.Symbol create(String)");3328 final static Method varintern =3329 Method.getMethod("clojure.lang.Var intern(clojure.lang.Symbol, clojure.lang.Symbol)");3331 final static Type DYNAMIC_CLASSLOADER_TYPE = Type.getType(DynamicClassLoader.class);3332 final static Method getClassMethod = Method.getMethod("Class getClass()");3333 final static Method getClassLoaderMethod = Method.getMethod("ClassLoader getClassLoader()");3334 final static Method getConstantsMethod = Method.getMethod("Object[] getConstants(int)");3335 final static Method readStringMethod = Method.getMethod("Object readString(String)");3337 final static Type ILOOKUP_SITE_TYPE = Type.getType(ILookupSite.class);3338 final static Type ILOOKUP_THUNK_TYPE = Type.getType(ILookupThunk.class);3339 final static Type KEYWORD_LOOKUPSITE_TYPE = Type.getType(KeywordLookupSite.class);3341 private DynamicClassLoader loader;3342 private byte[] bytecode;3344 public ObjExpr(Object tag){3345 this.tag = tag;3346 }3348 static String trimGenID(String name){3349 int i = name.lastIndexOf("__");3350 return i==-1?name:name.substring(0,i);3351 }3355 Type[] ctorTypes(){3356 IPersistentVector tv = isDeftype()?PersistentVector.EMPTY:RT.vector(IPERSISTENTMAP_TYPE);3357 for(ISeq s = RT.keys(closes); s != null; s = s.next())3358 {3359 LocalBinding lb = (LocalBinding) s.first();3360 if(lb.getPrimitiveType() != null)3361 tv = tv.cons(Type.getType(lb.getPrimitiveType()));3362 else3363 tv = tv.cons(OBJECT_TYPE);3364 }3365 Type[] ret = new Type[tv.count()];3366 for(int i = 0; i < tv.count(); i++)3367 ret[i] = (Type) tv.nth(i);3368 return ret;3369 }3371 void compile(String superName, String[] interfaceNames, boolean oneTimeUse) throws Exception{3372 //create bytecode for a class3373 //with name current_ns.defname[$letname]+3374 //anonymous fns get names fn__id3375 //derived from AFn/RestFn3376 if(keywordCallsites.count() > 0)3377 {3378 if(interfaceNames == null)3379 interfaceNames = new String[]{"clojure/lang/ILookupHost"};3380 else3381 {3382 String[] inames = new String[interfaceNames.length + 1];3383 System.arraycopy(interfaceNames,0,inames,0,interfaceNames.length);3384 inames[interfaceNames.length] = "clojure/lang/ILookupHost";3385 interfaceNames = inames;3386 }3387 }3388 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);3389 // ClassWriter cw = new ClassWriter(0);3390 ClassVisitor cv = cw;3391 // ClassVisitor cv = new TraceClassVisitor(new CheckClassAdapter(cw), new PrintWriter(System.out));3392 //ClassVisitor cv = new TraceClassVisitor(cw, new PrintWriter(System.out));3393 cv.visit(V1_5, ACC_PUBLIC + ACC_SUPER + ACC_FINAL, internalName, null,superName,interfaceNames);3394 // superName != null ? superName :3395 // (isVariadic() ? "clojure/lang/RestFn" : "clojure/lang/AFunction"), null);3396 String source = (String) SOURCE.deref();3397 int lineBefore = (Integer) LINE_BEFORE.deref();3398 int lineAfter = (Integer) LINE_AFTER.deref() + 1;3400 if(source != null && SOURCE_PATH.deref() != null)3401 {3402 //cv.visitSource(source, null);3403 String smap = "SMAP\n" +3404 ((source.lastIndexOf('.') > 0) ?3405 source.substring(0, source.lastIndexOf('.'))3406 :source)3407 // : simpleName)3408 + ".java\n" +3409 "Clojure\n" +3410 "*S Clojure\n" +3411 "*F\n" +3412 "+ 1 " + source + "\n" +3413 (String) SOURCE_PATH.deref() + "\n" +3414 "*L\n" +3415 String.format("%d#1,%d:%d\n", lineBefore, lineAfter - lineBefore, lineBefore) +3416 "*E";3417 cv.visitSource(source, smap);3418 }3419 addAnnotation(cv, classMeta);3420 //static fields for constants3421 for(int i = 0; i < constants.count(); i++)3422 {3423 cv.visitField(ACC_PUBLIC + ACC_FINAL3424 + ACC_STATIC, constantName(i), constantType(i).getDescriptor(),3425 null, null);3426 }3428 //static fields for lookup sites3429 for(int i = 0; i < keywordCallsites.count(); i++)3430 {3431 cv.visitField(ACC_FINAL3432 + ACC_STATIC, siteNameStatic(i), KEYWORD_LOOKUPSITE_TYPE.getDescriptor(),3433 null, null);3434 cv.visitField(ACC_STATIC, thunkNameStatic(i), ILOOKUP_THUNK_TYPE.getDescriptor(),3435 null, null);3436 }3438 for(int i=0;i<varCallsites.count();i++)3439 {3440 cv.visitField(ACC_PRIVATE + ACC_STATIC + ACC_FINAL3441 , varCallsiteName(i), IFN_TYPE.getDescriptor(), null, null);3442 }3444 //static init for constants, keywords and vars3445 GeneratorAdapter clinitgen = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC,3446 Method.getMethod("void <clinit> ()"),3447 null,3448 null,3449 cv);3450 clinitgen.visitCode();3451 clinitgen.visitLineNumber(line, clinitgen.mark());3453 if(constants.count() > 0)3454 {3455 emitConstants(clinitgen);3456 }3458 if(keywordCallsites.count() > 0)3459 emitKeywordCallsites(clinitgen);3461 for(int i=0;i<varCallsites.count();i++)3462 {3463 Label skipLabel = clinitgen.newLabel();3464 Label endLabel = clinitgen.newLabel();3465 Var var = (Var) varCallsites.nth(i);3466 clinitgen.push(var.ns.name.toString());3467 clinitgen.push(var.sym.toString());3468 clinitgen.invokeStatic(RT_TYPE, Method.getMethod("clojure.lang.Var var(String,String)"));3469 clinitgen.dup();3470 clinitgen.invokeVirtual(VAR_TYPE,Method.getMethod("boolean hasRoot()"));3471 clinitgen.ifZCmp(GeneratorAdapter.EQ,skipLabel);3473 clinitgen.invokeVirtual(VAR_TYPE,Method.getMethod("Object getRoot()"));3474 clinitgen.dup();3475 clinitgen.instanceOf(AFUNCTION_TYPE);3476 clinitgen.ifZCmp(GeneratorAdapter.EQ,skipLabel);3477 clinitgen.checkCast(IFN_TYPE);3478 clinitgen.putStatic(objtype, varCallsiteName(i), IFN_TYPE);3479 clinitgen.goTo(endLabel);3481 clinitgen.mark(skipLabel);3482 clinitgen.pop();3484 clinitgen.mark(endLabel);3485 }3487 clinitgen.returnValue();3489 clinitgen.endMethod();3490 if(!isDeftype())3491 {3492 cv.visitField(ACC_FINAL, "__meta", IPERSISTENTMAP_TYPE.getDescriptor(), null, null);3493 }3494 //instance fields for closed-overs3495 for(ISeq s = RT.keys(closes); s != null; s = s.next())3496 {3497 LocalBinding lb = (LocalBinding) s.first();3498 if(isDeftype())3499 {3500 int access = isVolatile(lb) ? ACC_VOLATILE :3501 isMutable(lb) ? 0 :3502 (ACC_PUBLIC + ACC_FINAL);3503 FieldVisitor fv;3504 if(lb.getPrimitiveType() != null)3505 fv = cv.visitField(access3506 , lb.name, Type.getType(lb.getPrimitiveType()).getDescriptor(),3507 null, null);3508 else3509 //todo - when closed-overs are fields, use more specific types here and in ctor and emitLocal?3510 fv = cv.visitField(access3511 , lb.name, OBJECT_TYPE.getDescriptor(), null, null);3512 addAnnotation(fv, RT.meta(lb.sym));3513 }3514 else3515 {3516 //todo - only enable this non-private+writability for letfns where we need it3517 if(lb.getPrimitiveType() != null)3518 cv.visitField(0 + (isVolatile(lb) ? ACC_VOLATILE : 0)3519 , lb.name, Type.getType(lb.getPrimitiveType()).getDescriptor(),3520 null, null);3521 else3522 cv.visitField(0 //+ (oneTimeUse ? 0 : ACC_FINAL)3523 , lb.name, OBJECT_TYPE.getDescriptor(), null, null);3524 }3525 }3527 //instance fields for callsites and thunks3528 for(int i=0;i<protocolCallsites.count();i++)3529 {3530 cv.visitField(ACC_PRIVATE, cachedClassName(i), CLASS_TYPE.getDescriptor(), null, null);3531 cv.visitField(ACC_PRIVATE, cachedProtoFnName(i), AFUNCTION_TYPE.getDescriptor(), null, null);3532 cv.visitField(ACC_PRIVATE, cachedProtoImplName(i), IFN_TYPE.getDescriptor(), null, null);3533 }3535 //ctor that takes closed-overs and inits base + fields3536 Method m = new Method("<init>", Type.VOID_TYPE, ctorTypes());3537 GeneratorAdapter ctorgen = new GeneratorAdapter(ACC_PUBLIC,3538 m,3539 null,3540 null,3541 cv);3542 Label start = ctorgen.newLabel();3543 Label end = ctorgen.newLabel();3544 ctorgen.visitCode();3545 ctorgen.visitLineNumber(line, ctorgen.mark());3546 ctorgen.visitLabel(start);3547 ctorgen.loadThis();3548 // if(superName != null)3549 ctorgen.invokeConstructor(Type.getObjectType(superName), voidctor);3550 // else if(isVariadic()) //RestFn ctor takes reqArity arg3551 // {3552 // ctorgen.push(variadicMethod.reqParms.count());3553 // ctorgen.invokeConstructor(restFnType, restfnctor);3554 // }3555 // else3556 // ctorgen.invokeConstructor(aFnType, voidctor);3557 if(!isDeftype())3558 {3559 ctorgen.loadThis();3560 ctorgen.visitVarInsn(IPERSISTENTMAP_TYPE.getOpcode(Opcodes.ILOAD), 1);3561 ctorgen.putField(objtype, "__meta", IPERSISTENTMAP_TYPE);3562 }3564 int a = isDeftype()?1:2;3565 for(ISeq s = RT.keys(closes); s != null; s = s.next(), ++a)3566 {3567 LocalBinding lb = (LocalBinding) s.first();3568 ctorgen.loadThis();3569 Class primc = lb.getPrimitiveType();3570 if(primc != null)3571 {3572 ctorgen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ILOAD), a);3573 ctorgen.putField(objtype, lb.name, Type.getType(primc));3574 if(primc == Long.TYPE || primc == Double.TYPE)3575 ++a;3576 }3577 else3578 {3579 ctorgen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), a);3580 ctorgen.putField(objtype, lb.name, OBJECT_TYPE);3581 }3582 closesExprs = closesExprs.cons(new LocalBindingExpr(lb, null));3583 }3586 ctorgen.visitLabel(end);3588 ctorgen.returnValue();3590 ctorgen.endMethod();3592 if(altCtorDrops > 0)3593 {3594 //ctor that takes closed-overs and inits base + fields3595 Type[] ctorTypes = ctorTypes();3596 Type[] altCtorTypes = new Type[ctorTypes.length-altCtorDrops];3597 for(int i=0;i<altCtorTypes.length;i++)3598 altCtorTypes[i] = ctorTypes[i];3599 Method alt = new Method("<init>", Type.VOID_TYPE, altCtorTypes);3600 ctorgen = new GeneratorAdapter(ACC_PUBLIC,3601 alt,3602 null,3603 null,3604 cv);3605 ctorgen.visitCode();3606 ctorgen.loadThis();3607 ctorgen.loadArgs();3608 for(int i=0;i<altCtorDrops;i++)3609 ctorgen.visitInsn(Opcodes.ACONST_NULL);3611 ctorgen.invokeConstructor(objtype, new Method("<init>", Type.VOID_TYPE, ctorTypes));3613 ctorgen.returnValue();3614 ctorgen.endMethod();3615 }3617 if(!isDeftype())3618 {3619 //ctor that takes closed-overs but not meta3620 Type[] ctorTypes = ctorTypes();3621 Type[] noMetaCtorTypes = new Type[ctorTypes.length-1];3622 for(int i=1;i<ctorTypes.length;i++)3623 noMetaCtorTypes[i-1] = ctorTypes[i];3624 Method alt = new Method("<init>", Type.VOID_TYPE, noMetaCtorTypes);3625 ctorgen = new GeneratorAdapter(ACC_PUBLIC,3626 alt,3627 null,3628 null,3629 cv);3630 ctorgen.visitCode();3631 ctorgen.loadThis();3632 ctorgen.visitInsn(Opcodes.ACONST_NULL); //null meta3633 ctorgen.loadArgs();3634 ctorgen.invokeConstructor(objtype, new Method("<init>", Type.VOID_TYPE, ctorTypes));3636 ctorgen.returnValue();3637 ctorgen.endMethod();3639 //meta()3640 Method meth = Method.getMethod("clojure.lang.IPersistentMap meta()");3642 GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC,3643 meth,3644 null,3645 null,3646 cv);3647 gen.visitCode();3648 gen.loadThis();3649 gen.getField(objtype,"__meta",IPERSISTENTMAP_TYPE);3651 gen.returnValue();3652 gen.endMethod();3654 //withMeta()3655 meth = Method.getMethod("clojure.lang.IObj withMeta(clojure.lang.IPersistentMap)");3657 gen = new GeneratorAdapter(ACC_PUBLIC,3658 meth,3659 null,3660 null,3661 cv);3662 gen.visitCode();3663 gen.newInstance(objtype);3664 gen.dup();3665 gen.loadArg(0);3667 for(ISeq s = RT.keys(closes); s != null; s = s.next(), ++a)3668 {3669 LocalBinding lb = (LocalBinding) s.first();3670 gen.loadThis();3671 Class primc = lb.getPrimitiveType();3672 if(primc != null)3673 {3674 gen.getField(objtype, lb.name, Type.getType(primc));3675 }3676 else3677 {3678 gen.getField(objtype, lb.name, OBJECT_TYPE);3679 }3680 }3682 gen.invokeConstructor(objtype, new Method("<init>", Type.VOID_TYPE, ctorTypes));3683 gen.returnValue();3684 gen.endMethod();3685 }3687 emitMethods(cv);3689 if(keywordCallsites.count() > 0)3690 {3691 Method meth = Method.getMethod("void swapThunk(int,clojure.lang.ILookupThunk)");3693 GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC,3694 meth,3695 null,3696 null,3697 cv);3698 gen.visitCode();3699 Label endLabel = gen.newLabel();3701 Label[] labels = new Label[keywordCallsites.count()];3702 for(int i = 0; i < keywordCallsites.count();i++)3703 {3704 labels[i] = gen.newLabel();3705 }3706 gen.loadArg(0);3707 gen.visitTableSwitchInsn(0,keywordCallsites.count()-1,endLabel,labels);3709 for(int i = 0; i < keywordCallsites.count();i++)3710 {3711 gen.mark(labels[i]);3712 // gen.loadThis();3713 gen.loadArg(1);3714 gen.putStatic(objtype, thunkNameStatic(i),ILOOKUP_THUNK_TYPE);3715 gen.goTo(endLabel);3716 }3718 gen.mark(endLabel);3720 gen.returnValue();3721 gen.endMethod();3722 }3724 //end of class3725 cv.visitEnd();3727 bytecode = cw.toByteArray();3728 if(RT.booleanCast(COMPILE_FILES.deref()))3729 writeClassFile(internalName, bytecode);3730 // else3731 // getCompiledClass();3732 }3734 private void emitKeywordCallsites(GeneratorAdapter clinitgen){3735 for(int i=0;i<keywordCallsites.count();i++)3736 {3737 Keyword k = (Keyword) keywordCallsites.nth(i);3738 clinitgen.newInstance(KEYWORD_LOOKUPSITE_TYPE);3739 clinitgen.dup();3740 clinitgen.push(i);3741 emitValue(k,clinitgen);3742 clinitgen.invokeConstructor(KEYWORD_LOOKUPSITE_TYPE,3743 Method.getMethod("void <init>(int,clojure.lang.Keyword)"));3744 clinitgen.dup();3745 clinitgen.putStatic(objtype, siteNameStatic(i), KEYWORD_LOOKUPSITE_TYPE);3746 clinitgen.putStatic(objtype, thunkNameStatic(i), ILOOKUP_THUNK_TYPE);3747 }3748 }3750 protected void emitMethods(ClassVisitor gen){3751 }3753 void emitListAsObjectArray(Object value, GeneratorAdapter gen){3754 gen.push(((List) value).size());3755 gen.newArray(OBJECT_TYPE);3756 int i = 0;3757 for(Iterator it = ((List) value).iterator(); it.hasNext(); i++)3758 {3759 gen.dup();3760 gen.push(i);3761 emitValue(it.next(), gen);3762 gen.arrayStore(OBJECT_TYPE);3763 }3764 }3766 void emitValue(Object value, GeneratorAdapter gen){3767 boolean partial = true;3768 //System.out.println(value.getClass().toString());3770 if(value instanceof String)3771 {3772 gen.push((String) value);3773 }3774 else if(value instanceof Integer)3775 {3776 gen.push(((Integer) value).intValue());3777 gen.invokeStatic(Type.getType(Integer.class), Method.getMethod("Integer valueOf(int)"));3778 }3779 else if(value instanceof Double)3780 {3781 gen.push(((Double) value).doubleValue());3782 gen.invokeStatic(Type.getType(Double.class), Method.getMethod("Double valueOf(double)"));3783 }3784 else if(value instanceof Character)3785 {3786 gen.push(((Character) value).charValue());3787 gen.invokeStatic(Type.getType(Character.class), Method.getMethod("Character valueOf(char)"));3788 }3789 else if(value instanceof Class)3790 {3791 Class cc = (Class)value;3792 if(cc.isPrimitive())3793 {3794 Type bt;3795 if ( cc == boolean.class ) bt = Type.getType(Boolean.class);3796 else if ( cc == byte.class ) bt = Type.getType(Byte.class);3797 else if ( cc == char.class ) bt = Type.getType(Character.class);3798 else if ( cc == double.class ) bt = Type.getType(Double.class);3799 else if ( cc == float.class ) bt = Type.getType(Float.class);3800 else if ( cc == int.class ) bt = Type.getType(Integer.class);3801 else if ( cc == long.class ) bt = Type.getType(Long.class);3802 else if ( cc == short.class ) bt = Type.getType(Short.class);3803 else throw new RuntimeException(3804 "Can't embed unknown primitive in code: " + value);3805 gen.getStatic( bt, "TYPE", Type.getType(Class.class) );3806 }3807 else3808 {3809 gen.push(destubClassName(cc.getName()));3810 gen.invokeStatic(Type.getType(Class.class), Method.getMethod("Class forName(String)"));3811 }3812 }3813 else if(value instanceof Symbol)3814 {3815 gen.push(((Symbol) value).ns);3816 gen.push(((Symbol) value).name);3817 gen.invokeStatic(Type.getType(Symbol.class),3818 Method.getMethod("clojure.lang.Symbol create(String,String)"));3819 }3820 else if(value instanceof Keyword)3821 {3822 emitValue(((Keyword) value).sym, gen);3823 gen.invokeStatic(Type.getType(Keyword.class),3824 Method.getMethod("clojure.lang.Keyword intern(clojure.lang.Symbol)"));3825 }3826 // else if(value instanceof KeywordCallSite)3827 // {3828 // emitValue(((KeywordCallSite) value).k.sym, gen);3829 // gen.invokeStatic(Type.getType(KeywordCallSite.class),3830 // Method.getMethod("clojure.lang.KeywordCallSite create(clojure.lang.Symbol)"));3831 // }3832 else if(value instanceof Var)3833 {3834 Var var = (Var) value;3835 gen.push(var.ns.name.toString());3836 gen.push(var.sym.toString());3837 gen.invokeStatic(RT_TYPE, Method.getMethod("clojure.lang.Var var(String,String)"));3838 }3839 else if(value instanceof IPersistentMap)3840 {3841 List entries = new ArrayList();3842 for(Map.Entry entry : (Set<Map.Entry>) ((Map) value).entrySet())3843 {3844 entries.add(entry.getKey());3845 entries.add(entry.getValue());3846 }3847 emitListAsObjectArray(entries, gen);3848 gen.invokeStatic(RT_TYPE,3849 Method.getMethod("clojure.lang.IPersistentMap map(Object[])"));3850 }3851 else if(value instanceof IPersistentVector)3852 {3853 emitListAsObjectArray(value, gen);3854 gen.invokeStatic(RT_TYPE, Method.getMethod(3855 "clojure.lang.IPersistentVector vector(Object[])"));3856 }3857 else if(value instanceof ISeq || value instanceof IPersistentList)3858 {3859 emitListAsObjectArray(value, gen);3860 gen.invokeStatic(Type.getType(java.util.Arrays.class),3861 Method.getMethod("java.util.List asList(Object[])"));3862 gen.invokeStatic(Type.getType(PersistentList.class),3863 Method.getMethod(3864 "clojure.lang.IPersistentList create(java.util.List)"));3865 }3866 else3867 {3868 String cs = null;3869 try3870 {3871 cs = RT.printString(value);3872 //System.out.println("WARNING SLOW CODE: " + value.getClass() + " -> " + cs);3873 }3874 catch(Exception e)3875 {3876 throw new RuntimeException(3877 "Can't embed object in code, maybe print-dup not defined: " +3878 value);3879 }3880 if(cs.length() == 0)3881 throw new RuntimeException(3882 "Can't embed unreadable object in code: " + value);3884 if(cs.startsWith("#<"))3885 throw new RuntimeException(3886 "Can't embed unreadable object in code: " + cs);3888 gen.push(cs);3889 gen.invokeStatic(RT_TYPE, readStringMethod);3890 partial = false;3891 }3893 if(partial)3894 {3895 if(value instanceof IObj && RT.count(((IObj) value).meta()) > 0)3896 {3897 gen.checkCast(IOBJ_TYPE);3898 emitValue(((IObj) value).meta(), gen);3899 gen.checkCast(IPERSISTENTMAP_TYPE);3900 gen.invokeInterface(IOBJ_TYPE,3901 Method.getMethod("clojure.lang.IObj withMeta(clojure.lang.IPersistentMap)"));3902 }3903 }3904 }3907 void emitConstants(GeneratorAdapter clinitgen){3908 try3909 {3910 Var.pushThreadBindings(RT.map(RT.PRINT_DUP, RT.T));3912 for(int i = 0; i < constants.count(); i++)3913 {3914 emitValue(constants.nth(i), clinitgen);3915 clinitgen.checkCast(constantType(i));3916 clinitgen.putStatic(objtype, constantName(i), constantType(i));3917 }3918 }3919 finally3920 {3921 Var.popThreadBindings();3922 }3923 }3925 boolean isMutable(LocalBinding lb){3926 return isVolatile(lb) ||3927 RT.booleanCast(RT.contains(fields, lb.sym)) &&3928 RT.booleanCast(RT.get(lb.sym.meta(), Keyword.intern("unsynchronized-mutable")));3929 }3931 boolean isVolatile(LocalBinding lb){3932 return RT.booleanCast(RT.contains(fields, lb.sym)) &&3933 RT.booleanCast(RT.get(lb.sym.meta(), Keyword.intern("volatile-mutable")));3934 }3936 boolean isDeftype(){3937 return fields != null;3938 }3940 void emitClearCloses(GeneratorAdapter gen){3941 // int a = 1;3942 // for(ISeq s = RT.keys(closes); s != null; s = s.next(), ++a)3943 // {3944 // LocalBinding lb = (LocalBinding) s.first();3945 // Class primc = lb.getPrimitiveType();3946 // if(primc == null)3947 // {3948 // gen.loadThis();3949 // gen.visitInsn(Opcodes.ACONST_NULL);3950 // gen.putField(objtype, lb.name, OBJECT_TYPE);3951 // }3952 // }3953 }3955 synchronized Class getCompiledClass(){3956 if(compiledClass == null)3957 try3958 {3959 // if(RT.booleanCast(COMPILE_FILES.deref()))3960 // compiledClass = RT.classForName(name);//loader.defineClass(name, bytecode);3961 // else3962 {3963 loader = (DynamicClassLoader) LOADER.deref();3964 compiledClass = loader.defineClass(name, bytecode, src);3965 }3966 }3967 catch(Exception e)3968 {3969 throw new RuntimeException(e);3970 }3971 return compiledClass;3972 }3974 public Object eval() throws Exception{3975 if(isDeftype())3976 return null;3977 return getCompiledClass().newInstance();3978 }3980 public void emitLetFnInits(GeneratorAdapter gen, ObjExpr objx, IPersistentSet letFnLocals){3981 //objx arg is enclosing objx, not this3982 gen.checkCast(objtype);3984 for(ISeq s = RT.keys(closes); s != null; s = s.next())3985 {3986 LocalBinding lb = (LocalBinding) s.first();3987 if(letFnLocals.contains(lb))3988 {3989 Class primc = lb.getPrimitiveType();3990 gen.dup();3991 if(primc != null)3992 {3993 objx.emitUnboxedLocal(gen, lb);3994 gen.putField(objtype, lb.name, Type.getType(primc));3995 }3996 else3997 {3998 objx.emitLocal(gen, lb, false);3999 gen.putField(objtype, lb.name, OBJECT_TYPE);4000 }4001 }4002 }4003 gen.pop();4005 }4007 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){4008 //emitting a Fn means constructing an instance, feeding closed-overs from enclosing scope, if any4009 //objx arg is enclosing objx, not this4010 // getCompiledClass();4011 if(isDeftype())4012 {4013 gen.visitInsn(Opcodes.ACONST_NULL);4014 }4015 else4016 {4017 gen.newInstance(objtype);4018 gen.dup();4019 gen.visitInsn(Opcodes.ACONST_NULL);4020 for(ISeq s = RT.seq(closesExprs); s != null; s = s.next())4021 {4022 LocalBindingExpr lbe = (LocalBindingExpr) s.first();4023 LocalBinding lb = lbe.b;4024 if(lb.getPrimitiveType() != null)4025 objx.emitUnboxedLocal(gen, lb);4026 else4027 objx.emitLocal(gen, lb, lbe.shouldClear);4028 }4029 gen.invokeConstructor(objtype, new Method("<init>", Type.VOID_TYPE, ctorTypes()));4030 }4031 if(context == C.STATEMENT)4032 gen.pop();4033 }4035 public boolean hasJavaClass() throws Exception{4036 return true;4037 }4039 public Class getJavaClass() throws Exception{4040 return (compiledClass != null) ? compiledClass4041 : (tag != null) ? HostExpr.tagToClass(tag)4042 : IFn.class;4043 }4045 public void emitAssignLocal(GeneratorAdapter gen, LocalBinding lb,Expr val){4046 if(!isMutable(lb))4047 throw new IllegalArgumentException("Cannot assign to non-mutable: " + lb.name);4048 Class primc = lb.getPrimitiveType();4049 gen.loadThis();4050 if(primc != null)4051 {4052 if(!(val instanceof MaybePrimitiveExpr && ((MaybePrimitiveExpr) val).canEmitPrimitive()))4053 throw new IllegalArgumentException("Must assign primitive to primitive mutable: " + lb.name);4054 MaybePrimitiveExpr me = (MaybePrimitiveExpr) val;4055 me.emitUnboxed(C.EXPRESSION, this, gen);4056 gen.putField(objtype, lb.name, Type.getType(primc));4057 }4058 else4059 {4060 val.emit(C.EXPRESSION, this, gen);4061 gen.putField(objtype, lb.name, OBJECT_TYPE);4062 }4063 }4065 private void emitLocal(GeneratorAdapter gen, LocalBinding lb, boolean clear){4066 if(closes.containsKey(lb))4067 {4068 Class primc = lb.getPrimitiveType();4069 gen.loadThis();4070 if(primc != null)4071 {4072 gen.getField(objtype, lb.name, Type.getType(primc));4073 HostExpr.emitBoxReturn(this, gen, primc);4074 }4075 else4076 {4077 gen.getField(objtype, lb.name, OBJECT_TYPE);4078 if(onceOnly && clear && lb.canBeCleared)4079 {4080 gen.loadThis();4081 gen.visitInsn(Opcodes.ACONST_NULL);4082 gen.putField(objtype, lb.name, OBJECT_TYPE);4083 }4084 }4085 }4086 else4087 {4088 Class primc = lb.getPrimitiveType();4089 // String rep = lb.sym.name + " " + lb.toString().substring(lb.toString().lastIndexOf('@'));4090 if(lb.isArg)4091 {4092 gen.loadArg(lb.idx-1);4093 if(primc != null)4094 HostExpr.emitBoxReturn(this, gen, primc);4095 else4096 {4097 if(clear && lb.canBeCleared)4098 {4099 // System.out.println("clear: " + rep);4100 gen.visitInsn(Opcodes.ACONST_NULL);4101 gen.storeArg(lb.idx - 1);4102 }4103 else4104 {4105 // System.out.println("use: " + rep);4106 }4107 }4108 }4109 else4110 {4111 if(primc != null)4112 {4113 gen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ILOAD), lb.idx);4114 HostExpr.emitBoxReturn(this, gen, primc);4115 }4116 else4117 {4118 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), lb.idx);4119 if(clear && lb.canBeCleared)4120 {4121 // System.out.println("clear: " + rep);4122 gen.visitInsn(Opcodes.ACONST_NULL);4123 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), lb.idx);4124 }4125 else4126 {4127 // System.out.println("use: " + rep);4128 }4129 }4130 }4131 }4132 }4134 private void emitUnboxedLocal(GeneratorAdapter gen, LocalBinding lb){4135 Class primc = lb.getPrimitiveType();4136 if(closes.containsKey(lb))4137 {4138 gen.loadThis();4139 gen.getField(objtype, lb.name, Type.getType(primc));4140 }4141 else if(lb.isArg)4142 gen.loadArg(lb.idx-1);4143 else4144 gen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ILOAD), lb.idx);4145 }4147 public void emitVar(GeneratorAdapter gen, Var var){4148 Integer i = (Integer) vars.valAt(var);4149 emitConstant(gen, i);4150 //gen.getStatic(fntype, munge(var.sym.toString()), VAR_TYPE);4151 }4153 public void emitKeyword(GeneratorAdapter gen, Keyword k){4154 Integer i = (Integer) keywords.valAt(k);4155 emitConstant(gen, i);4156 // gen.getStatic(fntype, munge(k.sym.toString()), KEYWORD_TYPE);4157 }4159 public void emitConstant(GeneratorAdapter gen, int id){4160 gen.getStatic(objtype, constantName(id), constantType(id));4161 }4164 String constantName(int id){4165 return CONST_PREFIX + id;4166 }4168 String siteName(int n){4169 return "__site__" + n;4170 }4172 String siteNameStatic(int n){4173 return siteName(n) + "__";4174 }4176 String thunkName(int n){4177 return "__thunk__" + n;4178 }4180 String cachedClassName(int n){4181 return "__cached_class__" + n;4182 }4184 String cachedProtoFnName(int n){4185 return "__cached_proto_fn__" + n;4186 }4188 String cachedProtoImplName(int n){4189 return "__cached_proto_impl__" + n;4190 }4192 String varCallsiteName(int n){4193 return "__var__callsite__" + n;4194 }4196 String thunkNameStatic(int n){4197 return thunkName(n) + "__";4198 }4200 Type constantType(int id){4201 Object o = constants.nth(id);4202 Class c = o.getClass();4203 if(Modifier.isPublic(c.getModifiers()))4204 {4205 //can't emit derived fn types due to visibility4206 if(LazySeq.class.isAssignableFrom(c))4207 return Type.getType(ISeq.class);4208 else if(c == Keyword.class)4209 return Type.getType(Keyword.class);4210 // else if(c == KeywordCallSite.class)4211 // return Type.getType(KeywordCallSite.class);4212 else if(RestFn.class.isAssignableFrom(c))4213 return Type.getType(RestFn.class);4214 else if(AFn.class.isAssignableFrom(c))4215 return Type.getType(AFn.class);4216 else if(c == Var.class)4217 return Type.getType(Var.class);4218 else if(c == String.class)4219 return Type.getType(String.class);4221 // return Type.getType(c);4222 }4223 return OBJECT_TYPE;4224 }4226 }4228 enum PATHTYPE {4229 PATH, BRANCH;4230 }4232 static class PathNode{4233 final PATHTYPE type;4234 final PathNode parent;4236 PathNode(PATHTYPE type, PathNode parent) {4237 this.type = type;4238 this.parent = parent;4239 }4240 }4242 static PathNode clearPathRoot(){4243 return (PathNode) CLEAR_ROOT.get();4244 }4246 enum PSTATE{4247 REQ, REST, DONE4248 }4250 public static class FnMethod extends ObjMethod{4251 //localbinding->localbinding4252 PersistentVector reqParms = PersistentVector.EMPTY;4253 LocalBinding restParm = null;4255 public FnMethod(ObjExpr objx, ObjMethod parent){4256 super(objx, parent);4257 }4259 static FnMethod parse(ObjExpr objx, ISeq form) throws Exception{4260 //([args] body...)4261 IPersistentVector parms = (IPersistentVector) RT.first(form);4262 ISeq body = RT.next(form);4263 try4264 {4265 FnMethod method = new FnMethod(objx, (ObjMethod) METHOD.deref());4266 method.line = (Integer) LINE.deref();4267 //register as the current method and set up a new env frame4268 PathNode pnode = (PathNode) CLEAR_PATH.get();4269 if(pnode == null)4270 pnode = new PathNode(PATHTYPE.PATH,null);4271 Var.pushThreadBindings(4272 RT.map(4273 METHOD, method,4274 LOCAL_ENV, LOCAL_ENV.deref(),4275 LOOP_LOCALS, null,4276 NEXT_LOCAL_NUM, 04277 ,CLEAR_PATH, pnode4278 ,CLEAR_ROOT, pnode4279 ,CLEAR_SITES, PersistentHashMap.EMPTY4280 ));4282 //register 'this' as local 04283 //registerLocal(THISFN, null, null);4284 if(objx.thisName != null)4285 registerLocal(Symbol.intern(objx.thisName), null, null,false);4286 else4287 getAndIncLocalNum();4288 PSTATE state = PSTATE.REQ;4289 PersistentVector argLocals = PersistentVector.EMPTY;4290 for(int i = 0; i < parms.count(); i++)4291 {4292 if(!(parms.nth(i) instanceof Symbol))4293 throw new IllegalArgumentException("fn params must be Symbols");4294 Symbol p = (Symbol) parms.nth(i);4295 if(p.getNamespace() != null)4296 throw new Exception("Can't use qualified name as parameter: " + p);4297 if(p.equals(_AMP_))4298 {4299 if(state == PSTATE.REQ)4300 state = PSTATE.REST;4301 else4302 throw new Exception("Invalid parameter list");4303 }4305 else4306 {4307 LocalBinding lb = registerLocal(p, state == PSTATE.REST ? ISEQ : tagOf(p), null,true);4308 argLocals = argLocals.cons(lb);4309 switch(state)4310 {4311 case REQ:4312 method.reqParms = method.reqParms.cons(lb);4313 break;4314 case REST:4315 method.restParm = lb;4316 state = PSTATE.DONE;4317 break;4319 default:4320 throw new Exception("Unexpected parameter");4321 }4322 }4323 }4324 if(method.reqParms.count() > MAX_POSITIONAL_ARITY)4325 throw new Exception("Can't specify more than " + MAX_POSITIONAL_ARITY + " params");4326 LOOP_LOCALS.set(argLocals);4327 method.argLocals = argLocals;4328 method.body = (new BodyExpr.Parser()).parse(C.RETURN, body);4329 return method;4330 }4331 finally4332 {4333 Var.popThreadBindings();4334 }4335 }4337 public final PersistentVector reqParms(){4338 return reqParms;4339 }4341 public final LocalBinding restParm(){4342 return restParm;4343 }4345 boolean isVariadic(){4346 return restParm != null;4347 }4349 int numParams(){4350 return reqParms.count() + (isVariadic() ? 1 : 0);4351 }4353 String getMethodName(){4354 return isVariadic()?"doInvoke":"invoke";4355 }4357 Type getReturnType(){4358 return OBJECT_TYPE;4359 }4361 Type[] getArgTypes(){4362 if(isVariadic() && reqParms.count() == MAX_POSITIONAL_ARITY)4363 {4364 Type[] ret = new Type[MAX_POSITIONAL_ARITY + 1];4365 for(int i = 0;i<MAX_POSITIONAL_ARITY + 1;i++)4366 ret[i] = OBJECT_TYPE;4367 return ret;4368 }4369 return ARG_TYPES[numParams()];4370 }4372 void emitClearLocals(GeneratorAdapter gen){4373 // for(int i = 1; i < numParams() + 1; i++)4374 // {4375 // if(!localsUsedInCatchFinally.contains(i))4376 // {4377 // gen.visitInsn(Opcodes.ACONST_NULL);4378 // gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), i);4379 // }4380 // }4381 // for(int i = numParams() + 1; i < maxLocal + 1; i++)4382 // {4383 // if(!localsUsedInCatchFinally.contains(i))4384 // {4385 // LocalBinding b = (LocalBinding) RT.get(indexlocals, i);4386 // if(b == null || maybePrimitiveType(b.init) == null)4387 // {4388 // gen.visitInsn(Opcodes.ACONST_NULL);4389 // gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), i);4390 // }4391 // }4392 // }4393 // if(((FnExpr)objx).onceOnly)4394 // {4395 // objx.emitClearCloses(gen);4396 // }4397 }4398 }4400 abstract public static class ObjMethod{4401 //when closures are defined inside other closures,4402 //the closed over locals need to be propagated to the enclosing objx4403 public final ObjMethod parent;4404 //localbinding->localbinding4405 IPersistentMap locals = null;4406 //num->localbinding4407 IPersistentMap indexlocals = null;4408 Expr body = null;4409 ObjExpr objx;4410 PersistentVector argLocals;4411 int maxLocal = 0;4412 int line;4413 PersistentHashSet localsUsedInCatchFinally = PersistentHashSet.EMPTY;4414 protected IPersistentMap methodMeta;4416 public final IPersistentMap locals(){4417 return locals;4418 }4420 public final Expr body(){4421 return body;4422 }4424 public final ObjExpr objx(){4425 return objx;4426 }4428 public final PersistentVector argLocals(){4429 return argLocals;4430 }4432 public final int maxLocal(){4433 return maxLocal;4434 }4436 public final int line(){4437 return line;4438 }4440 public ObjMethod(ObjExpr objx, ObjMethod parent){4441 this.parent = parent;4442 this.objx = objx;4443 }4445 abstract int numParams();4446 abstract String getMethodName();4447 abstract Type getReturnType();4448 abstract Type[] getArgTypes();4450 public void emit(ObjExpr fn, ClassVisitor cv){4451 Method m = new Method(getMethodName(), getReturnType(), getArgTypes());4453 GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC,4454 m,4455 null,4456 //todo don't hardwire this4457 EXCEPTION_TYPES,4458 cv);4459 gen.visitCode();4460 Label loopLabel = gen.mark();4461 gen.visitLineNumber(line, loopLabel);4462 try4463 {4464 Var.pushThreadBindings(RT.map(LOOP_LABEL, loopLabel, METHOD, this));4465 body.emit(C.RETURN, fn, gen);4466 Label end = gen.mark();4467 gen.visitLocalVariable("this", "Ljava/lang/Object;", null, loopLabel, end, 0);4468 for(ISeq lbs = argLocals.seq(); lbs != null; lbs = lbs.next())4469 {4470 LocalBinding lb = (LocalBinding) lbs.first();4471 gen.visitLocalVariable(lb.name, "Ljava/lang/Object;", null, loopLabel, end, lb.idx);4472 }4473 }4474 finally4475 {4476 Var.popThreadBindings();4477 }4479 gen.returnValue();4480 //gen.visitMaxs(1, 1);4481 gen.endMethod();4482 }4484 void emitClearLocals(GeneratorAdapter gen){4485 }4487 void emitClearLocalsOld(GeneratorAdapter gen){4488 for(int i=0;i<argLocals.count();i++)4489 {4490 LocalBinding lb = (LocalBinding) argLocals.nth(i);4491 if(!localsUsedInCatchFinally.contains(lb.idx) && lb.getPrimitiveType() == null)4492 {4493 gen.visitInsn(Opcodes.ACONST_NULL);4494 gen.storeArg(lb.idx - 1);4495 }4497 }4498 // for(int i = 1; i < numParams() + 1; i++)4499 // {4500 // if(!localsUsedInCatchFinally.contains(i))4501 // {4502 // gen.visitInsn(Opcodes.ACONST_NULL);4503 // gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), i);4504 // }4505 // }4506 for(int i = numParams() + 1; i < maxLocal + 1; i++)4507 {4508 if(!localsUsedInCatchFinally.contains(i))4509 {4510 LocalBinding b = (LocalBinding) RT.get(indexlocals, i);4511 if(b == null || maybePrimitiveType(b.init) == null)4512 {4513 gen.visitInsn(Opcodes.ACONST_NULL);4514 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), i);4515 }4516 }4517 }4518 }4519 }4521 public static class LocalBinding{4522 public final Symbol sym;4523 public final Symbol tag;4524 public Expr init;4525 public final int idx;4526 public final String name;4527 public final boolean isArg;4528 public final PathNode clearPathRoot;4529 public boolean canBeCleared = true;4531 public LocalBinding(int num, Symbol sym, Symbol tag, Expr init, boolean isArg,PathNode clearPathRoot)4532 throws Exception{4533 if(maybePrimitiveType(init) != null && tag != null)4534 throw new UnsupportedOperationException("Can't type hint a local with a primitive initializer");4535 this.idx = num;4536 this.sym = sym;4537 this.tag = tag;4538 this.init = init;4539 this.isArg = isArg;4540 this.clearPathRoot = clearPathRoot;4541 name = munge(sym.name);4542 }4544 public boolean hasJavaClass() throws Exception{4545 if(init != null && init.hasJavaClass()4546 && Util.isPrimitive(init.getJavaClass())4547 && !(init instanceof MaybePrimitiveExpr))4548 return false;4549 return tag != null4550 || (init != null && init.hasJavaClass());4551 }4553 public Class getJavaClass() throws Exception{4554 return tag != null ? HostExpr.tagToClass(tag)4555 : init.getJavaClass();4556 }4558 public Class getPrimitiveType(){4559 return maybePrimitiveType(init);4560 }4561 }4563 public static class LocalBindingExpr implements Expr, MaybePrimitiveExpr, AssignableExpr{4564 public final LocalBinding b;4565 public final Symbol tag;4567 public final PathNode clearPath;4568 public final PathNode clearRoot;4569 public boolean shouldClear = false;4572 public LocalBindingExpr(LocalBinding b, Symbol tag)4573 throws Exception{4574 if(b.getPrimitiveType() != null && tag != null)4575 throw new UnsupportedOperationException("Can't type hint a primitive local");4576 this.b = b;4577 this.tag = tag;4579 this.clearPath = (PathNode)CLEAR_PATH.get();4580 this.clearRoot = (PathNode)CLEAR_ROOT.get();4581 IPersistentCollection sites = (IPersistentCollection) RT.get(CLEAR_SITES.get(),b);4583 if(b.idx > 0)4584 {4585 // Object dummy;4587 if(sites != null)4588 {4589 for(ISeq s = sites.seq();s!=null;s = s.next())4590 {4591 LocalBindingExpr o = (LocalBindingExpr) s.first();4592 PathNode common = commonPath(clearPath,o.clearPath);4593 if(common != null && common.type == PATHTYPE.PATH)4594 o.shouldClear = false;4595 // else4596 // dummy = null;4597 }4598 }4600 if(clearRoot == b.clearPathRoot)4601 {4602 this.shouldClear = true;4603 sites = RT.conj(sites,this);4604 CLEAR_SITES.set(RT.assoc(CLEAR_SITES.get(), b, sites));4605 }4606 // else4607 // dummy = null;4608 }4609 }4611 public Object eval() throws Exception{4612 throw new UnsupportedOperationException("Can't eval locals");4613 }4615 public boolean canEmitPrimitive(){4616 return b.getPrimitiveType() != null;4617 }4619 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){4620 objx.emitUnboxedLocal(gen, b);4621 }4623 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){4624 if(context != C.STATEMENT)4625 objx.emitLocal(gen, b, shouldClear);4626 }4628 public Object evalAssign(Expr val) throws Exception{4629 throw new UnsupportedOperationException("Can't eval locals");4630 }4632 public void emitAssign(C context, ObjExpr objx, GeneratorAdapter gen, Expr val){4633 objx.emitAssignLocal(gen, b,val);4634 if(context != C.STATEMENT)4635 objx.emitLocal(gen, b, false);4636 }4638 public boolean hasJavaClass() throws Exception{4639 return tag != null || b.hasJavaClass();4640 }4642 public Class getJavaClass() throws Exception{4643 if(tag != null)4644 return HostExpr.tagToClass(tag);4645 return b.getJavaClass();4646 }4649 }4651 public static class BodyExpr implements Expr, MaybePrimitiveExpr{4652 PersistentVector exprs;4654 public final PersistentVector exprs(){4655 return exprs;4656 }4658 public BodyExpr(PersistentVector exprs){4659 this.exprs = exprs;4660 }4662 static class Parser implements IParser{4663 public Expr parse(C context, Object frms) throws Exception{4664 ISeq forms = (ISeq) frms;4665 if(Util.equals(RT.first(forms), DO))4666 forms = RT.next(forms);4667 PersistentVector exprs = PersistentVector.EMPTY;4668 for(; forms != null; forms = forms.next())4669 {4670 Expr e = (context != C.EVAL &&4671 (context == C.STATEMENT || forms.next() != null)) ?4672 analyze(C.STATEMENT, forms.first())4673 :4674 analyze(context, forms.first());4675 exprs = exprs.cons(e);4676 }4677 if(exprs.count() == 0)4678 exprs = exprs.cons(NIL_EXPR);4679 return new BodyExpr(exprs);4680 }4681 }4683 public Object eval() throws Exception{4684 Object ret = null;4685 for(Object o : exprs)4686 {4687 Expr e = (Expr) o;4688 ret = e.eval();4689 }4690 return ret;4691 }4693 public boolean canEmitPrimitive(){4694 return lastExpr() instanceof MaybePrimitiveExpr && ((MaybePrimitiveExpr)lastExpr()).canEmitPrimitive();4695 }4697 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){4698 for(int i = 0; i < exprs.count() - 1; i++)4699 {4700 Expr e = (Expr) exprs.nth(i);4701 e.emit(C.STATEMENT, objx, gen);4702 }4703 MaybePrimitiveExpr last = (MaybePrimitiveExpr) exprs.nth(exprs.count() - 1);4704 last.emitUnboxed(context, objx, gen);4705 }4707 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){4708 for(int i = 0; i < exprs.count() - 1; i++)4709 {4710 Expr e = (Expr) exprs.nth(i);4711 e.emit(C.STATEMENT, objx, gen);4712 }4713 Expr last = (Expr) exprs.nth(exprs.count() - 1);4714 last.emit(context, objx, gen);4715 }4717 public boolean hasJavaClass() throws Exception{4718 return lastExpr().hasJavaClass();4719 }4721 public Class getJavaClass() throws Exception{4722 return lastExpr().getJavaClass();4723 }4725 private Expr lastExpr(){4726 return (Expr) exprs.nth(exprs.count() - 1);4727 }4728 }4730 public static class BindingInit{4731 LocalBinding binding;4732 Expr init;4734 public final LocalBinding binding(){4735 return binding;4736 }4738 public final Expr init(){4739 return init;4740 }4742 public BindingInit(LocalBinding binding, Expr init){4743 this.binding = binding;4744 this.init = init;4745 }4746 }4748 public static class LetFnExpr implements Expr{4749 public final PersistentVector bindingInits;4750 public final Expr body;4752 public LetFnExpr(PersistentVector bindingInits, Expr body){4753 this.bindingInits = bindingInits;4754 this.body = body;4755 }4757 static class Parser implements IParser{4758 public Expr parse(C context, Object frm) throws Exception{4759 ISeq form = (ISeq) frm;4760 //(letfns* [var (fn [args] body) ...] body...)4761 if(!(RT.second(form) instanceof IPersistentVector))4762 throw new IllegalArgumentException("Bad binding form, expected vector");4764 IPersistentVector bindings = (IPersistentVector) RT.second(form);4765 if((bindings.count() % 2) != 0)4766 throw new IllegalArgumentException("Bad binding form, expected matched symbol expression pairs");4768 ISeq body = RT.next(RT.next(form));4770 if(context == C.EVAL)4771 return analyze(context, RT.list(RT.list(FN, PersistentVector.EMPTY, form)));4773 IPersistentMap dynamicBindings = RT.map(LOCAL_ENV, LOCAL_ENV.deref(),4774 NEXT_LOCAL_NUM, NEXT_LOCAL_NUM.deref());4776 try4777 {4778 Var.pushThreadBindings(dynamicBindings);4780 //pre-seed env (like Lisp labels)4781 PersistentVector lbs = PersistentVector.EMPTY;4782 for(int i = 0; i < bindings.count(); i += 2)4783 {4784 if(!(bindings.nth(i) instanceof Symbol))4785 throw new IllegalArgumentException(4786 "Bad binding form, expected symbol, got: " + bindings.nth(i));4787 Symbol sym = (Symbol) bindings.nth(i);4788 if(sym.getNamespace() != null)4789 throw new Exception("Can't let qualified name: " + sym);4790 LocalBinding lb = registerLocal(sym, tagOf(sym), null,false);4791 lb.canBeCleared = false;4792 lbs = lbs.cons(lb);4793 }4794 PersistentVector bindingInits = PersistentVector.EMPTY;4795 for(int i = 0; i < bindings.count(); i += 2)4796 {4797 Symbol sym = (Symbol) bindings.nth(i);4798 Expr init = analyze(C.EXPRESSION, bindings.nth(i + 1), sym.name);4799 LocalBinding lb = (LocalBinding) lbs.nth(i / 2);4800 lb.init = init;4801 BindingInit bi = new BindingInit(lb, init);4802 bindingInits = bindingInits.cons(bi);4803 }4804 return new LetFnExpr(bindingInits, (new BodyExpr.Parser()).parse(context, body));4805 }4806 finally4807 {4808 Var.popThreadBindings();4809 }4810 }4811 }4813 public Object eval() throws Exception{4814 throw new UnsupportedOperationException("Can't eval letfns");4815 }4817 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){4818 for(int i = 0; i < bindingInits.count(); i++)4819 {4820 BindingInit bi = (BindingInit) bindingInits.nth(i);4821 gen.visitInsn(Opcodes.ACONST_NULL);4822 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), bi.binding.idx);4823 }4825 IPersistentSet lbset = PersistentHashSet.EMPTY;4827 for(int i = 0; i < bindingInits.count(); i++)4828 {4829 BindingInit bi = (BindingInit) bindingInits.nth(i);4830 lbset = (IPersistentSet) lbset.cons(bi.binding);4831 bi.init.emit(C.EXPRESSION, objx, gen);4832 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), bi.binding.idx);4833 }4835 for(int i = 0; i < bindingInits.count(); i++)4836 {4837 BindingInit bi = (BindingInit) bindingInits.nth(i);4838 ObjExpr fe = (ObjExpr) bi.init;4839 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), bi.binding.idx);4840 fe.emitLetFnInits(gen, objx, lbset);4841 }4843 Label loopLabel = gen.mark();4845 body.emit(context, objx, gen);4847 Label end = gen.mark();4848 // gen.visitLocalVariable("this", "Ljava/lang/Object;", null, loopLabel, end, 0);4849 for(ISeq bis = bindingInits.seq(); bis != null; bis = bis.next())4850 {4851 BindingInit bi = (BindingInit) bis.first();4852 String lname = bi.binding.name;4853 if(lname.endsWith("__auto__"))4854 lname += RT.nextID();4855 Class primc = maybePrimitiveType(bi.init);4856 if(primc != null)4857 gen.visitLocalVariable(lname, Type.getDescriptor(primc), null, loopLabel, end,4858 bi.binding.idx);4859 else4860 gen.visitLocalVariable(lname, "Ljava/lang/Object;", null, loopLabel, end, bi.binding.idx);4861 }4862 }4864 public boolean hasJavaClass() throws Exception{4865 return body.hasJavaClass();4866 }4868 public Class getJavaClass() throws Exception{4869 return body.getJavaClass();4870 }4871 }4873 public static class LetExpr implements Expr, MaybePrimitiveExpr{4874 public final PersistentVector bindingInits;4875 public final Expr body;4876 public final boolean isLoop;4878 public LetExpr(PersistentVector bindingInits, Expr body, boolean isLoop){4879 this.bindingInits = bindingInits;4880 this.body = body;4881 this.isLoop = isLoop;4882 }4884 static class Parser implements IParser{4885 public Expr parse(C context, Object frm) throws Exception{4886 ISeq form = (ISeq) frm;4887 //(let [var val var2 val2 ...] body...)4888 boolean isLoop = RT.first(form).equals(LOOP);4889 if(!(RT.second(form) instanceof IPersistentVector))4890 throw new IllegalArgumentException("Bad binding form, expected vector");4892 IPersistentVector bindings = (IPersistentVector) RT.second(form);4893 if((bindings.count() % 2) != 0)4894 throw new IllegalArgumentException("Bad binding form, expected matched symbol expression pairs");4896 ISeq body = RT.next(RT.next(form));4898 if(context == C.EVAL4899 || (context == C.EXPRESSION && isLoop))4900 return analyze(context, RT.list(RT.list(FN, PersistentVector.EMPTY, form)));4902 IPersistentMap dynamicBindings = RT.map(LOCAL_ENV, LOCAL_ENV.deref(),4903 NEXT_LOCAL_NUM, NEXT_LOCAL_NUM.deref());4904 if(isLoop)4905 dynamicBindings = dynamicBindings.assoc(LOOP_LOCALS, null);4907 try4908 {4909 Var.pushThreadBindings(dynamicBindings);4911 PersistentVector bindingInits = PersistentVector.EMPTY;4912 PersistentVector loopLocals = PersistentVector.EMPTY;4913 for(int i = 0; i < bindings.count(); i += 2)4914 {4915 if(!(bindings.nth(i) instanceof Symbol))4916 throw new IllegalArgumentException(4917 "Bad binding form, expected symbol, got: " + bindings.nth(i));4918 Symbol sym = (Symbol) bindings.nth(i);4919 if(sym.getNamespace() != null)4920 throw new Exception("Can't let qualified name: " + sym);4921 Expr init = analyze(C.EXPRESSION, bindings.nth(i + 1), sym.name);4922 //sequential enhancement of env (like Lisp let*)4923 LocalBinding lb = registerLocal(sym, tagOf(sym), init,false);4924 BindingInit bi = new BindingInit(lb, init);4925 bindingInits = bindingInits.cons(bi);4927 if(isLoop)4928 loopLocals = loopLocals.cons(lb);4929 }4930 if(isLoop)4931 LOOP_LOCALS.set(loopLocals);4932 Expr bodyExpr;4933 try {4934 if(isLoop)4935 {4936 PathNode root = new PathNode(PATHTYPE.PATH, (PathNode) CLEAR_PATH.get());4937 Var.pushThreadBindings(4938 RT.map(CLEAR_PATH, new PathNode(PATHTYPE.PATH,root),4939 CLEAR_ROOT, new PathNode(PATHTYPE.PATH,root)));4940 }4941 bodyExpr = (new BodyExpr.Parser()).parse(isLoop ? C.RETURN : context, body);4942 }4943 finally{4944 if(isLoop)4945 Var.popThreadBindings();4946 }4947 return new LetExpr(bindingInits, bodyExpr,4948 isLoop);4949 }4950 finally4951 {4952 Var.popThreadBindings();4953 }4954 }4955 }4957 public Object eval() throws Exception{4958 throw new UnsupportedOperationException("Can't eval let/loop");4959 }4961 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){4962 doEmit(context, objx, gen, false);4963 }4965 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){4966 doEmit(context, objx, gen, true);4967 }4970 public void doEmit(C context, ObjExpr objx, GeneratorAdapter gen, boolean emitUnboxed){4971 for(int i = 0; i < bindingInits.count(); i++)4972 {4973 BindingInit bi = (BindingInit) bindingInits.nth(i);4974 Class primc = maybePrimitiveType(bi.init);4975 if(primc != null)4976 {4977 ((MaybePrimitiveExpr) bi.init).emitUnboxed(C.EXPRESSION, objx, gen);4978 gen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ISTORE), bi.binding.idx);4979 }4980 else4981 {4982 bi.init.emit(C.EXPRESSION, objx, gen);4983 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), bi.binding.idx);4984 }4985 }4986 Label loopLabel = gen.mark();4987 if(isLoop)4988 {4989 try4990 {4991 Var.pushThreadBindings(RT.map(LOOP_LABEL, loopLabel));4992 if(emitUnboxed)4993 ((MaybePrimitiveExpr)body).emitUnboxed(context, objx, gen);4994 else4995 body.emit(context, objx, gen);4996 }4997 finally4998 {4999 Var.popThreadBindings();5000 }5001 }5002 else5003 {5004 if(emitUnboxed)5005 ((MaybePrimitiveExpr)body).emitUnboxed(context, objx, gen);5006 else5007 body.emit(context, objx, gen);5008 }5009 Label end = gen.mark();5010 // gen.visitLocalVariable("this", "Ljava/lang/Object;", null, loopLabel, end, 0);5011 for(ISeq bis = bindingInits.seq(); bis != null; bis = bis.next())5012 {5013 BindingInit bi = (BindingInit) bis.first();5014 String lname = bi.binding.name;5015 if(lname.endsWith("__auto__"))5016 lname += RT.nextID();5017 Class primc = maybePrimitiveType(bi.init);5018 if(primc != null)5019 gen.visitLocalVariable(lname, Type.getDescriptor(primc), null, loopLabel, end,5020 bi.binding.idx);5021 else5022 gen.visitLocalVariable(lname, "Ljava/lang/Object;", null, loopLabel, end, bi.binding.idx);5023 }5024 }5026 public boolean hasJavaClass() throws Exception{5027 return body.hasJavaClass();5028 }5030 public Class getJavaClass() throws Exception{5031 return body.getJavaClass();5032 }5034 public boolean canEmitPrimitive(){5035 return body instanceof MaybePrimitiveExpr && ((MaybePrimitiveExpr)body).canEmitPrimitive();5036 }5038 }5040 public static class RecurExpr implements Expr{5041 public final IPersistentVector args;5042 public final IPersistentVector loopLocals;5044 public RecurExpr(IPersistentVector loopLocals, IPersistentVector args){5045 this.loopLocals = loopLocals;5046 this.args = args;5047 }5049 public Object eval() throws Exception{5050 throw new UnsupportedOperationException("Can't eval recur");5051 }5053 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){5054 Label loopLabel = (Label) LOOP_LABEL.deref();5055 if(loopLabel == null)5056 throw new IllegalStateException();5057 for(int i = 0; i < loopLocals.count(); i++)5058 {5059 LocalBinding lb = (LocalBinding) loopLocals.nth(i);5060 Expr arg = (Expr) args.nth(i);5061 if(lb.getPrimitiveType() != null)5062 {5063 Class primc = lb.getPrimitiveType();5064 try5065 {5066 if(!(arg instanceof MaybePrimitiveExpr && arg.hasJavaClass() && arg.getJavaClass() == primc))5067 throw new IllegalArgumentException("recur arg for primitive local: " +5068 lb.name + " must be matching primitive");5069 }5070 catch(Exception e)5071 {5072 throw new RuntimeException(e);5073 }5074 ((MaybePrimitiveExpr) arg).emitUnboxed(C.EXPRESSION, objx, gen);5075 }5076 else5077 {5078 arg.emit(C.EXPRESSION, objx, gen);5079 }5080 }5082 for(int i = loopLocals.count() - 1; i >= 0; i--)5083 {5084 LocalBinding lb = (LocalBinding) loopLocals.nth(i);5085 Class primc = lb.getPrimitiveType();5086 if(lb.isArg)5087 gen.storeArg(lb.idx-1);5088 else5089 {5090 if(primc != null)5091 gen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ISTORE), lb.idx);5092 else5093 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), lb.idx);5094 }5095 }5097 gen.goTo(loopLabel);5098 }5100 public boolean hasJavaClass() throws Exception{5101 return true;5102 }5104 public Class getJavaClass() throws Exception{5105 return null;5106 }5108 static class Parser implements IParser{5109 public Expr parse(C context, Object frm) throws Exception{5110 ISeq form = (ISeq) frm;5111 IPersistentVector loopLocals = (IPersistentVector) LOOP_LOCALS.deref();5112 if(context != C.RETURN || loopLocals == null)5113 throw new UnsupportedOperationException("Can only recur from tail position");5114 if(IN_CATCH_FINALLY.deref() != null)5115 throw new UnsupportedOperationException("Cannot recur from catch/finally");5116 PersistentVector args = PersistentVector.EMPTY;5117 for(ISeq s = RT.seq(form.next()); s != null; s = s.next())5118 {5119 args = args.cons(analyze(C.EXPRESSION, s.first()));5120 }5121 if(args.count() != loopLocals.count())5122 throw new IllegalArgumentException(5123 String.format("Mismatched argument count to recur, expected: %d args, got: %d",5124 loopLocals.count(), args.count()));5125 return new RecurExpr(loopLocals, args);5126 }5127 }5128 }5130 private static LocalBinding registerLocal(Symbol sym, Symbol tag, Expr init, boolean isArg) throws Exception{5131 int num = getAndIncLocalNum();5132 LocalBinding b = new LocalBinding(num, sym, tag, init, isArg, clearPathRoot());5133 IPersistentMap localsMap = (IPersistentMap) LOCAL_ENV.deref();5134 LOCAL_ENV.set(RT.assoc(localsMap, b.sym, b));5135 ObjMethod method = (ObjMethod) METHOD.deref();5136 method.locals = (IPersistentMap) RT.assoc(method.locals, b, b);5137 method.indexlocals = (IPersistentMap) RT.assoc(method.indexlocals, num, b);5138 return b;5139 }5141 private static int getAndIncLocalNum(){5142 int num = ((Number) NEXT_LOCAL_NUM.deref()).intValue();5143 ObjMethod m = (ObjMethod) METHOD.deref();5144 if(num > m.maxLocal)5145 m.maxLocal = num;5146 NEXT_LOCAL_NUM.set(num + 1);5147 return num;5148 }5150 public static Expr analyze(C context, Object form) throws Exception{5151 return analyze(context, form, null);5152 }5154 private static Expr analyze(C context, Object form, String name) throws Exception{5155 //todo symbol macro expansion?5156 try5157 {5158 if(form instanceof LazySeq)5159 {5160 form = RT.seq(form);5161 if(form == null)5162 form = PersistentList.EMPTY;5163 }5164 if(form == null)5165 return NIL_EXPR;5166 else if(form == Boolean.TRUE)5167 return TRUE_EXPR;5168 else if(form == Boolean.FALSE)5169 return FALSE_EXPR;5170 Class fclass = form.getClass();5171 if(fclass == Symbol.class)5172 return analyzeSymbol((Symbol) form);5173 else if(fclass == Keyword.class)5174 return registerKeyword((Keyword) form);5175 // else if(form instanceof Num)5176 // return new NumExpr((Num) form);5177 else if(fclass == String.class)5178 return new StringExpr(((String) form).intern());5179 // else if(fclass == Character.class)5180 // return new CharExpr((Character) form);5181 else if(form instanceof IPersistentCollection && ((IPersistentCollection) form).count() == 0)5182 {5183 Expr ret = new EmptyExpr(form);5184 if(RT.meta(form) != null)5185 ret = new MetaExpr(ret, (MapExpr) MapExpr5186 .parse(context == C.EVAL ? context : C.EXPRESSION, ((IObj) form).meta()));5187 return ret;5188 }5189 else if(form instanceof ISeq)5190 return analyzeSeq(context, (ISeq) form, name);5191 else if(form instanceof IPersistentVector)5192 return VectorExpr.parse(context, (IPersistentVector) form);5193 else if(form instanceof IPersistentMap)5194 return MapExpr.parse(context, (IPersistentMap) form);5195 else if(form instanceof IPersistentSet)5196 return SetExpr.parse(context, (IPersistentSet) form);5198 // else5199 //throw new UnsupportedOperationException();5200 return new ConstantExpr(form);5201 }5202 catch(Throwable e)5203 {5204 if(!(e instanceof CompilerException))5205 throw new CompilerException((String) SOURCE.deref(), (Integer) LINE.deref(), e);5206 else5207 throw (CompilerException) e;5208 }5209 }5211 static public class CompilerException extends Exception{5213 public CompilerException(String source, int line, Throwable cause){5214 super(errorMsg(source, line, cause.toString()), cause);5215 }5217 public String toString(){5218 return getMessage();5219 }5220 }5222 static public Var isMacro(Object op) throws Exception{5223 //no local macros for now5224 if(op instanceof Symbol && referenceLocal((Symbol) op) != null)5225 return null;5226 if(op instanceof Symbol || op instanceof Var)5227 {5228 Var v = (op instanceof Var) ? (Var) op : lookupVar((Symbol) op, false);5229 if(v != null && v.isMacro())5230 {5231 if(v.ns != currentNS() && !v.isPublic())5232 throw new IllegalStateException("var: " + v + " is not public");5233 return v;5234 }5235 }5236 return null;5237 }5239 static public IFn isInline(Object op, int arity) throws Exception{5240 //no local inlines for now5241 if(op instanceof Symbol && referenceLocal((Symbol) op) != null)5242 return null;5243 if(op instanceof Symbol || op instanceof Var)5244 {5245 Var v = (op instanceof Var) ? (Var) op : lookupVar((Symbol) op, false);5246 if(v != null)5247 {5248 if(v.ns != currentNS() && !v.isPublic())5249 throw new IllegalStateException("var: " + v + " is not public");5250 IFn ret = (IFn) RT.get(v.meta(), inlineKey);5251 if(ret != null)5252 {5253 IFn arityPred = (IFn) RT.get(v.meta(), inlineAritiesKey);5254 if(arityPred == null || RT.booleanCast(arityPred.invoke(arity)))5255 return ret;5256 }5257 }5258 }5259 return null;5260 }5262 public static boolean namesStaticMember(Symbol sym){5263 return sym.ns != null && namespaceFor(sym) == null;5264 }5266 public static Object preserveTag(ISeq src, Object dst) {5267 Symbol tag = tagOf(src);5268 if (tag != null && dst instanceof IObj) {5269 IPersistentMap meta = RT.meta(dst);5270 return ((IObj) dst).withMeta((IPersistentMap) RT.assoc(meta, RT.TAG_KEY, tag));5271 }5272 return dst;5273 }5275 public static Object macroexpand1(Object x) throws Exception{5276 if(x instanceof ISeq)5277 {5278 ISeq form = (ISeq) x;5279 Object op = RT.first(form);5280 if(isSpecial(op))5281 return x;5282 //macro expansion5283 Var v = isMacro(op);5284 if(v != null)5285 {5286 return v.applyTo(RT.cons(form,RT.cons(LOCAL_ENV.get(),form.next())));5287 }5288 else5289 {5290 if(op instanceof Symbol)5291 {5292 Symbol sym = (Symbol) op;5293 String sname = sym.name;5294 //(.substring s 2 5) => (. s substring 2 5)5295 if(sym.name.charAt(0) == '.')5296 {5297 if(RT.length(form) < 2)5298 throw new IllegalArgumentException(5299 "Malformed member expression, expecting (.member target ...)");5300 Symbol meth = Symbol.intern(sname.substring(1));5301 Object target = RT.second(form);5302 if(HostExpr.maybeClass(target, false) != null)5303 {5304 target = ((IObj)RT.list(IDENTITY, target)).withMeta(RT.map(RT.TAG_KEY,CLASS));5305 }5306 return preserveTag(form, RT.listStar(DOT, target, meth, form.next().next()));5307 }5308 else if(namesStaticMember(sym))5309 {5310 Symbol target = Symbol.intern(sym.ns);5311 Class c = HostExpr.maybeClass(target, false);5312 if(c != null)5313 {5314 Symbol meth = Symbol.intern(sym.name);5315 return preserveTag(form, RT.listStar(DOT, target, meth, form.next()));5316 }5317 }5318 else5319 {5320 //(s.substring 2 5) => (. s substring 2 5)5321 //also (package.class.name ...) (. package.class name ...)5322 int idx = sname.lastIndexOf('.');5323 // if(idx > 0 && idx < sname.length() - 1)5324 // {5325 // Symbol target = Symbol.intern(sname.substring(0, idx));5326 // Symbol meth = Symbol.intern(sname.substring(idx + 1));5327 // return RT.listStar(DOT, target, meth, form.rest());5328 // }5329 //(StringBuilder. "foo") => (new StringBuilder "foo")5330 //else5331 if(idx == sname.length() - 1)5332 return RT.listStar(NEW, Symbol.intern(sname.substring(0, idx)), form.next());5333 }5334 }5335 }5336 }5337 return x;5338 }5340 static Object macroexpand(Object form) throws Exception{5341 Object exf = macroexpand1(form);5342 if(exf != form)5343 return macroexpand(exf);5344 return form;5345 }5347 private static Expr analyzeSeq(C context, ISeq form, String name) throws Exception{5348 Integer line = (Integer) LINE.deref();5349 if(RT.meta(form) != null && RT.meta(form).containsKey(RT.LINE_KEY))5350 line = (Integer) RT.meta(form).valAt(RT.LINE_KEY);5351 Var.pushThreadBindings(5352 RT.map(LINE, line));5353 try5354 {5355 Object me = macroexpand1(form);5356 if(me != form)5357 return analyze(context, me, name);5359 Object op = RT.first(form);5360 if(op == null)5361 throw new IllegalArgumentException("Can't call nil");5362 IFn inline = isInline(op, RT.count(RT.next(form)));5363 if(inline != null)5364 return analyze(context, preserveTag(form, inline.applyTo(RT.next(form))));5365 IParser p;5366 if(op.equals(FN))5367 return FnExpr.parse(context, form, name);5368 else if((p = (IParser) specials.valAt(op)) != null)5369 return p.parse(context, form);5370 else5371 return InvokeExpr.parse(context, form);5372 }5373 catch(Throwable e)5374 {5375 if(!(e instanceof CompilerException))5376 throw new CompilerException((String) SOURCE.deref(), (Integer) LINE.deref(), e);5377 else5378 throw (CompilerException) e;5379 }5380 finally5381 {5382 Var.popThreadBindings();5383 }5384 }5386 static String errorMsg(String source, int line, String s){5387 return String.format("%s (%s:%d)", s, source, line);5388 }5390 public static Object eval(Object form) throws Exception{5391 return eval(form, true);5392 }5394 public static Object eval(Object form, boolean freshLoader) throws Exception{5395 boolean createdLoader = false;5396 if(true)//!LOADER.isBound())5397 {5398 Var.pushThreadBindings(RT.map(LOADER, RT.makeClassLoader()));5399 createdLoader = true;5400 }5401 try5402 {5403 Integer line = (Integer) LINE.deref();5404 if(RT.meta(form) != null && RT.meta(form).containsKey(RT.LINE_KEY))5405 line = (Integer) RT.meta(form).valAt(RT.LINE_KEY);5406 Var.pushThreadBindings(RT.map(LINE, line));5407 try5408 {5409 form = macroexpand(form);5410 if(form instanceof IPersistentCollection && Util.equals(RT.first(form), DO))5411 {5412 ISeq s = RT.next(form);5413 for(; RT.next(s) != null; s = RT.next(s))5414 eval(RT.first(s),false);5415 return eval(RT.first(s),false);5416 }5417 else if(form instanceof IPersistentCollection5418 && !(RT.first(form) instanceof Symbol5419 && ((Symbol) RT.first(form)).name.startsWith("def")))5420 {5421 ObjExpr fexpr = (ObjExpr) analyze(C.EXPRESSION, RT.list(FN, PersistentVector.EMPTY, form),5422 "eval" + RT.nextID());5423 IFn fn = (IFn) fexpr.eval();5424 return fn.invoke();5425 }5426 else5427 {5428 Expr expr = analyze(C.EVAL, form);5429 return expr.eval();5430 }5431 }5432 finally5433 {5434 Var.popThreadBindings();5435 }5436 }5437 catch(Throwable e)5438 {5439 if(!(e instanceof CompilerException))5440 throw new CompilerException((String) SOURCE.deref(), (Integer) LINE.deref(), e);5441 else5442 throw (CompilerException) e;5443 }5444 finally5445 {5446 if(createdLoader)5447 Var.popThreadBindings();5448 }5449 }5451 private static int registerConstant(Object o){5452 if(!CONSTANTS.isBound())5453 return -1;5454 PersistentVector v = (PersistentVector) CONSTANTS.deref();5455 IdentityHashMap<Object,Integer> ids = (IdentityHashMap<Object,Integer>) CONSTANT_IDS.deref();5456 Integer i = ids.get(o);5457 if(i != null)5458 return i;5459 CONSTANTS.set(RT.conj(v, o));5460 ids.put(o, v.count());5461 return v.count();5462 }5464 private static KeywordExpr registerKeyword(Keyword keyword){5465 if(!KEYWORDS.isBound())5466 return new KeywordExpr(keyword);5468 IPersistentMap keywordsMap = (IPersistentMap) KEYWORDS.deref();5469 Object id = RT.get(keywordsMap, keyword);5470 if(id == null)5471 {5472 KEYWORDS.set(RT.assoc(keywordsMap, keyword, registerConstant(keyword)));5473 }5474 return new KeywordExpr(keyword);5475 // KeywordExpr ke = (KeywordExpr) RT.get(keywordsMap, keyword);5476 // if(ke == null)5477 // KEYWORDS.set(RT.assoc(keywordsMap, keyword, ke = new KeywordExpr(keyword)));5478 // return ke;5479 }5481 private static int registerKeywordCallsite(Keyword keyword){5482 if(!KEYWORD_CALLSITES.isBound())5483 throw new IllegalAccessError("KEYWORD_CALLSITES is not bound");5485 IPersistentVector keywordCallsites = (IPersistentVector) KEYWORD_CALLSITES.deref();5487 keywordCallsites = keywordCallsites.cons(keyword);5488 KEYWORD_CALLSITES.set(keywordCallsites);5489 return keywordCallsites.count()-1;5490 }5492 private static int registerProtocolCallsite(Var v){5493 if(!PROTOCOL_CALLSITES.isBound())5494 throw new IllegalAccessError("PROTOCOL_CALLSITES is not bound");5496 IPersistentVector protocolCallsites = (IPersistentVector) PROTOCOL_CALLSITES.deref();5498 protocolCallsites = protocolCallsites.cons(v);5499 PROTOCOL_CALLSITES.set(protocolCallsites);5500 return protocolCallsites.count()-1;5501 }5503 private static int registerVarCallsite(Var v){5504 if(!VAR_CALLSITES.isBound())5505 throw new IllegalAccessError("VAR_CALLSITES is not bound");5507 IPersistentVector varCallsites = (IPersistentVector) VAR_CALLSITES.deref();5509 varCallsites = varCallsites.cons(v);5510 VAR_CALLSITES.set(varCallsites);5511 return varCallsites.count()-1;5512 }5514 static ISeq fwdPath(PathNode p1){5515 ISeq ret = null;5516 for(;p1 != null;p1 = p1.parent)5517 ret = RT.cons(p1,ret);5518 return ret;5519 }5521 static PathNode commonPath(PathNode n1, PathNode n2){5522 ISeq xp = fwdPath(n1);5523 ISeq yp = fwdPath(n2);5524 if(RT.first(xp) != RT.first(yp))5525 return null;5526 while(RT.second(xp) != null && RT.second(xp) == RT.second(yp))5527 {5528 xp = xp.next();5529 yp = yp.next();5530 }5531 return (PathNode) RT.first(xp);5532 }5534 static void addAnnotation(Object visitor, IPersistentMap meta){5535 try{5536 if(meta != null && ADD_ANNOTATIONS.isBound())5537 ADD_ANNOTATIONS.invoke(visitor, meta);5538 }5539 catch (Exception e)5540 {5541 throw new RuntimeException(e);5542 }5543 }5545 static void addParameterAnnotation(Object visitor, IPersistentMap meta, int i){5546 try{5547 if(meta != null && ADD_ANNOTATIONS.isBound())5548 ADD_ANNOTATIONS.invoke(visitor, meta, i);5549 }5550 catch (Exception e)5551 {5552 throw new RuntimeException(e);5553 }5554 }5556 private static Expr analyzeSymbol(Symbol sym) throws Exception{5557 Symbol tag = tagOf(sym);5558 if(sym.ns == null) //ns-qualified syms are always Vars5559 {5560 LocalBinding b = referenceLocal(sym);5561 if(b != null)5562 {5563 return new LocalBindingExpr(b, tag);5564 }5565 }5566 else5567 {5568 if(namespaceFor(sym) == null)5569 {5570 Symbol nsSym = Symbol.create(sym.ns);5571 Class c = HostExpr.maybeClass(nsSym, false);5572 if(c != null)5573 {5574 if(Reflector.getField(c, sym.name, true) != null)5575 return new StaticFieldExpr((Integer) LINE.deref(), c, sym.name, tag);5576 throw new Exception("Unable to find static field: " + sym.name + " in " + c);5577 }5578 }5579 }5580 //Var v = lookupVar(sym, false);5581 // Var v = lookupVar(sym, false);5582 // if(v != null)5583 // return new VarExpr(v, tag);5584 Object o = resolve(sym);5585 if(o instanceof Var)5586 {5587 Var v = (Var) o;5588 if(isMacro(v) != null)5589 throw new Exception("Can't take value of a macro: " + v);5590 registerVar(v);5591 return new VarExpr(v, tag);5592 }5593 else if(o instanceof Class)5594 return new ConstantExpr(o);5595 else if(o instanceof Symbol)5596 return new UnresolvedVarExpr((Symbol) o);5598 throw new Exception("Unable to resolve symbol: " + sym + " in this context");5600 }5602 static String destubClassName(String className){5603 //skip over prefix + '.' or '/'5604 if(className.startsWith(COMPILE_STUB_PREFIX))5605 return className.substring(COMPILE_STUB_PREFIX.length()+1);5606 return className;5607 }5609 static Type getType(Class c){5610 String descriptor = Type.getType(c).getDescriptor();5611 if(descriptor.startsWith("L"))5612 descriptor = "L" + destubClassName(descriptor.substring(1));5613 return Type.getType(descriptor);5614 }5616 static Object resolve(Symbol sym, boolean allowPrivate) throws Exception{5617 return resolveIn(currentNS(), sym, allowPrivate);5618 }5620 static Object resolve(Symbol sym) throws Exception{5621 return resolveIn(currentNS(), sym, false);5622 }5624 static Namespace namespaceFor(Symbol sym){5625 return namespaceFor(currentNS(), sym);5626 }5628 static Namespace namespaceFor(Namespace inns, Symbol sym){5629 //note, presumes non-nil sym.ns5630 // first check against currentNS' aliases...5631 Symbol nsSym = Symbol.create(sym.ns);5632 Namespace ns = inns.lookupAlias(nsSym);5633 if(ns == null)5634 {5635 // ...otherwise check the Namespaces map.5636 ns = Namespace.find(nsSym);5637 }5638 return ns;5639 }5641 static public Object resolveIn(Namespace n, Symbol sym, boolean allowPrivate) throws Exception{5642 //note - ns-qualified vars must already exist5643 if(sym.ns != null)5644 {5645 Namespace ns = namespaceFor(n, sym);5646 if(ns == null)5647 throw new Exception("No such namespace: " + sym.ns);5649 Var v = ns.findInternedVar(Symbol.create(sym.name));5650 if(v == null)5651 throw new Exception("No such var: " + sym);5652 else if(v.ns != currentNS() && !v.isPublic() && !allowPrivate)5653 throw new IllegalStateException("var: " + sym + " is not public");5654 return v;5655 }5656 else if(sym.name.indexOf('.') > 0 || sym.name.charAt(0) == '[')5657 {5658 return RT.classForName(sym.name);5659 }5660 else if(sym.equals(NS))5661 return RT.NS_VAR;5662 else if(sym.equals(IN_NS))5663 return RT.IN_NS_VAR;5664 else5665 {5666 if(Util.equals(sym,COMPILE_STUB_SYM.get()))5667 return COMPILE_STUB_CLASS.get();5668 Object o = n.getMapping(sym);5669 if(o == null)5670 {5671 if(RT.booleanCast(RT.ALLOW_UNRESOLVED_VARS.deref()))5672 {5673 return sym;5674 }5675 else5676 {5677 throw new Exception("Unable to resolve symbol: " + sym + " in this context");5678 }5679 }5680 return o;5681 }5682 }5685 static public Object maybeResolveIn(Namespace n, Symbol sym) throws Exception{5686 //note - ns-qualified vars must already exist5687 if(sym.ns != null)5688 {5689 Namespace ns = namespaceFor(n, sym);5690 if(ns == null)5691 return null;5692 Var v = ns.findInternedVar(Symbol.create(sym.name));5693 if(v == null)5694 return null;5695 return v;5696 }5697 else if(sym.name.indexOf('.') > 0 && !sym.name.endsWith(".")5698 || sym.name.charAt(0) == '[')5699 {5700 return RT.classForName(sym.name);5701 }5702 else if(sym.equals(NS))5703 return RT.NS_VAR;5704 else if(sym.equals(IN_NS))5705 return RT.IN_NS_VAR;5706 else5707 {5708 Object o = n.getMapping(sym);5709 return o;5710 }5711 }5714 static Var lookupVar(Symbol sym, boolean internNew) throws Exception{5715 Var var = null;5717 //note - ns-qualified vars in other namespaces must already exist5718 if(sym.ns != null)5719 {5720 Namespace ns = namespaceFor(sym);5721 if(ns == null)5722 return null;5723 //throw new Exception("No such namespace: " + sym.ns);5724 Symbol name = Symbol.create(sym.name);5725 if(internNew && ns == currentNS())5726 var = currentNS().intern(name);5727 else5728 var = ns.findInternedVar(name);5729 }5730 else if(sym.equals(NS))5731 var = RT.NS_VAR;5732 else if(sym.equals(IN_NS))5733 var = RT.IN_NS_VAR;5734 else5735 {5736 //is it mapped?5737 Object o = currentNS().getMapping(sym);5738 if(o == null)5739 {5740 //introduce a new var in the current ns5741 if(internNew)5742 var = currentNS().intern(Symbol.create(sym.name));5743 }5744 else if(o instanceof Var)5745 {5746 var = (Var) o;5747 }5748 else5749 {5750 throw new Exception("Expecting var, but " + sym + " is mapped to " + o);5751 }5752 }5753 if(var != null)5754 registerVar(var);5755 return var;5756 }5758 private static void registerVar(Var var) throws Exception{5759 if(!VARS.isBound())5760 return;5761 IPersistentMap varsMap = (IPersistentMap) VARS.deref();5762 Object id = RT.get(varsMap, var);5763 if(id == null)5764 {5765 VARS.set(RT.assoc(varsMap, var, registerConstant(var)));5766 }5767 // if(varsMap != null && RT.get(varsMap, var) == null)5768 // VARS.set(RT.assoc(varsMap, var, var));5769 }5771 static Namespace currentNS(){5772 return (Namespace) RT.CURRENT_NS.deref();5773 }5775 static void closeOver(LocalBinding b, ObjMethod method){5776 if(b != null && method != null)5777 {5778 if(RT.get(method.locals, b) == null)5779 {5780 method.objx.closes = (IPersistentMap) RT.assoc(method.objx.closes, b, b);5781 closeOver(b, method.parent);5782 }5783 else if(IN_CATCH_FINALLY.deref() != null)5784 {5785 method.localsUsedInCatchFinally = (PersistentHashSet) method.localsUsedInCatchFinally.cons(b.idx);5786 }5787 }5788 }5791 static LocalBinding referenceLocal(Symbol sym) throws Exception{5792 if(!LOCAL_ENV.isBound())5793 return null;5794 LocalBinding b = (LocalBinding) RT.get(LOCAL_ENV.deref(), sym);5795 if(b != null)5796 {5797 ObjMethod method = (ObjMethod) METHOD.deref();5798 closeOver(b, method);5799 }5800 return b;5801 }5803 private static Symbol tagOf(Object o){5804 Object tag = RT.get(RT.meta(o), RT.TAG_KEY);5805 if(tag instanceof Symbol)5806 return (Symbol) tag;5807 else if(tag instanceof String)5808 return Symbol.intern(null, (String) tag);5809 return null;5810 }5812 public static Object loadFile(String file) throws Exception{5813 // File fo = new File(file);5814 // if(!fo.exists())5815 // return null;5817 FileInputStream f = new FileInputStream(file);5818 try5819 {5820 return load(new InputStreamReader(f, RT.UTF8), new File(file).getAbsolutePath(), (new File(file)).getName());5821 }5822 finally5823 {5824 f.close();5825 }5826 }5828 public static Object load(Reader rdr) throws Exception{5829 return load(rdr, null, "NO_SOURCE_FILE");5830 }5832 public static Object load(Reader rdr, String sourcePath, String sourceName) throws Exception{5833 Object EOF = new Object();5834 Object ret = null;5835 LineNumberingPushbackReader pushbackReader =5836 (rdr instanceof LineNumberingPushbackReader) ? (LineNumberingPushbackReader) rdr :5837 new LineNumberingPushbackReader(rdr);5838 Var.pushThreadBindings(5839 RT.map(LOADER, RT.makeClassLoader(),5840 SOURCE_PATH, sourcePath,5841 SOURCE, sourceName,5842 METHOD, null,5843 LOCAL_ENV, null,5844 LOOP_LOCALS, null,5845 NEXT_LOCAL_NUM, 0,5846 RT.CURRENT_NS, RT.CURRENT_NS.deref(),5847 LINE_BEFORE, pushbackReader.getLineNumber(),5848 LINE_AFTER, pushbackReader.getLineNumber()5849 ));5851 try5852 {5853 for(Object r = LispReader.read(pushbackReader, false, EOF, false); r != EOF;5854 r = LispReader.read(pushbackReader, false, EOF, false))5855 {5856 LINE_AFTER.set(pushbackReader.getLineNumber());5857 ret = eval(r,false);5858 LINE_BEFORE.set(pushbackReader.getLineNumber());5859 }5860 }5861 catch(LispReader.ReaderException e)5862 {5863 throw new CompilerException(sourceName, e.line, e.getCause());5864 }5865 finally5866 {5867 Var.popThreadBindings();5868 }5869 return ret;5870 }5872 static public void writeClassFile(String internalName, byte[] bytecode) throws Exception{5873 String genPath = (String) COMPILE_PATH.deref();5874 if(genPath == null)5875 throw new Exception("*compile-path* not set");5876 String[] dirs = internalName.split("/");5877 String p = genPath;5878 for(int i = 0; i < dirs.length - 1; i++)5879 {5880 p += File.separator + dirs[i];5881 (new File(p)).mkdir();5882 }5883 String path = genPath + File.separator + internalName + ".class";5884 File cf = new File(path);5885 cf.createNewFile();5886 FileOutputStream cfs = new FileOutputStream(cf);5887 try5888 {5889 cfs.write(bytecode);5890 cfs.flush();5891 cfs.getFD().sync();5892 }5893 finally5894 {5895 cfs.close();5896 }5897 }5899 public static void pushNS(){5900 Var.pushThreadBindings(PersistentHashMap.create(Var.intern(Symbol.create("clojure.core"),5901 Symbol.create("*ns*")), null));5902 }5904 public static ILookupThunk getLookupThunk(Object target, Keyword k){5905 return null; //To change body of created methods use File | Settings | File Templates.5906 }5908 static void compile1(GeneratorAdapter gen, ObjExpr objx, Object form) throws Exception{5909 Integer line = (Integer) LINE.deref();5910 if(RT.meta(form) != null && RT.meta(form).containsKey(RT.LINE_KEY))5911 line = (Integer) RT.meta(form).valAt(RT.LINE_KEY);5912 Var.pushThreadBindings(5913 RT.map(LINE, line5914 ,LOADER, RT.makeClassLoader()5915 ));5916 try5917 {5918 form = macroexpand(form);5919 if(form instanceof IPersistentCollection && Util.equals(RT.first(form), DO))5920 {5921 for(ISeq s = RT.next(form); s != null; s = RT.next(s))5922 {5923 compile1(gen, objx, RT.first(s));5924 }5925 }5926 else5927 {5928 Expr expr = analyze(C.EVAL, form);5929 objx.keywords = (IPersistentMap) KEYWORDS.deref();5930 objx.vars = (IPersistentMap) VARS.deref();5931 objx.constants = (PersistentVector) CONSTANTS.deref();5932 expr.emit(C.EXPRESSION, objx, gen);5933 expr.eval();5934 }5935 }5936 finally5937 {5938 Var.popThreadBindings();5939 }5940 }5942 public static Object compile(Reader rdr, String sourcePath, String sourceName) throws Exception{5943 if(COMPILE_PATH.deref() == null)5944 throw new Exception("*compile-path* not set");5946 Object EOF = new Object();5947 Object ret = null;5948 LineNumberingPushbackReader pushbackReader =5949 (rdr instanceof LineNumberingPushbackReader) ? (LineNumberingPushbackReader) rdr :5950 new LineNumberingPushbackReader(rdr);5951 Var.pushThreadBindings(5952 RT.map(SOURCE_PATH, sourcePath,5953 SOURCE, sourceName,5954 METHOD, null,5955 LOCAL_ENV, null,5956 LOOP_LOCALS, null,5957 NEXT_LOCAL_NUM, 0,5958 RT.CURRENT_NS, RT.CURRENT_NS.deref(),5959 LINE_BEFORE, pushbackReader.getLineNumber(),5960 LINE_AFTER, pushbackReader.getLineNumber(),5961 CONSTANTS, PersistentVector.EMPTY,5962 CONSTANT_IDS, new IdentityHashMap(),5963 KEYWORDS, PersistentHashMap.EMPTY,5964 VARS, PersistentHashMap.EMPTY5965 // ,LOADER, RT.makeClassLoader()5966 ));5968 try5969 {5970 //generate loader class5971 ObjExpr objx = new ObjExpr(null);5972 objx.internalName = sourcePath.replace(File.separator, "/").substring(0, sourcePath.lastIndexOf('.'))5973 + RT.LOADER_SUFFIX;5975 objx.objtype = Type.getObjectType(objx.internalName);5976 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);5977 ClassVisitor cv = cw;5978 cv.visit(V1_5, ACC_PUBLIC + ACC_SUPER, objx.internalName, null, "java/lang/Object", null);5980 //static load method5981 GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC,5982 Method.getMethod("void load ()"),5983 null,5984 null,5985 cv);5986 gen.visitCode();5988 for(Object r = LispReader.read(pushbackReader, false, EOF, false); r != EOF;5989 r = LispReader.read(pushbackReader, false, EOF, false))5990 {5991 LINE_AFTER.set(pushbackReader.getLineNumber());5992 compile1(gen, objx, r);5993 LINE_BEFORE.set(pushbackReader.getLineNumber());5994 }5995 //end of load5996 gen.returnValue();5997 gen.endMethod();5999 //static fields for constants6000 for(int i = 0; i < objx.constants.count(); i++)6001 {6002 cv.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, objx.constantName(i), objx.constantType(i).getDescriptor(),6003 null, null);6004 }6006 //static init for constants, keywords and vars6007 GeneratorAdapter clinitgen = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC,6008 Method.getMethod("void <clinit> ()"),6009 null,6010 null,6011 cv);6012 clinitgen.visitCode();6013 Label startTry = clinitgen.newLabel();6014 Label endTry = clinitgen.newLabel();6015 Label end = clinitgen.newLabel();6016 Label finallyLabel = clinitgen.newLabel();6018 if(objx.constants.count() > 0)6019 {6020 objx.emitConstants(clinitgen);6021 }6022 clinitgen.invokeStatic(Type.getType(Compiler.class), Method.getMethod("void pushNS()"));6023 clinitgen.mark(startTry);6024 clinitgen.invokeStatic(objx.objtype, Method.getMethod("void load()"));6025 clinitgen.mark(endTry);6026 clinitgen.invokeStatic(VAR_TYPE, Method.getMethod("void popThreadBindings()"));6027 clinitgen.goTo(end);6029 clinitgen.mark(finallyLabel);6030 //exception should be on stack6031 clinitgen.invokeStatic(VAR_TYPE, Method.getMethod("void popThreadBindings()"));6032 clinitgen.throwException();6033 clinitgen.mark(end);6034 clinitgen.visitTryCatchBlock(startTry, endTry, finallyLabel, null);6036 //end of static init6037 clinitgen.returnValue();6038 clinitgen.endMethod();6040 //end of class6041 cv.visitEnd();6043 writeClassFile(objx.internalName, cw.toByteArray());6044 }6045 catch(LispReader.ReaderException e)6046 {6047 throw new CompilerException(sourceName, e.line, e.getCause());6048 }6049 finally6050 {6051 Var.popThreadBindings();6052 }6053 return ret;6054 }6057 static public class NewInstanceExpr extends ObjExpr{6058 //IPersistentMap optionsMap = PersistentArrayMap.EMPTY;6059 IPersistentCollection methods;6061 Map<IPersistentVector,java.lang.reflect.Method> mmap;6062 Map<IPersistentVector,Set<Class>> covariants;6064 public NewInstanceExpr(Object tag){6065 super(tag);6066 }6068 static class DeftypeParser implements IParser{6069 public Expr parse(C context, final Object frm) throws Exception{6070 ISeq rform = (ISeq) frm;6071 //(deftype* tagname classname [fields] :implements [interfaces] :tag tagname methods*)6072 rform = RT.next(rform);6073 String tagname = ((Symbol) rform.first()).toString();6074 rform = rform.next();6075 Symbol classname = (Symbol) rform.first();6076 rform = rform.next();6077 IPersistentVector fields = (IPersistentVector) rform.first();6078 rform = rform.next();6079 IPersistentMap opts = PersistentHashMap.EMPTY;6080 while(rform != null && rform.first() instanceof Keyword)6081 {6082 opts = opts.assoc(rform.first(), RT.second(rform));6083 rform = rform.next().next();6084 }6086 ObjExpr ret = build((IPersistentVector)RT.get(opts,implementsKey,PersistentVector.EMPTY),fields,null,tagname, classname,6087 (Symbol) RT.get(opts,RT.TAG_KEY),rform, frm);6088 return ret;6089 }6090 }6092 static class ReifyParser implements IParser{6093 public Expr parse(C context, Object frm) throws Exception{6094 //(reify this-name? [interfaces] (method-name [args] body)*)6095 ISeq form = (ISeq) frm;6096 ObjMethod enclosingMethod = (ObjMethod) METHOD.deref();6097 String basename = enclosingMethod != null ?6098 (trimGenID(enclosingMethod.objx.name) + "$")6099 : (munge(currentNS().name.name) + "$");6100 String simpleName = "reify__" + RT.nextID();6101 String classname = basename + simpleName;6103 ISeq rform = RT.next(form);6105 IPersistentVector interfaces = ((IPersistentVector) RT.first(rform)).cons(Symbol.intern("clojure.lang.IObj"));6108 rform = RT.next(rform);6111 ObjExpr ret = build(interfaces, null, null, classname, Symbol.intern(classname), null, rform, frm);6112 if(frm instanceof IObj && ((IObj) frm).meta() != null)6113 return new MetaExpr(ret, (MapExpr) MapExpr6114 .parse(context == C.EVAL ? context : C.EXPRESSION, ((IObj) frm).meta()));6115 else6116 return ret;6117 }6118 }6120 static ObjExpr build(IPersistentVector interfaceSyms, IPersistentVector fieldSyms, Symbol thisSym,6121 String tagName, Symbol className,6122 Symbol typeTag, ISeq methodForms, Object frm) throws Exception{6123 NewInstanceExpr ret = new NewInstanceExpr(null);6125 ret.src = frm;6126 ret.name = className.toString();6127 ret.classMeta = RT.meta(className);6128 ret.internalName = ret.name.replace('.', '/');6129 ret.objtype = Type.getObjectType(ret.internalName);6131 if(thisSym != null)6132 ret.thisName = thisSym.name;6134 if(fieldSyms != null)6135 {6136 IPersistentMap fmap = PersistentHashMap.EMPTY;6137 Object[] closesvec = new Object[2 * fieldSyms.count()];6138 for(int i=0;i<fieldSyms.count();i++)6139 {6140 Symbol sym = (Symbol) fieldSyms.nth(i);6141 LocalBinding lb = new LocalBinding(-1, sym, null,6142 new MethodParamExpr(tagClass(tagOf(sym))),false,null);6143 fmap = fmap.assoc(sym, lb);6144 closesvec[i*2] = lb;6145 closesvec[i*2 + 1] = lb;6146 }6148 //todo - inject __meta et al into closes - when?6149 //use array map to preserve ctor order6150 ret.closes = new PersistentArrayMap(closesvec);6151 ret.fields = fmap;6152 for(int i=fieldSyms.count()-1;i >= 0 && ((Symbol)fieldSyms.nth(i)).name.startsWith("__");--i)6153 ret.altCtorDrops++;6154 }6155 //todo - set up volatiles6156 // ret.volatiles = PersistentHashSet.create(RT.seq(RT.get(ret.optionsMap, volatileKey)));6158 PersistentVector interfaces = PersistentVector.EMPTY;6159 for(ISeq s = RT.seq(interfaceSyms);s!=null;s = s.next())6160 {6161 Class c = (Class) resolve((Symbol) s.first());6162 if(!c.isInterface())6163 throw new IllegalArgumentException("only interfaces are supported, had: " + c.getName());6164 interfaces = interfaces.cons(c);6165 }6166 Class superClass = Object.class;6167 Map[] mc = gatherMethods(superClass,RT.seq(interfaces));6168 Map overrideables = mc[0];6169 Map covariants = mc[1];6170 ret.mmap = overrideables;6171 ret.covariants = covariants;6173 String[] inames = interfaceNames(interfaces);6175 Class stub = compileStub(slashname(superClass),ret, inames, frm);6176 Symbol thistag = Symbol.intern(null,stub.getName());6178 try6179 {6180 Var.pushThreadBindings(6181 RT.map(CONSTANTS, PersistentVector.EMPTY,6182 CONSTANT_IDS, new IdentityHashMap(),6183 KEYWORDS, PersistentHashMap.EMPTY,6184 VARS, PersistentHashMap.EMPTY,6185 KEYWORD_CALLSITES, PersistentVector.EMPTY,6186 PROTOCOL_CALLSITES, PersistentVector.EMPTY,6187 VAR_CALLSITES, PersistentVector.EMPTY6188 ));6189 if(ret.isDeftype())6190 {6191 Var.pushThreadBindings(RT.map(METHOD, null,6192 LOCAL_ENV, ret.fields6193 , COMPILE_STUB_SYM, Symbol.intern(null, tagName)6194 , COMPILE_STUB_CLASS, stub));6195 }6197 //now (methodname [args] body)*6198 ret.line = (Integer) LINE.deref();6199 IPersistentCollection methods = null;6200 for(ISeq s = methodForms; s != null; s = RT.next(s))6201 {6202 NewInstanceMethod m = NewInstanceMethod.parse(ret, (ISeq) RT.first(s),thistag, overrideables);6203 methods = RT.conj(methods, m);6204 }6207 ret.methods = methods;6208 ret.keywords = (IPersistentMap) KEYWORDS.deref();6209 ret.vars = (IPersistentMap) VARS.deref();6210 ret.constants = (PersistentVector) CONSTANTS.deref();6211 ret.constantsID = RT.nextID();6212 ret.keywordCallsites = (IPersistentVector) KEYWORD_CALLSITES.deref();6213 ret.protocolCallsites = (IPersistentVector) PROTOCOL_CALLSITES.deref();6214 ret.varCallsites = (IPersistentVector) VAR_CALLSITES.deref();6215 }6216 finally6217 {6218 if(ret.isDeftype())6219 Var.popThreadBindings();6220 Var.popThreadBindings();6221 }6223 ret.compile(slashname(superClass),inames,false);6224 ret.getCompiledClass();6225 return ret;6226 }6228 /***6229 * Current host interop uses reflection, which requires pre-existing classes6230 * Work around this by:6231 * Generate a stub class that has the same interfaces and fields as the class we are generating.6232 * Use it as a type hint for this, and bind the simple name of the class to this stub (in resolve etc)6233 * Unmunge the name (using a magic prefix) on any code gen for classes6234 */6235 static Class compileStub(String superName, NewInstanceExpr ret, String[] interfaceNames, Object frm){6236 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);6237 ClassVisitor cv = cw;6238 cv.visit(V1_5, ACC_PUBLIC + ACC_SUPER, COMPILE_STUB_PREFIX + "/" + ret.internalName,6239 null,superName,interfaceNames);6241 //instance fields for closed-overs6242 for(ISeq s = RT.keys(ret.closes); s != null; s = s.next())6243 {6244 LocalBinding lb = (LocalBinding) s.first();6245 int access = ACC_PUBLIC + (ret.isVolatile(lb) ? ACC_VOLATILE :6246 ret.isMutable(lb) ? 0 :6247 ACC_FINAL);6248 if(lb.getPrimitiveType() != null)6249 cv.visitField(access6250 , lb.name, Type.getType(lb.getPrimitiveType()).getDescriptor(),6251 null, null);6252 else6253 //todo - when closed-overs are fields, use more specific types here and in ctor and emitLocal?6254 cv.visitField(access6255 , lb.name, OBJECT_TYPE.getDescriptor(), null, null);6256 }6258 //ctor that takes closed-overs and does nothing6259 Method m = new Method("<init>", Type.VOID_TYPE, ret.ctorTypes());6260 GeneratorAdapter ctorgen = new GeneratorAdapter(ACC_PUBLIC,6261 m,6262 null,6263 null,6264 cv);6265 ctorgen.visitCode();6266 ctorgen.loadThis();6267 ctorgen.invokeConstructor(Type.getObjectType(superName), voidctor);6268 ctorgen.returnValue();6269 ctorgen.endMethod();6271 if(ret.altCtorDrops > 0)6272 {6273 Type[] ctorTypes = ret.ctorTypes();6274 Type[] altCtorTypes = new Type[ctorTypes.length-ret.altCtorDrops];6275 for(int i=0;i<altCtorTypes.length;i++)6276 altCtorTypes[i] = ctorTypes[i];6277 Method alt = new Method("<init>", Type.VOID_TYPE, altCtorTypes);6278 ctorgen = new GeneratorAdapter(ACC_PUBLIC,6279 alt,6280 null,6281 null,6282 cv);6283 ctorgen.visitCode();6284 ctorgen.loadThis();6285 ctorgen.loadArgs();6286 for(int i=0;i<ret.altCtorDrops;i++)6287 ctorgen.visitInsn(Opcodes.ACONST_NULL);6289 ctorgen.invokeConstructor(Type.getObjectType(COMPILE_STUB_PREFIX + "/" + ret.internalName),6290 new Method("<init>", Type.VOID_TYPE, ctorTypes));6292 ctorgen.returnValue();6293 ctorgen.endMethod();6294 }6295 //end of class6296 cv.visitEnd();6298 byte[] bytecode = cw.toByteArray();6299 DynamicClassLoader loader = (DynamicClassLoader) LOADER.deref();6300 return loader.defineClass(COMPILE_STUB_PREFIX + "." + ret.name, bytecode, frm);6301 }6303 static String[] interfaceNames(IPersistentVector interfaces){6304 int icnt = interfaces.count();6305 String[] inames = icnt > 0 ? new String[icnt] : null;6306 for(int i=0;i<icnt;i++)6307 inames[i] = slashname((Class) interfaces.nth(i));6308 return inames;6309 }6312 static String slashname(Class c){6313 return c.getName().replace('.', '/');6314 }6317 protected void emitMethods(ClassVisitor cv){6318 for(ISeq s = RT.seq(methods); s != null; s = s.next())6319 {6320 ObjMethod method = (ObjMethod) s.first();6321 method.emit(this, cv);6322 }6323 //emit bridge methods6324 for(Map.Entry<IPersistentVector,Set<Class>> e : covariants.entrySet())6325 {6326 java.lang.reflect.Method m = mmap.get(e.getKey());6327 Class[] params = m.getParameterTypes();6328 Type[] argTypes = new Type[params.length];6330 for(int i = 0; i < params.length; i++)6331 {6332 argTypes[i] = Type.getType(params[i]);6333 }6335 Method target = new Method(m.getName(), Type.getType(m.getReturnType()), argTypes);6337 for(Class retType : e.getValue())6338 {6339 Method meth = new Method(m.getName(), Type.getType(retType), argTypes);6341 GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC + ACC_BRIDGE,6342 meth,6343 null,6344 //todo don't hardwire this6345 EXCEPTION_TYPES,6346 cv);6347 gen.visitCode();6348 gen.loadThis();6349 gen.loadArgs();6350 gen.invokeInterface(Type.getType(m.getDeclaringClass()),target);6351 gen.returnValue();6352 gen.endMethod();6353 }6354 }6355 }6357 static public IPersistentVector msig(java.lang.reflect.Method m){6358 return RT.vector(m.getName(), RT.seq(m.getParameterTypes()),m.getReturnType());6359 }6361 static void considerMethod(java.lang.reflect.Method m, Map mm){6362 IPersistentVector mk = msig(m);6363 int mods = m.getModifiers();6365 if(!(mm.containsKey(mk)6366 || !(Modifier.isPublic(mods) || Modifier.isProtected(mods))6367 || Modifier.isStatic(mods)6368 || Modifier.isFinal(mods)))6369 {6370 mm.put(mk, m);6371 }6372 }6374 static void gatherMethods(Class c, Map mm){6375 for(; c != null; c = c.getSuperclass())6376 {6377 for(java.lang.reflect.Method m : c.getDeclaredMethods())6378 considerMethod(m, mm);6379 for(java.lang.reflect.Method m : c.getMethods())6380 considerMethod(m, mm);6381 }6382 }6384 static public Map[] gatherMethods(Class sc, ISeq interfaces){6385 Map allm = new HashMap();6386 gatherMethods(sc, allm);6387 for(; interfaces != null; interfaces = interfaces.next())6388 gatherMethods((Class) interfaces.first(), allm);6390 Map<IPersistentVector,java.lang.reflect.Method> mm = new HashMap<IPersistentVector,java.lang.reflect.Method>();6391 Map<IPersistentVector,Set<Class>> covariants = new HashMap<IPersistentVector,Set<Class>>();6392 for(Object o : allm.entrySet())6393 {6394 Map.Entry e = (Map.Entry) o;6395 IPersistentVector mk = (IPersistentVector) e.getKey();6396 mk = (IPersistentVector) mk.pop();6397 java.lang.reflect.Method m = (java.lang.reflect.Method) e.getValue();6398 if(mm.containsKey(mk)) //covariant return6399 {6400 Set<Class> cvs = covariants.get(mk);6401 if(cvs == null)6402 {6403 cvs = new HashSet<Class>();6404 covariants.put(mk,cvs);6405 }6406 java.lang.reflect.Method om = mm.get(mk);6407 if(om.getReturnType().isAssignableFrom(m.getReturnType()))6408 {6409 cvs.add(om.getReturnType());6410 mm.put(mk, m);6411 }6412 else6413 cvs.add(m.getReturnType());6414 }6415 else6416 mm.put(mk, m);6417 }6418 return new Map[]{mm,covariants};6419 }6420 }6422 public static class NewInstanceMethod extends ObjMethod{6423 String name;6424 Type[] argTypes;6425 Type retType;6426 Class retClass;6427 Class[] exclasses;6429 static Symbol dummyThis = Symbol.intern(null,"dummy_this_dlskjsdfower");6430 private IPersistentVector parms;6432 public NewInstanceMethod(ObjExpr objx, ObjMethod parent){6433 super(objx, parent);6434 }6436 int numParams(){6437 return argLocals.count();6438 }6440 String getMethodName(){6441 return name;6442 }6444 Type getReturnType(){6445 return retType;6446 }6448 Type[] getArgTypes(){6449 return argTypes;6450 }6454 static public IPersistentVector msig(String name,Class[] paramTypes){6455 return RT.vector(name,RT.seq(paramTypes));6456 }6458 static NewInstanceMethod parse(ObjExpr objx, ISeq form, Symbol thistag,6459 Map overrideables) throws Exception{6460 //(methodname [this-name args*] body...)6461 //this-name might be nil6462 NewInstanceMethod method = new NewInstanceMethod(objx, (ObjMethod) METHOD.deref());6463 Symbol dotname = (Symbol)RT.first(form);6464 Symbol name = (Symbol) Symbol.intern(null,munge(dotname.name)).withMeta(RT.meta(dotname));6465 IPersistentVector parms = (IPersistentVector) RT.second(form);6466 if(parms.count() == 0)6467 {6468 throw new IllegalArgumentException("Must supply at least one argument for 'this' in: " + dotname);6469 }6470 Symbol thisName = (Symbol) parms.nth(0);6471 parms = RT.subvec(parms,1,parms.count());6472 ISeq body = RT.next(RT.next(form));6473 try6474 {6475 method.line = (Integer) LINE.deref();6476 //register as the current method and set up a new env frame6477 PathNode pnode = new PathNode(PATHTYPE.PATH, (PathNode) CLEAR_PATH.get());6478 Var.pushThreadBindings(6479 RT.map(6480 METHOD, method,6481 LOCAL_ENV, LOCAL_ENV.deref(),6482 LOOP_LOCALS, null,6483 NEXT_LOCAL_NUM, 06484 ,CLEAR_PATH, pnode6485 ,CLEAR_ROOT, pnode6486 ,CLEAR_SITES, PersistentHashMap.EMPTY6487 ));6489 //register 'this' as local 06490 if(thisName != null)6491 registerLocal((thisName == null) ? dummyThis:thisName,thistag, null,false);6492 else6493 getAndIncLocalNum();6495 PersistentVector argLocals = PersistentVector.EMPTY;6496 method.retClass = tagClass(tagOf(name));6497 method.argTypes = new Type[parms.count()];6498 boolean hinted = tagOf(name) != null;6499 Class[] pclasses = new Class[parms.count()];6500 Symbol[] psyms = new Symbol[parms.count()];6502 for(int i = 0; i < parms.count(); i++)6503 {6504 if(!(parms.nth(i) instanceof Symbol))6505 throw new IllegalArgumentException("params must be Symbols");6506 Symbol p = (Symbol) parms.nth(i);6507 Object tag = tagOf(p);6508 if(tag != null)6509 hinted = true;6510 if(p.getNamespace() != null)6511 p = Symbol.create(p.name);6512 Class pclass = tagClass(tag);6513 pclasses[i] = pclass;6514 psyms[i] = p;6515 }6516 Map matches = findMethodsWithNameAndArity(name.name, parms.count(), overrideables);6517 Object mk = msig(name.name, pclasses);6518 java.lang.reflect.Method m = null;6519 if(matches.size() > 0)6520 {6521 //multiple methods6522 if(matches.size() > 1)6523 {6524 //must be hinted and match one method6525 if(!hinted)6526 throw new IllegalArgumentException("Must hint overloaded method: " + name.name);6527 m = (java.lang.reflect.Method) matches.get(mk);6528 if(m == null)6529 throw new IllegalArgumentException("Can't find matching overloaded method: " + name.name);6530 if(m.getReturnType() != method.retClass)6531 throw new IllegalArgumentException("Mismatched return type: " + name.name +6532 ", expected: " + m.getReturnType().getName() + ", had: " + method.retClass.getName());6533 }6534 else //one match6535 {6536 //if hinted, validate match,6537 if(hinted)6538 {6539 m = (java.lang.reflect.Method) matches.get(mk);6540 if(m == null)6541 throw new IllegalArgumentException("Can't find matching method: " + name.name +6542 ", leave off hints for auto match.");6543 if(m.getReturnType() != method.retClass)6544 throw new IllegalArgumentException("Mismatched return type: " + name.name +6545 ", expected: " + m.getReturnType().getName() + ", had: " + method.retClass.getName());6546 }6547 else //adopt found method sig6548 {6549 m = (java.lang.reflect.Method) matches.values().iterator().next();6550 method.retClass = m.getReturnType();6551 pclasses = m.getParameterTypes();6552 }6553 }6554 }6555 // else if(findMethodsWithName(name.name,allmethods).size()>0)6556 // throw new IllegalArgumentException("Can't override/overload method: " + name.name);6557 else6558 throw new IllegalArgumentException("Can't define method not in interfaces: " + name.name);6560 //else6561 //validate unque name+arity among additional methods6563 method.retType = Type.getType(method.retClass);6564 method.exclasses = m.getExceptionTypes();6566 for(int i = 0; i < parms.count(); i++)6567 {6568 LocalBinding lb = registerLocal(psyms[i], null, new MethodParamExpr(pclasses[i]),true);6569 argLocals = argLocals.assocN(i,lb);6570 method.argTypes[i] = Type.getType(pclasses[i]);6571 }6572 for(int i = 0; i < parms.count(); i++)6573 {6574 if(pclasses[i] == long.class || pclasses[i] == double.class)6575 getAndIncLocalNum();6576 }6577 LOOP_LOCALS.set(argLocals);6578 method.name = name.name;6579 method.methodMeta = RT.meta(name);6580 method.parms = parms;6581 method.argLocals = argLocals;6582 method.body = (new BodyExpr.Parser()).parse(C.RETURN, body);6583 return method;6584 }6585 finally6586 {6587 Var.popThreadBindings();6588 }6589 }6591 private static Map findMethodsWithNameAndArity(String name, int arity, Map mm){6592 Map ret = new HashMap();6593 for(Object o : mm.entrySet())6594 {6595 Map.Entry e = (Map.Entry) o;6596 java.lang.reflect.Method m = (java.lang.reflect.Method) e.getValue();6597 if(name.equals(m.getName()) && m.getParameterTypes().length == arity)6598 ret.put(e.getKey(), e.getValue());6599 }6600 return ret;6601 }6603 private static Map findMethodsWithName(String name, Map mm){6604 Map ret = new HashMap();6605 for(Object o : mm.entrySet())6606 {6607 Map.Entry e = (Map.Entry) o;6608 java.lang.reflect.Method m = (java.lang.reflect.Method) e.getValue();6609 if(name.equals(m.getName()))6610 ret.put(e.getKey(), e.getValue());6611 }6612 return ret;6613 }6615 public void emit(ObjExpr obj, ClassVisitor cv){6616 Method m = new Method(getMethodName(), getReturnType(), getArgTypes());6618 Type[] extypes = null;6619 if(exclasses.length > 0)6620 {6621 extypes = new Type[exclasses.length];6622 for(int i=0;i<exclasses.length;i++)6623 extypes[i] = Type.getType(exclasses[i]);6624 }6625 GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC,6626 m,6627 null,6628 extypes,6629 cv);6630 addAnnotation(gen,methodMeta);6631 for(int i = 0; i < parms.count(); i++)6632 {6633 IPersistentMap meta = RT.meta(parms.nth(i));6634 addParameterAnnotation(gen, meta, i);6635 }6636 gen.visitCode();6637 Label loopLabel = gen.mark();6638 gen.visitLineNumber(line, loopLabel);6639 try6640 {6641 Var.pushThreadBindings(RT.map(LOOP_LABEL, loopLabel, METHOD, this));6642 MaybePrimitiveExpr be = (MaybePrimitiveExpr) body;6643 if(Util.isPrimitive(retClass) && be.canEmitPrimitive())6644 {6645 if(be.getJavaClass() == retClass)6646 be.emitUnboxed(C.RETURN,obj,gen);6647 //todo - support the standard widening conversions6648 else6649 throw new IllegalArgumentException("Mismatched primitive return, expected: "6650 + retClass + ", had: " + be.getJavaClass());6651 }6652 else6653 {6654 body.emit(C.RETURN, obj, gen);6655 if(retClass == void.class)6656 {6657 gen.pop();6658 }6659 else6660 gen.unbox(retType);6661 }6663 Label end = gen.mark();6664 gen.visitLocalVariable("this", obj.objtype.getDescriptor(), null, loopLabel, end, 0);6665 for(ISeq lbs = argLocals.seq(); lbs != null; lbs = lbs.next())6666 {6667 LocalBinding lb = (LocalBinding) lbs.first();6668 gen.visitLocalVariable(lb.name, argTypes[lb.idx-1].getDescriptor(), null, loopLabel, end, lb.idx);6669 }6670 }6671 catch(Exception e)6672 {6673 throw new RuntimeException(e);6674 }6675 finally6676 {6677 Var.popThreadBindings();6678 }6680 gen.returnValue();6681 //gen.visitMaxs(1, 1);6682 gen.endMethod();6683 }6684 }6686 static Class primClass(Symbol sym){6687 if(sym == null)6688 return null;6689 Class c = null;6690 if(sym.name.equals("int"))6691 c = int.class;6692 else if(sym.name.equals("long"))6693 c = long.class;6694 else if(sym.name.equals("float"))6695 c = float.class;6696 else if(sym.name.equals("double"))6697 c = double.class;6698 else if(sym.name.equals("char"))6699 c = char.class;6700 else if(sym.name.equals("short"))6701 c = short.class;6702 else if(sym.name.equals("byte"))6703 c = byte.class;6704 else if(sym.name.equals("boolean"))6705 c = boolean.class;6706 else if(sym.name.equals("void"))6707 c = void.class;6708 return c;6709 }6711 static Class tagClass(Object tag) throws Exception{6712 if(tag == null)6713 return Object.class;6714 Class c = null;6715 if(tag instanceof Symbol)6716 c = primClass((Symbol) tag);6717 if(c == null)6718 c = HostExpr.tagToClass(tag);6719 return c;6720 }6722 static public class MethodParamExpr implements Expr, MaybePrimitiveExpr{6723 final Class c;6725 public MethodParamExpr(Class c){6726 this.c = c;6727 }6729 public Object eval() throws Exception{6730 throw new Exception("Can't eval");6731 }6733 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){6734 throw new RuntimeException("Can't emit");6735 }6737 public boolean hasJavaClass() throws Exception{6738 return c != null;6739 }6741 public Class getJavaClass() throws Exception{6742 return c;6743 }6745 public boolean canEmitPrimitive(){6746 return Util.isPrimitive(c);6747 }6749 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){6750 throw new RuntimeException("Can't emit");6751 }6752 }6754 public static class CaseExpr extends UntypedExpr{6755 public final LocalBindingExpr expr;6756 public final int shift, mask, low, high;6757 public final Expr defaultExpr;6758 public final HashMap<Integer,Expr> tests;6759 public final HashMap<Integer,Expr> thens;6760 public final boolean allKeywords;6762 public final int line;6764 final static Method hashMethod = Method.getMethod("int hash(Object)");6765 final static Method hashCodeMethod = Method.getMethod("int hashCode()");6766 final static Method equalsMethod = Method.getMethod("boolean equals(Object, Object)");6769 public CaseExpr(int line, LocalBindingExpr expr, int shift, int mask, int low, int high, Expr defaultExpr,6770 HashMap<Integer,Expr> tests,HashMap<Integer,Expr> thens, boolean allKeywords){6771 this.expr = expr;6772 this.shift = shift;6773 this.mask = mask;6774 this.low = low;6775 this.high = high;6776 this.defaultExpr = defaultExpr;6777 this.tests = tests;6778 this.thens = thens;6779 this.line = line;6780 this.allKeywords = allKeywords;6781 }6783 public Object eval() throws Exception{6784 throw new UnsupportedOperationException("Can't eval case");6785 }6787 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){6788 Label defaultLabel = gen.newLabel();6789 Label endLabel = gen.newLabel();6790 HashMap<Integer,Label> labels = new HashMap();6792 for(Integer i : tests.keySet())6793 {6794 labels.put(i, gen.newLabel());6795 }6797 Label[] la = new Label[(high-low)+1];6799 for(int i=low;i<=high;i++)6800 {6801 la[i-low] = labels.containsKey(i) ? labels.get(i) : defaultLabel;6802 }6804 gen.visitLineNumber(line, gen.mark());6805 expr.emit(C.EXPRESSION, objx, gen);6806 gen.invokeStatic(UTIL_TYPE,hashMethod);6807 gen.push(shift);6808 gen.visitInsn(ISHR);6809 gen.push(mask);6810 gen.visitInsn(IAND);6811 gen.visitTableSwitchInsn(low, high, defaultLabel, la);6813 for(Integer i : labels.keySet())6814 {6815 gen.mark(labels.get(i));6816 expr.emit(C.EXPRESSION, objx, gen);6817 tests.get(i).emit(C.EXPRESSION, objx, gen);6818 if(allKeywords)6819 {6820 gen.visitJumpInsn(IF_ACMPNE, defaultLabel);6821 }6822 else6823 {6824 gen.invokeStatic(UTIL_TYPE, equalsMethod);6825 gen.ifZCmp(GeneratorAdapter.EQ, defaultLabel);6826 }6827 thens.get(i).emit(C.EXPRESSION,objx,gen);6828 gen.goTo(endLabel);6829 }6831 gen.mark(defaultLabel);6832 defaultExpr.emit(C.EXPRESSION, objx, gen);6833 gen.mark(endLabel);6834 if(context == C.STATEMENT)6835 gen.pop();6836 }6838 static class Parser implements IParser{6839 //(case* expr shift mask low high default map<minhash, [test then]> identity?)6840 //prepared by case macro and presumed correct6841 //case macro binds actual expr in let so expr is always a local,6842 //no need to worry about multiple evaluation6843 public Expr parse(C context, Object frm) throws Exception{6844 ISeq form = (ISeq) frm;6845 if(context == C.EVAL)6846 return analyze(context, RT.list(RT.list(FN, PersistentVector.EMPTY, form)));6847 PersistentVector args = PersistentVector.create(form.next());6848 HashMap<Integer,Expr> tests = new HashMap();6849 HashMap<Integer,Expr> thens = new HashMap();6851 LocalBindingExpr testexpr = (LocalBindingExpr) analyze(C.EXPRESSION, args.nth(0));6852 testexpr.shouldClear = false;6854 PathNode branch = new PathNode(PATHTYPE.BRANCH, (PathNode) CLEAR_PATH.get());6855 for(Object o : ((Map)args.nth(6)).entrySet())6856 {6857 Map.Entry e = (Map.Entry) o;6858 Integer minhash = (Integer) e.getKey();6859 MapEntry me = (MapEntry) e.getValue();6860 Expr testExpr = new ConstantExpr(me.getKey());6861 tests.put(minhash, testExpr);6862 Expr thenExpr;6863 try {6864 Var.pushThreadBindings(6865 RT.map(CLEAR_PATH, new PathNode(PATHTYPE.PATH,branch)));6866 thenExpr = analyze(context, me.getValue());6867 }6868 finally{6869 Var.popThreadBindings();6870 }6871 thens.put(minhash, thenExpr);6872 }6874 Expr defaultExpr;6875 try {6876 Var.pushThreadBindings(6877 RT.map(CLEAR_PATH, new PathNode(PATHTYPE.PATH,branch)));6878 defaultExpr = analyze(context, args.nth(5));6879 }6880 finally{6881 Var.popThreadBindings();6882 }6884 return new CaseExpr((Integer) LINE.deref(),6885 testexpr,6886 (Integer)args.nth(1),6887 (Integer)args.nth(2),6888 (Integer)args.nth(3),6889 (Integer)args.nth(4),6890 defaultExpr,6891 tests,thens,args.nth(7) != RT.F);6893 }6894 }6895 }6897 }