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 the
4 * 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 by
7 * 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_, null
128 );
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 static
152 {
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->localbinding
173 static final public Var LOCAL_ENV = Var.create(null);
175 //vector<localbinding>
176 static final public Var LOOP_LOCALS = Var.create();
178 //Label
179 static final public Var LOOP_LABEL = Var.create();
181 //vector<object>
182 static final public Var CONSTANTS = Var.create();
184 //IdentityHashMap
185 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->constid
197 static final public Var KEYWORDS = Var.create();
199 //var->constid
200 static final public Var VARS = Var.create();
202 //FnFrame
203 static final public Var METHOD = Var.create(null);
205 //null or not
206 static final public Var IN_CATCH_FINALLY = Var.create(null);
208 //DynamicClassLoader
209 static final public Var LOADER = Var.create();
211 //String
212 static final public Var SOURCE = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")),
213 Symbol.create("*source-path*"), "NO_SOURCE_FILE");
215 //String
216 static final public Var SOURCE_PATH = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")),
217 Symbol.create("*file*"), "NO_SOURCE_PATH");
219 //String
220 static final public Var COMPILE_PATH = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")),
221 Symbol.create("*compile-path*"), null);
222 //boolean
223 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 //Integer
233 static final public Var LINE = Var.create(0);
235 //Integer
236 static final public Var LINE_BEFORE = Var.create(0);
237 static final public Var LINE_AFTER = Var.create(0);
239 //Integer
240 static final public Var NEXT_LOCAL_NUM = Var.create(0);
242 //Integer
243 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 chain
251 static final public Var CLEAR_PATH = Var.create(null);
253 //tail of PathNode chain
254 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 ignored
261 EXPRESSION, //value required
262 RETURN, //tail position relative to enclosing recur frame
263 EVAL
264 }
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 try
355 {
356 if(initProvided)
357 {
358 // if(init instanceof FnExpr && ((FnExpr) init).closes.count()==0)
359 // var.bindRoot(new FnLoaderThunk((FnExpr) init,var));
360 // else
361 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 else
376 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 else
431 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 else
712 {
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 else
757 {
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 else
776 {
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) or
785 //(. 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 instance
791 //static target must be symbol, either fully.qualified.Classname or Classname that has been imported
792 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 static
796 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 Symbol
801 || 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) //field
811 {
812 Symbol sym = (RT.third(form) instanceof Keyword)?
813 ((Keyword)RT.third(form)).sym
814 :(Symbol) RT.third(form);
815 Symbol tag = tagOf(form);
816 if(c != null) {
817 return new StaticFieldExpr(line, c, munge(sym.name), tag);
818 } else
819 return new InstanceFieldExpr(line, instance, munge(sym.name), tag);
820 }
821 else
822 {
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 else
834 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 classname
847 {
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 else
853 {
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 classname
872 {
873 if(sym.name.indexOf('.') > 0 || sym.name.charAt(0) == '[')
874 className = sym.name;
875 else
876 {
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 classname
893 {
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 else
967 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 else
985 {
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();
1002 public Object evalAssign(Expr val) throws Exception{
1003 return Reflector.setInstanceField(target.eval(), fieldName, val.eval());
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)
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()));
1018 else
1020 target.emit(C.EXPRESSION, objx, gen);
1021 gen.push(fieldName);
1022 val.emit(C.EXPRESSION, objx, gen);
1023 gen.invokeStatic(REFLECTOR_TYPE, setInstanceFieldMethod);
1025 if(context == C.STATEMENT)
1026 gen.pop();
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;
1050 public Object eval() throws Exception{
1051 return Reflector.getStaticField(c, fieldName);
1054 public boolean canEmitPrimitive(){
1055 return Util.isPrimitive(field.getType());
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()));
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)
1071 gen.pop();
1073 // gen.push(className);
1074 // gen.push(fieldName);
1075 // gen.invokeStatic(REFLECTOR_TYPE, getStaticFieldMethod);
1078 public boolean hasJavaClass(){
1079 return true;
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();
1088 public Object evalAssign(Expr val) throws Exception{
1089 return Reflector.setStaticField(c, fieldName, val.eval());
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();
1106 static Class maybePrimitiveType(Expr e){
1107 try
1109 if(e instanceof MaybePrimitiveExpr && e.hasJavaClass() && ((MaybePrimitiveExpr)e).canEmitPrimitive())
1111 Class c = e.getJavaClass();
1112 if(Util.isPrimitive(c))
1113 return c;
1116 catch(Exception ex)
1118 throw new RuntimeException(ex);
1120 return null;
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++)
1129 gen.dup();
1130 gen.push(i);
1131 ((Expr) args.nth(i)).emit(C.EXPRESSION, objx, gen);
1132 gen.arrayStore(OBJECT_TYPE);
1136 public static void emitTypedArgs(ObjExpr objx, GeneratorAdapter gen, Class[] parameterTypes, IPersistentVector args){
1137 for(int i = 0; i < parameterTypes.length; i++)
1139 Expr e = (Expr) args.nth(i);
1140 try
1142 if(maybePrimitiveType(e) == parameterTypes[i])
1144 ((MaybePrimitiveExpr) e).emitUnboxed(C.EXPRESSION, objx, gen);
1146 else
1148 e.emit(C.EXPRESSION, objx, gen);
1149 HostExpr.emitUnboxArg(objx, gen, parameterTypes[i]);
1152 catch(Exception e1)
1154 e1.printStackTrace(RT.errPrintWriter());
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)
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 else
1190 int methodidx = 0;
1191 if(methods.size() > 1)
1193 ArrayList<Class[]> params = new ArrayList();
1194 ArrayList<Class> rets = new ArrayList();
1195 for(int i = 0; i < methods.size(); i++)
1197 java.lang.reflect.Method m = (java.lang.reflect.Method) methods.get(i);
1198 params.add(m.getParameterTypes());
1199 rets.add(m.getReturnType());
1201 methodidx = getMatchingParams(methodName, params, args, rets);
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()))
1207 //public method of non-public class, try to find it in hierarchy
1208 m = Reflector.getAsMethodOfPublicBase(m.getDeclaringClass(), m);
1210 method = m;
1213 else
1214 method = null;
1216 if(method == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
1218 RT.errPrintWriter()
1219 .format("Reflection warning, %s:%d - call to %s can't be resolved.\n",
1220 SOURCE_PATH.deref(), line, methodName);
1224 public Object eval() throws Exception{
1225 try
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)
1233 LinkedList ms = new LinkedList();
1234 ms.add(method);
1235 return Reflector.invokeMatchingMethod(methodName, ms, targetval, argvals);
1237 return Reflector.invokeInstanceMethod(targetval, methodName, argvals);
1239 catch(Throwable e)
1241 if(!(e instanceof CompilerException))
1242 throw new CompilerException(source, line, e);
1243 else
1244 throw (CompilerException) e;
1248 public boolean canEmitPrimitive(){
1249 return method != null && Util.isPrimitive(method.getReturnType());
1252 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){
1253 gen.visitLineNumber(line, gen.mark());
1254 if(method != null)
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)
1263 ObjMethod method = (ObjMethod) METHOD.deref();
1264 method.emitClearLocals(gen);
1266 Method m = new Method(methodName, Type.getReturnType(method), Type.getArgumentTypes(method));
1267 if(method.getDeclaringClass().isInterface())
1268 gen.invokeInterface(type, m);
1269 else
1270 gen.invokeVirtual(type, m);
1272 else
1273 throw new UnsupportedOperationException("Unboxed emit of unknown member");
1276 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
1277 gen.visitLineNumber(line, gen.mark());
1278 if(method != null)
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)
1287 ObjMethod method = (ObjMethod) METHOD.deref();
1288 method.emitClearLocals(gen);
1290 Method m = new Method(methodName, Type.getReturnType(method), Type.getArgumentTypes(method));
1291 if(method.getDeclaringClass().isInterface())
1292 gen.invokeInterface(type, m);
1293 else
1294 gen.invokeVirtual(type, m);
1295 //if(context != C.STATEMENT || method.getReturnType() == Void.TYPE)
1296 HostExpr.emitBoxReturn(objx, gen, method.getReturnType());
1298 else
1300 target.emit(C.EXPRESSION, objx, gen);
1301 gen.push(methodName);
1302 emitArgsAsArray(args, objx, gen);
1303 if(context == C.RETURN)
1305 ObjMethod method = (ObjMethod) METHOD.deref();
1306 method.emitClearLocals(gen);
1308 gen.invokeStatic(REFLECTOR_TYPE, invokeInstanceMethodMethod);
1310 if(context == C.STATEMENT)
1311 gen.pop();
1314 public boolean hasJavaClass(){
1315 return method != null || tag != null;
1318 public Class getJavaClass() throws Exception{
1319 return tag != null ? HostExpr.tagToClass(tag) : method.getReturnType();
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)
1354 ArrayList<Class[]> params = new ArrayList();
1355 ArrayList<Class> rets = new ArrayList();
1356 for(int i = 0; i < methods.size(); i++)
1358 java.lang.reflect.Method m = (java.lang.reflect.Method) methods.get(i);
1359 params.add(m.getParameterTypes());
1360 rets.add(m.getReturnType());
1362 methodidx = getMatchingParams(methodName, params, args, rets);
1364 method = (java.lang.reflect.Method) (methodidx >= 0 ? methods.get(methodidx) : null);
1365 if(method == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
1367 RT.errPrintWriter()
1368 .format("Reflection warning, %s:%d - call to %s can't be resolved.\n",
1369 SOURCE_PATH.deref(), line, methodName);
1373 public Object eval() throws Exception{
1374 try
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)
1381 LinkedList ms = new LinkedList();
1382 ms.add(method);
1383 return Reflector.invokeMatchingMethod(methodName, ms, null, argvals);
1385 return Reflector.invokeStaticMethod(c, methodName, argvals);
1387 catch(Throwable e)
1389 if(!(e instanceof CompilerException))
1390 throw new CompilerException(source, line, e);
1391 else
1392 throw (CompilerException) e;
1396 public boolean canEmitPrimitive(){
1397 return method != null && Util.isPrimitive(method.getReturnType());
1400 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){
1401 gen.visitLineNumber(line, gen.mark());
1402 if(method != null)
1404 MethodExpr.emitTypedArgs(objx, gen, method.getParameterTypes(), args);
1405 //Type type = Type.getObjectType(className.replace('.', '/'));
1406 if(context == C.RETURN)
1408 ObjMethod method = (ObjMethod) METHOD.deref();
1409 method.emitClearLocals(gen);
1411 Type type = Type.getType(c);
1412 Method m = new Method(methodName, Type.getReturnType(method), Type.getArgumentTypes(method));
1413 gen.invokeStatic(type, m);
1415 else
1416 throw new UnsupportedOperationException("Unboxed emit of unknown member");
1419 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
1420 gen.visitLineNumber(line, gen.mark());
1421 if(method != null)
1423 MethodExpr.emitTypedArgs(objx, gen, method.getParameterTypes(), args);
1424 //Type type = Type.getObjectType(className.replace('.', '/'));
1425 if(context == C.RETURN)
1427 ObjMethod method = (ObjMethod) METHOD.deref();
1428 method.emitClearLocals(gen);
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());
1436 else
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)
1444 ObjMethod method = (ObjMethod) METHOD.deref();
1445 method.emitClearLocals(gen);
1447 gen.invokeStatic(REFLECTOR_TYPE, invokeStaticMethodMethod);
1449 if(context == C.STATEMENT)
1450 gen.pop();
1453 public boolean hasJavaClass(){
1454 return method != null || tag != null;
1457 public Class getJavaClass() throws Exception{
1458 return tag != null ? HostExpr.tagToClass(tag) : method.getReturnType();
1462 static class UnresolvedVarExpr implements Expr{
1463 public final Symbol symbol;
1465 public UnresolvedVarExpr(Symbol symbol){
1466 this.symbol = symbol;
1469 public boolean hasJavaClass(){
1470 return false;
1473 public Class getJavaClass() throws Exception{
1474 throw new IllegalArgumentException(
1475 "UnresolvedVarExpr has no Java class");
1478 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
1481 public Object eval() throws Exception{
1482 throw new IllegalArgumentException(
1483 "UnresolvedVarExpr cannot be evalled");
1487 static class ConstantExpr extends LiteralExpr{
1488 //stuff quoted vals in classloader at compile time, pull out at runtime
1489 //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);
1501 Object val(){
1502 return v;
1505 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
1506 objx.emitConstant(gen, id);
1507 if(context == C.STATEMENT)
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);
1519 public boolean hasJavaClass(){
1520 return Modifier.isPublic(v.getClass().getModifiers());
1521 //return false;
1524 public Class getJavaClass() throws Exception{
1525 return v.getClass();
1526 //throw new IllegalArgumentException("Has no Java class");
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 else
1547 return new ConstantExpr(v);
1552 static class NilExpr extends LiteralExpr{
1553 Object val(){
1554 return null;
1557 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
1558 gen.visitInsn(Opcodes.ACONST_NULL);
1559 if(context == C.STATEMENT)
1560 gen.pop();
1563 public boolean hasJavaClass(){
1564 return true;
1567 public Class getJavaClass() throws Exception{
1568 return null;
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;
1582 Object val(){
1583 return val ? RT.T : RT.F;
1586 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
1587 if(val)
1588 gen.getStatic(BOOLEAN_OBJECT_TYPE, "TRUE", BOOLEAN_OBJECT_TYPE);
1589 else
1590 gen.getStatic(BOOLEAN_OBJECT_TYPE, "FALSE", BOOLEAN_OBJECT_TYPE);
1591 if(context == C.STATEMENT)
1593 gen.pop();
1597 public boolean hasJavaClass(){
1598 return true;
1601 public Class getJavaClass() throws Exception{
1602 return Boolean.class;
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;
1616 Object val(){
1617 return str;
1620 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
1621 if(context != C.STATEMENT)
1622 gen.push(str);
1625 public boolean hasJavaClass(){
1626 return true;
1629 public Class getJavaClass() throws Exception{
1630 return String.class;
1635 static class MonitorEnterExpr extends UntypedExpr{
1636 final Expr target;
1638 public MonitorEnterExpr(Expr target){
1639 this.target = target;
1642 public Object eval() throws Exception{
1643 throw new UnsupportedOperationException("Can't eval monitor-enter");
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);
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)));
1659 static class MonitorExitExpr extends UntypedExpr{
1660 final Expr target;
1662 public MonitorExitExpr(Expr target){
1663 this.target = target;
1666 public Object eval() throws Exception{
1667 throw new UnsupportedOperationException("Can't eval monitor-exit");
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);
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)));
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;
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;
1715 public Object eval() throws Exception{
1716 throw new UnsupportedOperationException("Can't eval try");
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++)
1727 CatchClause clause = (CatchClause) catchExprs.nth(i);
1728 clause.label = gen.newLabel();
1729 clause.endLabel = gen.newLabel();
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++)
1743 CatchClause clause = (CatchClause) catchExprs.nth(i);
1744 gen.mark(clause.label);
1745 //exception should be on stack
1746 //put in clause local
1747 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);
1757 if(finallyExpr != null)
1759 gen.mark(finallyLabel);
1760 //exception should be on stack
1761 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();
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++)
1772 CatchClause clause = (CatchClause) catchExprs.nth(i);
1773 gen.visitTryCatchBlock(startTry, endTry, clause.label, clause.c.getName().replace('.', '/'));
1775 if(finallyExpr != null)
1777 gen.visitTryCatchBlock(startTry, endTry, finallyLabel, null);
1778 for(int i = 0; i < catchExprs.count(); i++)
1780 CatchClause clause = (CatchClause) catchExprs.nth(i);
1781 gen.visitTryCatchBlock(clause.label, clause.endLabel, finallyLabel, null);
1784 for(int i = 0; i < catchExprs.count(); i++)
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);
1792 public boolean hasJavaClass() throws Exception{
1793 return tryExpr.hasJavaClass();
1796 public Class getJavaClass() throws Exception{
1797 return tryExpr.getJavaClass();
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())
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))
1826 if(caught)
1827 throw new Exception("Only catch or finally clause can follow catch in try expression");
1828 body = body.cons(f);
1830 else
1832 if(bodyExpr == null)
1833 bodyExpr = (new BodyExpr.Parser()).parse(context, RT.seq(body));
1834 if(Util.equals(op, CATCH))
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 try
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));
1859 finally
1861 Var.popThreadBindings();
1863 caught = true;
1865 else //finally
1867 if(fs.next() != null)
1868 throw new Exception("finally clause must be last in try expression");
1869 try
1871 Var.pushThreadBindings(RT.map(IN_CATCH_FINALLY, RT.T));
1872 finallyExpr = (new BodyExpr.Parser()).parse(C.STATEMENT, RT.next(f));
1874 finally
1876 Var.popThreadBindings();
1881 if(bodyExpr == null)
1882 bodyExpr = (new BodyExpr.Parser()).parse(context, RT.seq(body));
1884 return new TryExpr(bodyExpr, catches, finallyExpr, retLocal,
1885 finallyLocal);
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 stack
1917 // 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;
1955 public Object eval() throws Exception{
1956 throw new Exception("Can't eval throw");
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();
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)));
1975 static public boolean subsumes(Class[] c1, Class[] c2){
1976 //presumes matching lengths
1977 Boolean better = false;
1978 for(int i = 0; i < c1.length; i++)
1980 if(c1[i] != c2[i])// || c2[i].isPrimitive() && c1[i] == Object.class))
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 else
1988 return false;
1991 return better;
1994 static int getMatchingParams(String methodName, ArrayList<Class[]> paramlists, IPersistentVector argexprs,
1995 List<Class> rets)
1996 throws Exception{
1997 //presumes matching lengths
1998 int matchIdx = -1;
1999 boolean tied = false;
2000 boolean foundExact = false;
2001 for(int i = 0; i < paramlists.size(); i++)
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())
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 else
2014 match = Reflector.paramArgTypeMatch(pclass, aclass);
2016 if(exact == argexprs.count())
2018 if(!foundExact || matchIdx == -1 || rets.get(matchIdx).isAssignableFrom(rets.get(i)))
2019 matchIdx = i;
2020 foundExact = true;
2022 else if(match && !foundExact)
2024 if(matchIdx == -1)
2025 matchIdx = i;
2026 else
2028 if(subsumes(paramlists.get(i), paramlists.get(matchIdx)))
2030 matchIdx = i;
2031 tied = false;
2033 else if(Arrays.equals(paramlists.get(matchIdx), paramlists.get(i)))
2035 if(rets.get(matchIdx).isAssignableFrom(rets.get(i)))
2036 matchIdx = i;
2038 else if(!(subsumes(paramlists.get(matchIdx), paramlists.get(i))))
2039 tied = true;
2043 if(tied)
2044 throw new IllegalArgumentException("More than one matching method found: " + methodName);
2046 return matchIdx;
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++)
2068 Constructor ctor = allctors[i];
2069 if(ctor.getParameterTypes().length == args.count())
2071 ctors.add(ctor);
2072 params.add(ctor.getParameterTypes());
2073 rets.add(c);
2076 if(ctors.isEmpty())
2077 throw new IllegalArgumentException("No matching ctor found for " + c);
2079 int ctoridx = 0;
2080 if(ctors.size() > 1)
2082 ctoridx = getMatchingParams(c.getName(), params, args, rets);
2085 this.ctor = ctoridx >= 0 ? (Constructor) ctors.get(ctoridx) : null;
2086 if(ctor == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
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());
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)
2100 return ctor.newInstance(Reflector.boxArgs(ctor.getParameterTypes(), argvals));
2102 return Reflector.invokeConstructor(c, argvals);
2105 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
2106 if(this.ctor != null)
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)
2114 ObjMethod method = (ObjMethod) METHOD.deref();
2115 method.emitClearLocals(gen);
2117 gen.invokeConstructor(type, new Method("<init>", Type.getConstructorDescriptor(ctor)));
2119 else
2121 gen.push(destubClassName(c.getName()));
2122 gen.invokeStatic(CLASS_TYPE, forNameMethod);
2123 MethodExpr.emitArgsAsArray(args, objx, gen);
2124 if(context == C.RETURN)
2126 ObjMethod method = (ObjMethod) METHOD.deref();
2127 method.emitClearLocals(gen);
2129 gen.invokeStatic(REFLECTOR_TYPE, invokeConstructorMethod);
2131 if(context == C.STATEMENT)
2132 gen.pop();
2135 public boolean hasJavaClass(){
2136 return true;
2139 public Class getJavaClass() throws Exception{
2140 return c;
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);
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;
2174 public Object eval() throws Exception{
2175 return ((IObj) expr.eval()).withMeta((IPersistentMap) meta.eval());
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)
2186 gen.pop();
2190 public boolean hasJavaClass() throws Exception{
2191 return expr.hasJavaClass();
2194 public Class getJavaClass() throws Exception{
2195 return expr.getJavaClass();
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;
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();
2220 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
2221 doEmit(context, objx, gen,false);
2224 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){
2225 doEmit(context, objx, gen, true);
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 try
2237 if(maybePrimitiveType(testExpr) == boolean.class)
2239 ((MaybePrimitiveExpr) testExpr).emitUnboxed(C.EXPRESSION, objx, gen);
2240 gen.ifZCmp(gen.EQ, falseLabel);
2242 else
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);
2251 catch(Exception e)
2253 throw new RuntimeException(e);
2255 if(emitUnboxed)
2256 ((MaybePrimitiveExpr)thenExpr).emitUnboxed(context, objx, gen);
2257 else
2258 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 else
2266 elseExpr.emit(context, objx, gen);
2267 gen.mark(endLabel);
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()));
2279 public boolean canEmitPrimitive(){
2280 try
2282 return thenExpr instanceof MaybePrimitiveExpr
2283 && elseExpr instanceof MaybePrimitiveExpr
2284 && thenExpr.getJavaClass() == elseExpr.getJavaClass()
2285 && ((MaybePrimitiveExpr)thenExpr).canEmitPrimitive()
2286 && ((MaybePrimitiveExpr)elseExpr).canEmitPrimitive();
2288 catch(Exception e)
2290 return false;
2294 public Class getJavaClass() throws Exception{
2295 Class thenClass = thenExpr.getJavaClass();
2296 if(thenClass != null)
2297 return thenClass;
2298 return elseExpr.getJavaClass();
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));
2317 finally{
2318 Var.popThreadBindings();
2320 try {
2321 Var.pushThreadBindings(
2322 RT.map(CLEAR_PATH, new PathNode(PATHTYPE.PATH,branch)));
2323 elseexpr = analyze(context, RT.fourth(form));
2325 finally{
2326 Var.popThreadBindings();
2328 return new IfExpr((Integer) LINE.deref(),
2329 testexpr,
2330 thenexpr,
2331 elseexpr);
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())
2366 String sub = (String) CHAR_MAP.valAt(c);
2367 if(sub != null)
2368 sb.append(sub);
2369 else
2370 sb.append(c);
2372 return sb.toString();
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;
2388 public Object eval() throws Exception{
2389 return coll;
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 else
2402 throw new UnsupportedOperationException("Unknown Collection type");
2403 if(context == C.STATEMENT)
2405 gen.pop();
2409 public boolean hasJavaClass() throws Exception{
2410 return true;
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 else
2423 throw new UnsupportedOperationException("Unknown Collection type");
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;
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();
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();
2450 public boolean hasJavaClass() throws Exception{
2451 return true;
2454 public Class getJavaClass() throws Exception{
2455 return IPersistentList.class;
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;
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);
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();
2483 public boolean hasJavaClass() throws Exception{
2484 return true;
2487 public Class getJavaClass() throws Exception{
2488 return IPersistentMap.class;
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())
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()));
2500 Expr ret = new MapExpr(keyvals);
2501 if(form instanceof IObj && ((IObj) form).meta() != null)
2502 return new MetaExpr(ret, (MapExpr) MapExpr
2503 .parse(context == C.EVAL ? context : C.EXPRESSION, ((IObj) form).meta()));
2504 else
2505 return ret;
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;
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);
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();
2532 public boolean hasJavaClass() throws Exception{
2533 return true;
2536 public Class getJavaClass() throws Exception{
2537 return IPersistentSet.class;
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())
2545 Object e = s.first();
2546 keys = (IPersistentVector) keys.cons(analyze(context == C.EVAL ? context : C.EXPRESSION, e));
2548 Expr ret = new SetExpr(keys);
2549 if(form instanceof IObj && ((IObj) form).meta() != null)
2550 return new MetaExpr(ret, (MapExpr) MapExpr
2551 .parse(context == C.EVAL ? context : C.EXPRESSION, ((IObj) form).meta()));
2552 else
2553 return ret;
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;
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;
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();
2580 public boolean hasJavaClass() throws Exception{
2581 return true;
2584 public Class getJavaClass() throws Exception{
2585 return IPersistentVector.class;
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) MapExpr
2595 .parse(context == C.EVAL ? context : C.EXPRESSION, ((IObj) form).meta()));
2596 else
2597 return ret;
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);
2620 public Object eval() throws Exception{
2621 try
2623 return kw.k.invoke(target.eval());
2625 catch(Throwable e)
2627 if(!(e instanceof CompilerException))
2628 throw new CompilerException(source, line, e);
2629 else
2630 throw (CompilerException) e;
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();
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();
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();
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();
2734 public boolean hasJavaClass() throws Exception{
2735 return tag != null;
2738 public Class getJavaClass() throws Exception{
2739 return HostExpr.tagToClass(tag);
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 // try
2760 // {
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 // else
2769 // 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;
2805 public Object eval() throws Exception{
2806 if(c.isInstance(expr.eval()))
2807 return RT.T;
2808 return RT.F;
2811 public boolean canEmitPrimitive(){
2812 return true;
2815 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){
2816 expr.emit(C.EXPRESSION,objx,gen);
2817 gen.instanceOf(Type.getType(c));
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();
2827 public boolean hasJavaClass() throws Exception{
2828 return true;
2831 public Class getJavaClass() throws Exception{
2832 return Boolean.TYPE;
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)
2859 Var fvar = ((VarExpr)fexpr).var;
2860 Var pvar = (Var)RT.get(fvar.meta(), protocolKey);
2861 if(pvar != null && PROTOCOL_CALLSITES.isBound())
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)
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.)");
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);
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 these
2892 // this.isDirect = true;
2893 // this.siteIndex = registerVarCallsite(((VarExpr) fexpr).var);
2894 // }
2896 this.tag = tag != null ? tag : (fexpr instanceof VarExpr ? ((VarExpr) fexpr).tag : null);
2899 public Object eval() throws Exception{
2900 try
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));
2908 catch(Throwable e)
2910 if(!(e instanceof CompilerException))
2911 throw new CompilerException(source, line, e);
2912 else
2913 throw (CompilerException) e;
2917 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
2918 gen.visitLineNumber(line, gen.mark());
2919 if(isProtocol)
2921 emitProto(context,objx,gen);
2923 else if(isDirect)
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);
2940 else
2942 fexpr.emit(C.EXPRESSION, objx, gen);
2943 gen.checkCast(IFN_TYPE);
2944 emitArgsAndCall(0, context,objx,gen);
2946 if(context == C.STATEMENT)
2947 gen.pop();
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, target
2960 gen.invokeStatic(UTIL_TYPE,Method.getMethod("Class classOf(Object)")); //target,class
2961 gen.loadThis();
2962 gen.getField(objx.objtype, objx.cachedClassName(siteIndex),CLASS_TYPE); //target,class,cached-class
2963 gen.visitJumpInsn(IF_ACMPEQ, callLabel); //target
2964 if(protocolOn != null)
2966 gen.dup(); //target, target
2967 gen.instanceOf(Type.getType(protocolOn));
2968 gen.ifZCmp(GeneratorAdapter.NE, onLabel);
2971 gen.mark(callLabel); //target
2972 gen.dup(); //target, target
2973 gen.invokeStatic(UTIL_TYPE,Method.getMethod("Class classOf(Object)")); //target,class
2974 gen.loadThis();
2975 gen.swap();
2976 gen.putField(objx.objtype, objx.cachedClassName(siteIndex),CLASS_TYPE); //target
2977 objx.emitVar(gen, v);
2978 gen.invokeVirtual(VAR_TYPE, Method.getMethod("Object getRawRoot()")); //target, proto-fn
2979 gen.swap();
2980 emitArgsAndCall(1, context,objx,gen);
2981 gen.goTo(endLabel);
2983 gen.mark(onLabel); //target
2984 if(protocolOn != null)
2986 MethodExpr.emitTypedArgs(objx, gen, onMethod.getParameterTypes(), RT.subvec(args,1,args.count()));
2987 if(context == C.RETURN)
2989 ObjMethod method = (ObjMethod) METHOD.deref();
2990 method.emitClearLocals(gen);
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());
2996 gen.mark(endLabel);
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++)
3002 Expr e = (Expr) args.nth(i);
3003 e.emit(C.EXPRESSION, objx, gen);
3005 if(args.count() > MAX_POSITIONAL_ARITY)
3007 PersistentVector restArgs = PersistentVector.EMPTY;
3008 for(int i = MAX_POSITIONAL_ARITY; i < args.count(); i++)
3010 restArgs = restArgs.cons(args.nth(i));
3012 MethodExpr.emitArgsAsArray(restArgs, objx, gen);
3015 if(context == C.RETURN)
3017 ObjMethod method = (ObjMethod) METHOD.deref();
3018 method.emitClearLocals(gen);
3021 gen.invokeInterface(IFN_TYPE, new Method("invoke", OBJECT_TYPE, ARG_TYPES[Math.min(MAX_POSITIONAL_ARITY + 1,
3022 args.count())]));
3025 public boolean hasJavaClass() throws Exception{
3026 return tag != null;
3029 public Class getJavaClass() throws Exception{
3030 return HostExpr.tagToClass(tag);
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))
3039 if(RT.second(form) instanceof Symbol)
3041 Class c = HostExpr.maybeClass(RT.second(form),false);
3042 if(c != null)
3043 return new InstanceOfExpr(c, analyze(context, RT.third(form)));
3047 if(fexpr instanceof KeywordExpr && RT.count(form) == 2 && KEYWORD_CALLSITES.isBound())
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);
3054 PersistentVector args = PersistentVector.EMPTY;
3055 for(ISeq s = RT.seq(form.next()); s != null; s = s.next())
3057 args = args.cons(analyze(context, s.first()));
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);
3067 static class SourceDebugExtensionAttribute extends Attribute{
3068 public SourceDebugExtensionAttribute(){
3069 super("SourceDebugExtension");
3072 void writeSMAP(ClassWriter cw, String smap){
3073 ByteVector bv = write(cw, null, -1, -1, -1);
3074 bv.putUTF8(smap);
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 here
3082 FnMethod variadicMethod = null;
3083 IPersistentCollection methods;
3084 // String superName = null;
3086 public FnExpr(Object tag){
3087 super(tag);
3090 public boolean hasJavaClass() throws Exception{
3091 return true;
3094 public Class getJavaClass() throws Exception{
3095 return AFunction.class;
3098 protected void emitMethods(ClassVisitor cv){
3099 //override of invoke/doInvoke for each method
3100 for(ISeq s = RT.seq(methods); s != null; s = s.next())
3102 ObjMethod method = (ObjMethod) s.first();
3103 method.emit(this, cv);
3106 if(isVariadic())
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();
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)
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"));
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 try
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.EMPTY
3155 ));
3157 //arglist might be preceded by symbol naming this fn
3158 if(RT.second(form) instanceof Symbol)
3160 fn.thisName = ((Symbol) RT.second(form)).name;
3161 form = RT.cons(FN, RT.next(RT.next(form)));
3164 //now (fn [args] body...) or (fn ([args] body...) ([args2] body2...) ...)
3165 //turn former into latter
3166 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))
3173 FnMethod f = FnMethod.parse(fn, (ISeq) RT.first(s));
3174 if(f.isVariadic())
3176 if(variadicMethod == null)
3177 variadicMethod = f;
3178 else
3179 throw new Exception("Can't have more than 1 variadic overload");
3181 else if(methodArray[f.reqParms.count()] == null)
3182 methodArray[f.reqParms.count()] = f;
3183 else
3184 throw new Exception("Can't have 2 overloads with same arity");
3186 if(variadicMethod != null)
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");
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());
3214 finally
3216 Var.popThreadBindings();
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) MapExpr
3223 .parse(context == C.EVAL ? context : C.EXPRESSION, ((IObj) origForm).meta()));
3224 else
3225 return fn;
3228 public final ObjMethod variadicMethod(){
3229 return variadicMethod;
3232 boolean isVariadic(){
3233 return variadicMethod != null;
3236 public final IPersistentCollection methods(){
3237 return methods;
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->itself
3250 IPersistentMap closes = PersistentHashMap.EMPTY;
3251 //localbndingexprs
3252 IPersistentVector closesExprs = PersistentVector.EMPTY;
3253 //symbols
3254 IPersistentSet volatiles = PersistentHashSet.EMPTY;
3256 //symbol->lb
3257 IPersistentMap fields = null;
3259 //Keyword->KeywordExpr
3260 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;
3282 // public final String simpleName(){
3283 // return simpleName;
3284 // }
3286 public final String internalName(){
3287 return internalName;
3290 public final String thisName(){
3291 return thisName;
3294 public final Type objtype(){
3295 return objtype;
3298 public final IPersistentMap closes(){
3299 return closes;
3302 public final IPersistentMap keywords(){
3303 return keywords;
3306 public final IPersistentMap vars(){
3307 return vars;
3310 public final Class compiledClass(){
3311 return compiledClass;
3314 public final int line(){
3315 return line;
3318 public final PersistentVector constants(){
3319 return constants;
3322 public final int constantsID(){
3323 return constantsID;
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;
3348 static String trimGenID(String name){
3349 int i = name.lastIndexOf("__");
3350 return i==-1?name:name.substring(0,i);
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())
3359 LocalBinding lb = (LocalBinding) s.first();
3360 if(lb.getPrimitiveType() != null)
3361 tv = tv.cons(Type.getType(lb.getPrimitiveType()));
3362 else
3363 tv = tv.cons(OBJECT_TYPE);
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;
3371 void compile(String superName, String[] interfaceNames, boolean oneTimeUse) throws Exception{
3372 //create bytecode for a class
3373 //with name current_ns.defname[$letname]+
3374 //anonymous fns get names fn__id
3375 //derived from AFn/RestFn
3376 if(keywordCallsites.count() > 0)
3378 if(interfaceNames == null)
3379 interfaceNames = new String[]{"clojure/lang/ILookupHost"};
3380 else
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;
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)
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);
3419 addAnnotation(cv, classMeta);
3420 //static fields for constants
3421 for(int i = 0; i < constants.count(); i++)
3423 cv.visitField(ACC_PUBLIC + ACC_FINAL
3424 + ACC_STATIC, constantName(i), constantType(i).getDescriptor(),
3425 null, null);
3428 //static fields for lookup sites
3429 for(int i = 0; i < keywordCallsites.count(); i++)
3431 cv.visitField(ACC_FINAL
3432 + 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);
3438 for(int i=0;i<varCallsites.count();i++)
3440 cv.visitField(ACC_PRIVATE + ACC_STATIC + ACC_FINAL
3441 , varCallsiteName(i), IFN_TYPE.getDescriptor(), null, null);
3444 //static init for constants, keywords and vars
3445 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)
3455 emitConstants(clinitgen);
3458 if(keywordCallsites.count() > 0)
3459 emitKeywordCallsites(clinitgen);
3461 for(int i=0;i<varCallsites.count();i++)
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);
3487 clinitgen.returnValue();
3489 clinitgen.endMethod();
3490 if(!isDeftype())
3492 cv.visitField(ACC_FINAL, "__meta", IPERSISTENTMAP_TYPE.getDescriptor(), null, null);
3494 //instance fields for closed-overs
3495 for(ISeq s = RT.keys(closes); s != null; s = s.next())
3497 LocalBinding lb = (LocalBinding) s.first();
3498 if(isDeftype())
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(access
3506 , lb.name, Type.getType(lb.getPrimitiveType()).getDescriptor(),
3507 null, null);
3508 else
3509 //todo - when closed-overs are fields, use more specific types here and in ctor and emitLocal?
3510 fv = cv.visitField(access
3511 , lb.name, OBJECT_TYPE.getDescriptor(), null, null);
3512 addAnnotation(fv, RT.meta(lb.sym));
3514 else
3516 //todo - only enable this non-private+writability for letfns where we need it
3517 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 else
3522 cv.visitField(0 //+ (oneTimeUse ? 0 : ACC_FINAL)
3523 , lb.name, OBJECT_TYPE.getDescriptor(), null, null);
3527 //instance fields for callsites and thunks
3528 for(int i=0;i<protocolCallsites.count();i++)
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);
3535 //ctor that takes closed-overs and inits base + fields
3536 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 arg
3551 // {
3552 // ctorgen.push(variadicMethod.reqParms.count());
3553 // ctorgen.invokeConstructor(restFnType, restfnctor);
3554 // }
3555 // else
3556 // ctorgen.invokeConstructor(aFnType, voidctor);
3557 if(!isDeftype())
3559 ctorgen.loadThis();
3560 ctorgen.visitVarInsn(IPERSISTENTMAP_TYPE.getOpcode(Opcodes.ILOAD), 1);
3561 ctorgen.putField(objtype, "__meta", IPERSISTENTMAP_TYPE);
3564 int a = isDeftype()?1:2;
3565 for(ISeq s = RT.keys(closes); s != null; s = s.next(), ++a)
3567 LocalBinding lb = (LocalBinding) s.first();
3568 ctorgen.loadThis();
3569 Class primc = lb.getPrimitiveType();
3570 if(primc != null)
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;
3577 else
3579 ctorgen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), a);
3580 ctorgen.putField(objtype, lb.name, OBJECT_TYPE);
3582 closesExprs = closesExprs.cons(new LocalBindingExpr(lb, null));
3586 ctorgen.visitLabel(end);
3588 ctorgen.returnValue();
3590 ctorgen.endMethod();
3592 if(altCtorDrops > 0)
3594 //ctor that takes closed-overs and inits base + fields
3595 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();
3617 if(!isDeftype())
3619 //ctor that takes closed-overs but not meta
3620 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 meta
3633 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)
3669 LocalBinding lb = (LocalBinding) s.first();
3670 gen.loadThis();
3671 Class primc = lb.getPrimitiveType();
3672 if(primc != null)
3674 gen.getField(objtype, lb.name, Type.getType(primc));
3676 else
3678 gen.getField(objtype, lb.name, OBJECT_TYPE);
3682 gen.invokeConstructor(objtype, new Method("<init>", Type.VOID_TYPE, ctorTypes));
3683 gen.returnValue();
3684 gen.endMethod();
3687 emitMethods(cv);
3689 if(keywordCallsites.count() > 0)
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++)
3704 labels[i] = gen.newLabel();
3706 gen.loadArg(0);
3707 gen.visitTableSwitchInsn(0,keywordCallsites.count()-1,endLabel,labels);
3709 for(int i = 0; i < keywordCallsites.count();i++)
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);
3718 gen.mark(endLabel);
3720 gen.returnValue();
3721 gen.endMethod();
3724 //end of class
3725 cv.visitEnd();
3727 bytecode = cw.toByteArray();
3728 if(RT.booleanCast(COMPILE_FILES.deref()))
3729 writeClassFile(internalName, bytecode);
3730 // else
3731 // getCompiledClass();
3734 private void emitKeywordCallsites(GeneratorAdapter clinitgen){
3735 for(int i=0;i<keywordCallsites.count();i++)
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);
3750 protected void emitMethods(ClassVisitor gen){
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++)
3759 gen.dup();
3760 gen.push(i);
3761 emitValue(it.next(), gen);
3762 gen.arrayStore(OBJECT_TYPE);
3766 void emitValue(Object value, GeneratorAdapter gen){
3767 boolean partial = true;
3768 //System.out.println(value.getClass().toString());
3770 if(value instanceof String)
3772 gen.push((String) value);
3774 else if(value instanceof Integer)
3776 gen.push(((Integer) value).intValue());
3777 gen.invokeStatic(Type.getType(Integer.class), Method.getMethod("Integer valueOf(int)"));
3779 else if(value instanceof Double)
3781 gen.push(((Double) value).doubleValue());
3782 gen.invokeStatic(Type.getType(Double.class), Method.getMethod("Double valueOf(double)"));
3784 else if(value instanceof Character)
3786 gen.push(((Character) value).charValue());
3787 gen.invokeStatic(Type.getType(Character.class), Method.getMethod("Character valueOf(char)"));
3789 else if(value instanceof Class)
3791 Class cc = (Class)value;
3792 if(cc.isPrimitive())
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) );
3807 else
3809 gen.push(destubClassName(cc.getName()));
3810 gen.invokeStatic(Type.getType(Class.class), Method.getMethod("Class forName(String)"));
3813 else if(value instanceof Symbol)
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)"));
3820 else if(value instanceof Keyword)
3822 emitValue(((Keyword) value).sym, gen);
3823 gen.invokeStatic(Type.getType(Keyword.class),
3824 Method.getMethod("clojure.lang.Keyword intern(clojure.lang.Symbol)"));
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)
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)"));
3839 else if(value instanceof IPersistentMap)
3841 List entries = new ArrayList();
3842 for(Map.Entry entry : (Set<Map.Entry>) ((Map) value).entrySet())
3844 entries.add(entry.getKey());
3845 entries.add(entry.getValue());
3847 emitListAsObjectArray(entries, gen);
3848 gen.invokeStatic(RT_TYPE,
3849 Method.getMethod("clojure.lang.IPersistentMap map(Object[])"));
3851 else if(value instanceof IPersistentVector)
3853 emitListAsObjectArray(value, gen);
3854 gen.invokeStatic(RT_TYPE, Method.getMethod(
3855 "clojure.lang.IPersistentVector vector(Object[])"));
3857 else if(value instanceof ISeq || value instanceof IPersistentList)
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)"));
3866 else
3868 String cs = null;
3869 try
3871 cs = RT.printString(value);
3872 //System.out.println("WARNING SLOW CODE: " + value.getClass() + " -> " + cs);
3874 catch(Exception e)
3876 throw new RuntimeException(
3877 "Can't embed object in code, maybe print-dup not defined: " +
3878 value);
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;
3893 if(partial)
3895 if(value instanceof IObj && RT.count(((IObj) value).meta()) > 0)
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)"));
3907 void emitConstants(GeneratorAdapter clinitgen){
3908 try
3910 Var.pushThreadBindings(RT.map(RT.PRINT_DUP, RT.T));
3912 for(int i = 0; i < constants.count(); i++)
3914 emitValue(constants.nth(i), clinitgen);
3915 clinitgen.checkCast(constantType(i));
3916 clinitgen.putStatic(objtype, constantName(i), constantType(i));
3919 finally
3921 Var.popThreadBindings();
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")));
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")));
3936 boolean isDeftype(){
3937 return fields != null;
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 // }
3955 synchronized Class getCompiledClass(){
3956 if(compiledClass == null)
3957 try
3959 // if(RT.booleanCast(COMPILE_FILES.deref()))
3960 // compiledClass = RT.classForName(name);//loader.defineClass(name, bytecode);
3961 // else
3963 loader = (DynamicClassLoader) LOADER.deref();
3964 compiledClass = loader.defineClass(name, bytecode, src);
3967 catch(Exception e)
3969 throw new RuntimeException(e);
3971 return compiledClass;
3974 public Object eval() throws Exception{
3975 if(isDeftype())
3976 return null;
3977 return getCompiledClass().newInstance();
3980 public void emitLetFnInits(GeneratorAdapter gen, ObjExpr objx, IPersistentSet letFnLocals){
3981 //objx arg is enclosing objx, not this
3982 gen.checkCast(objtype);
3984 for(ISeq s = RT.keys(closes); s != null; s = s.next())
3986 LocalBinding lb = (LocalBinding) s.first();
3987 if(letFnLocals.contains(lb))
3989 Class primc = lb.getPrimitiveType();
3990 gen.dup();
3991 if(primc != null)
3993 objx.emitUnboxedLocal(gen, lb);
3994 gen.putField(objtype, lb.name, Type.getType(primc));
3996 else
3998 objx.emitLocal(gen, lb, false);
3999 gen.putField(objtype, lb.name, OBJECT_TYPE);
4003 gen.pop();
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 any
4009 //objx arg is enclosing objx, not this
4010 // getCompiledClass();
4011 if(isDeftype())
4013 gen.visitInsn(Opcodes.ACONST_NULL);
4015 else
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())
4022 LocalBindingExpr lbe = (LocalBindingExpr) s.first();
4023 LocalBinding lb = lbe.b;
4024 if(lb.getPrimitiveType() != null)
4025 objx.emitUnboxedLocal(gen, lb);
4026 else
4027 objx.emitLocal(gen, lb, lbe.shouldClear);
4029 gen.invokeConstructor(objtype, new Method("<init>", Type.VOID_TYPE, ctorTypes()));
4031 if(context == C.STATEMENT)
4032 gen.pop();
4035 public boolean hasJavaClass() throws Exception{
4036 return true;
4039 public Class getJavaClass() throws Exception{
4040 return (compiledClass != null) ? compiledClass
4041 : (tag != null) ? HostExpr.tagToClass(tag)
4042 : IFn.class;
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)
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));
4058 else
4060 val.emit(C.EXPRESSION, this, gen);
4061 gen.putField(objtype, lb.name, OBJECT_TYPE);
4065 private void emitLocal(GeneratorAdapter gen, LocalBinding lb, boolean clear){
4066 if(closes.containsKey(lb))
4068 Class primc = lb.getPrimitiveType();
4069 gen.loadThis();
4070 if(primc != null)
4072 gen.getField(objtype, lb.name, Type.getType(primc));
4073 HostExpr.emitBoxReturn(this, gen, primc);
4075 else
4077 gen.getField(objtype, lb.name, OBJECT_TYPE);
4078 if(onceOnly && clear && lb.canBeCleared)
4080 gen.loadThis();
4081 gen.visitInsn(Opcodes.ACONST_NULL);
4082 gen.putField(objtype, lb.name, OBJECT_TYPE);
4086 else
4088 Class primc = lb.getPrimitiveType();
4089 // String rep = lb.sym.name + " " + lb.toString().substring(lb.toString().lastIndexOf('@'));
4090 if(lb.isArg)
4092 gen.loadArg(lb.idx-1);
4093 if(primc != null)
4094 HostExpr.emitBoxReturn(this, gen, primc);
4095 else
4097 if(clear && lb.canBeCleared)
4099 // System.out.println("clear: " + rep);
4100 gen.visitInsn(Opcodes.ACONST_NULL);
4101 gen.storeArg(lb.idx - 1);
4103 else
4105 // System.out.println("use: " + rep);
4109 else
4111 if(primc != null)
4113 gen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ILOAD), lb.idx);
4114 HostExpr.emitBoxReturn(this, gen, primc);
4116 else
4118 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), lb.idx);
4119 if(clear && lb.canBeCleared)
4121 // System.out.println("clear: " + rep);
4122 gen.visitInsn(Opcodes.ACONST_NULL);
4123 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), lb.idx);
4125 else
4127 // System.out.println("use: " + rep);
4134 private void emitUnboxedLocal(GeneratorAdapter gen, LocalBinding lb){
4135 Class primc = lb.getPrimitiveType();
4136 if(closes.containsKey(lb))
4138 gen.loadThis();
4139 gen.getField(objtype, lb.name, Type.getType(primc));
4141 else if(lb.isArg)
4142 gen.loadArg(lb.idx-1);
4143 else
4144 gen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ILOAD), lb.idx);
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);
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);
4159 public void emitConstant(GeneratorAdapter gen, int id){
4160 gen.getStatic(objtype, constantName(id), constantType(id));
4164 String constantName(int id){
4165 return CONST_PREFIX + id;
4168 String siteName(int n){
4169 return "__site__" + n;
4172 String siteNameStatic(int n){
4173 return siteName(n) + "__";
4176 String thunkName(int n){
4177 return "__thunk__" + n;
4180 String cachedClassName(int n){
4181 return "__cached_class__" + n;
4184 String cachedProtoFnName(int n){
4185 return "__cached_proto_fn__" + n;
4188 String cachedProtoImplName(int n){
4189 return "__cached_proto_impl__" + n;
4192 String varCallsiteName(int n){
4193 return "__var__callsite__" + n;
4196 String thunkNameStatic(int n){
4197 return thunkName(n) + "__";
4200 Type constantType(int id){
4201 Object o = constants.nth(id);
4202 Class c = o.getClass();
4203 if(Modifier.isPublic(c.getModifiers()))
4205 //can't emit derived fn types due to visibility
4206 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);
4223 return OBJECT_TYPE;
4228 enum PATHTYPE {
4229 PATH, BRANCH;
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;
4242 static PathNode clearPathRoot(){
4243 return (PathNode) CLEAR_ROOT.get();
4246 enum PSTATE{
4247 REQ, REST, DONE
4250 public static class FnMethod extends ObjMethod{
4251 //localbinding->localbinding
4252 PersistentVector reqParms = PersistentVector.EMPTY;
4253 LocalBinding restParm = null;
4255 public FnMethod(ObjExpr objx, ObjMethod parent){
4256 super(objx, parent);
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 try
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 frame
4268 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, 0
4277 ,CLEAR_PATH, pnode
4278 ,CLEAR_ROOT, pnode
4279 ,CLEAR_SITES, PersistentHashMap.EMPTY
4280 ));
4282 //register 'this' as local 0
4283 //registerLocal(THISFN, null, null);
4284 if(objx.thisName != null)
4285 registerLocal(Symbol.intern(objx.thisName), null, null,false);
4286 else
4287 getAndIncLocalNum();
4288 PSTATE state = PSTATE.REQ;
4289 PersistentVector argLocals = PersistentVector.EMPTY;
4290 for(int i = 0; i < parms.count(); i++)
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_))
4299 if(state == PSTATE.REQ)
4300 state = PSTATE.REST;
4301 else
4302 throw new Exception("Invalid parameter list");
4305 else
4307 LocalBinding lb = registerLocal(p, state == PSTATE.REST ? ISEQ : tagOf(p), null,true);
4308 argLocals = argLocals.cons(lb);
4309 switch(state)
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");
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;
4331 finally
4333 Var.popThreadBindings();
4337 public final PersistentVector reqParms(){
4338 return reqParms;
4341 public final LocalBinding restParm(){
4342 return restParm;
4345 boolean isVariadic(){
4346 return restParm != null;
4349 int numParams(){
4350 return reqParms.count() + (isVariadic() ? 1 : 0);
4353 String getMethodName(){
4354 return isVariadic()?"doInvoke":"invoke";
4357 Type getReturnType(){
4358 return OBJECT_TYPE;
4361 Type[] getArgTypes(){
4362 if(isVariadic() && reqParms.count() == MAX_POSITIONAL_ARITY)
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;
4369 return ARG_TYPES[numParams()];
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 // }
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 objx
4403 public final ObjMethod parent;
4404 //localbinding->localbinding
4405 IPersistentMap locals = null;
4406 //num->localbinding
4407 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;
4420 public final Expr body(){
4421 return body;
4424 public final ObjExpr objx(){
4425 return objx;
4428 public final PersistentVector argLocals(){
4429 return argLocals;
4432 public final int maxLocal(){
4433 return maxLocal;
4436 public final int line(){
4437 return line;
4440 public ObjMethod(ObjExpr objx, ObjMethod parent){
4441 this.parent = parent;
4442 this.objx = objx;
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 this
4457 EXCEPTION_TYPES,
4458 cv);
4459 gen.visitCode();
4460 Label loopLabel = gen.mark();
4461 gen.visitLineNumber(line, loopLabel);
4462 try
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())
4470 LocalBinding lb = (LocalBinding) lbs.first();
4471 gen.visitLocalVariable(lb.name, "Ljava/lang/Object;", null, loopLabel, end, lb.idx);
4474 finally
4476 Var.popThreadBindings();
4479 gen.returnValue();
4480 //gen.visitMaxs(1, 1);
4481 gen.endMethod();
4484 void emitClearLocals(GeneratorAdapter gen){
4487 void emitClearLocalsOld(GeneratorAdapter gen){
4488 for(int i=0;i<argLocals.count();i++)
4490 LocalBinding lb = (LocalBinding) argLocals.nth(i);
4491 if(!localsUsedInCatchFinally.contains(lb.idx) && lb.getPrimitiveType() == null)
4493 gen.visitInsn(Opcodes.ACONST_NULL);
4494 gen.storeArg(lb.idx - 1);
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++)
4508 if(!localsUsedInCatchFinally.contains(i))
4510 LocalBinding b = (LocalBinding) RT.get(indexlocals, i);
4511 if(b == null || maybePrimitiveType(b.init) == null)
4513 gen.visitInsn(Opcodes.ACONST_NULL);
4514 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), i);
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);
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 != null
4550 || (init != null && init.hasJavaClass());
4553 public Class getJavaClass() throws Exception{
4554 return tag != null ? HostExpr.tagToClass(tag)
4555 : init.getJavaClass();
4558 public Class getPrimitiveType(){
4559 return maybePrimitiveType(init);
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)
4585 // Object dummy;
4587 if(sites != null)
4589 for(ISeq s = sites.seq();s!=null;s = s.next())
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 // else
4596 // dummy = null;
4600 if(clearRoot == b.clearPathRoot)
4602 this.shouldClear = true;
4603 sites = RT.conj(sites,this);
4604 CLEAR_SITES.set(RT.assoc(CLEAR_SITES.get(), b, sites));
4606 // else
4607 // dummy = null;
4611 public Object eval() throws Exception{
4612 throw new UnsupportedOperationException("Can't eval locals");
4615 public boolean canEmitPrimitive(){
4616 return b.getPrimitiveType() != null;
4619 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){
4620 objx.emitUnboxedLocal(gen, b);
4623 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
4624 if(context != C.STATEMENT)
4625 objx.emitLocal(gen, b, shouldClear);
4628 public Object evalAssign(Expr val) throws Exception{
4629 throw new UnsupportedOperationException("Can't eval locals");
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);
4638 public boolean hasJavaClass() throws Exception{
4639 return tag != null || b.hasJavaClass();
4642 public Class getJavaClass() throws Exception{
4643 if(tag != null)
4644 return HostExpr.tagToClass(tag);
4645 return b.getJavaClass();
4651 public static class BodyExpr implements Expr, MaybePrimitiveExpr{
4652 PersistentVector exprs;
4654 public final PersistentVector exprs(){
4655 return exprs;
4658 public BodyExpr(PersistentVector exprs){
4659 this.exprs = exprs;
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())
4670 Expr e = (context != C.EVAL &&
4671 (context == C.STATEMENT || forms.next() != null)) ?
4672 analyze(C.STATEMENT, forms.first())
4674 analyze(context, forms.first());
4675 exprs = exprs.cons(e);
4677 if(exprs.count() == 0)
4678 exprs = exprs.cons(NIL_EXPR);
4679 return new BodyExpr(exprs);
4683 public Object eval() throws Exception{
4684 Object ret = null;
4685 for(Object o : exprs)
4687 Expr e = (Expr) o;
4688 ret = e.eval();
4690 return ret;
4693 public boolean canEmitPrimitive(){
4694 return lastExpr() instanceof MaybePrimitiveExpr && ((MaybePrimitiveExpr)lastExpr()).canEmitPrimitive();
4697 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){
4698 for(int i = 0; i < exprs.count() - 1; i++)
4700 Expr e = (Expr) exprs.nth(i);
4701 e.emit(C.STATEMENT, objx, gen);
4703 MaybePrimitiveExpr last = (MaybePrimitiveExpr) exprs.nth(exprs.count() - 1);
4704 last.emitUnboxed(context, objx, gen);
4707 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
4708 for(int i = 0; i < exprs.count() - 1; i++)
4710 Expr e = (Expr) exprs.nth(i);
4711 e.emit(C.STATEMENT, objx, gen);
4713 Expr last = (Expr) exprs.nth(exprs.count() - 1);
4714 last.emit(context, objx, gen);
4717 public boolean hasJavaClass() throws Exception{
4718 return lastExpr().hasJavaClass();
4721 public Class getJavaClass() throws Exception{
4722 return lastExpr().getJavaClass();
4725 private Expr lastExpr(){
4726 return (Expr) exprs.nth(exprs.count() - 1);
4730 public static class BindingInit{
4731 LocalBinding binding;
4732 Expr init;
4734 public final LocalBinding binding(){
4735 return binding;
4738 public final Expr init(){
4739 return init;
4742 public BindingInit(LocalBinding binding, Expr init){
4743 this.binding = binding;
4744 this.init = init;
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;
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 try
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)
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);
4794 PersistentVector bindingInits = PersistentVector.EMPTY;
4795 for(int i = 0; i < bindings.count(); i += 2)
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);
4804 return new LetFnExpr(bindingInits, (new BodyExpr.Parser()).parse(context, body));
4806 finally
4808 Var.popThreadBindings();
4813 public Object eval() throws Exception{
4814 throw new UnsupportedOperationException("Can't eval letfns");
4817 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
4818 for(int i = 0; i < bindingInits.count(); i++)
4820 BindingInit bi = (BindingInit) bindingInits.nth(i);
4821 gen.visitInsn(Opcodes.ACONST_NULL);
4822 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), bi.binding.idx);
4825 IPersistentSet lbset = PersistentHashSet.EMPTY;
4827 for(int i = 0; i < bindingInits.count(); i++)
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);
4835 for(int i = 0; i < bindingInits.count(); i++)
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);
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())
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 else
4860 gen.visitLocalVariable(lname, "Ljava/lang/Object;", null, loopLabel, end, bi.binding.idx);
4864 public boolean hasJavaClass() throws Exception{
4865 return body.hasJavaClass();
4868 public Class getJavaClass() throws Exception{
4869 return body.getJavaClass();
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;
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.EVAL
4899 || (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 try
4909 Var.pushThreadBindings(dynamicBindings);
4911 PersistentVector bindingInits = PersistentVector.EMPTY;
4912 PersistentVector loopLocals = PersistentVector.EMPTY;
4913 for(int i = 0; i < bindings.count(); i += 2)
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);
4930 if(isLoop)
4931 LOOP_LOCALS.set(loopLocals);
4932 Expr bodyExpr;
4933 try {
4934 if(isLoop)
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)));
4941 bodyExpr = (new BodyExpr.Parser()).parse(isLoop ? C.RETURN : context, body);
4943 finally{
4944 if(isLoop)
4945 Var.popThreadBindings();
4947 return new LetExpr(bindingInits, bodyExpr,
4948 isLoop);
4950 finally
4952 Var.popThreadBindings();
4957 public Object eval() throws Exception{
4958 throw new UnsupportedOperationException("Can't eval let/loop");
4961 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
4962 doEmit(context, objx, gen, false);
4965 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){
4966 doEmit(context, objx, gen, true);
4970 public void doEmit(C context, ObjExpr objx, GeneratorAdapter gen, boolean emitUnboxed){
4971 for(int i = 0; i < bindingInits.count(); i++)
4973 BindingInit bi = (BindingInit) bindingInits.nth(i);
4974 Class primc = maybePrimitiveType(bi.init);
4975 if(primc != null)
4977 ((MaybePrimitiveExpr) bi.init).emitUnboxed(C.EXPRESSION, objx, gen);
4978 gen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ISTORE), bi.binding.idx);
4980 else
4982 bi.init.emit(C.EXPRESSION, objx, gen);
4983 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), bi.binding.idx);
4986 Label loopLabel = gen.mark();
4987 if(isLoop)
4989 try
4991 Var.pushThreadBindings(RT.map(LOOP_LABEL, loopLabel));
4992 if(emitUnboxed)
4993 ((MaybePrimitiveExpr)body).emitUnboxed(context, objx, gen);
4994 else
4995 body.emit(context, objx, gen);
4997 finally
4999 Var.popThreadBindings();
5002 else
5004 if(emitUnboxed)
5005 ((MaybePrimitiveExpr)body).emitUnboxed(context, objx, gen);
5006 else
5007 body.emit(context, objx, gen);
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())
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 else
5022 gen.visitLocalVariable(lname, "Ljava/lang/Object;", null, loopLabel, end, bi.binding.idx);
5026 public boolean hasJavaClass() throws Exception{
5027 return body.hasJavaClass();
5030 public Class getJavaClass() throws Exception{
5031 return body.getJavaClass();
5034 public boolean canEmitPrimitive(){
5035 return body instanceof MaybePrimitiveExpr && ((MaybePrimitiveExpr)body).canEmitPrimitive();
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;
5049 public Object eval() throws Exception{
5050 throw new UnsupportedOperationException("Can't eval recur");
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++)
5059 LocalBinding lb = (LocalBinding) loopLocals.nth(i);
5060 Expr arg = (Expr) args.nth(i);
5061 if(lb.getPrimitiveType() != null)
5063 Class primc = lb.getPrimitiveType();
5064 try
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");
5070 catch(Exception e)
5072 throw new RuntimeException(e);
5074 ((MaybePrimitiveExpr) arg).emitUnboxed(C.EXPRESSION, objx, gen);
5076 else
5078 arg.emit(C.EXPRESSION, objx, gen);
5082 for(int i = loopLocals.count() - 1; i >= 0; i--)
5084 LocalBinding lb = (LocalBinding) loopLocals.nth(i);
5085 Class primc = lb.getPrimitiveType();
5086 if(lb.isArg)
5087 gen.storeArg(lb.idx-1);
5088 else
5090 if(primc != null)
5091 gen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ISTORE), lb.idx);
5092 else
5093 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), lb.idx);
5097 gen.goTo(loopLabel);
5100 public boolean hasJavaClass() throws Exception{
5101 return true;
5104 public Class getJavaClass() throws Exception{
5105 return null;
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())
5119 args = args.cons(analyze(C.EXPRESSION, s.first()));
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);
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;
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;
5150 public static Expr analyze(C context, Object form) throws Exception{
5151 return analyze(context, form, null);
5154 private static Expr analyze(C context, Object form, String name) throws Exception{
5155 //todo symbol macro expansion?
5156 try
5158 if(form instanceof LazySeq)
5160 form = RT.seq(form);
5161 if(form == null)
5162 form = PersistentList.EMPTY;
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)
5183 Expr ret = new EmptyExpr(form);
5184 if(RT.meta(form) != null)
5185 ret = new MetaExpr(ret, (MapExpr) MapExpr
5186 .parse(context == C.EVAL ? context : C.EXPRESSION, ((IObj) form).meta()));
5187 return ret;
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 // else
5199 //throw new UnsupportedOperationException();
5200 return new ConstantExpr(form);
5202 catch(Throwable e)
5204 if(!(e instanceof CompilerException))
5205 throw new CompilerException((String) SOURCE.deref(), (Integer) LINE.deref(), e);
5206 else
5207 throw (CompilerException) e;
5211 static public class CompilerException extends Exception{
5213 public CompilerException(String source, int line, Throwable cause){
5214 super(errorMsg(source, line, cause.toString()), cause);
5217 public String toString(){
5218 return getMessage();
5222 static public Var isMacro(Object op) throws Exception{
5223 //no local macros for now
5224 if(op instanceof Symbol && referenceLocal((Symbol) op) != null)
5225 return null;
5226 if(op instanceof Symbol || op instanceof Var)
5228 Var v = (op instanceof Var) ? (Var) op : lookupVar((Symbol) op, false);
5229 if(v != null && v.isMacro())
5231 if(v.ns != currentNS() && !v.isPublic())
5232 throw new IllegalStateException("var: " + v + " is not public");
5233 return v;
5236 return null;
5239 static public IFn isInline(Object op, int arity) throws Exception{
5240 //no local inlines for now
5241 if(op instanceof Symbol && referenceLocal((Symbol) op) != null)
5242 return null;
5243 if(op instanceof Symbol || op instanceof Var)
5245 Var v = (op instanceof Var) ? (Var) op : lookupVar((Symbol) op, false);
5246 if(v != null)
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)
5253 IFn arityPred = (IFn) RT.get(v.meta(), inlineAritiesKey);
5254 if(arityPred == null || RT.booleanCast(arityPred.invoke(arity)))
5255 return ret;
5259 return null;
5262 public static boolean namesStaticMember(Symbol sym){
5263 return sym.ns != null && namespaceFor(sym) == null;
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));
5272 return dst;
5275 public static Object macroexpand1(Object x) throws Exception{
5276 if(x instanceof ISeq)
5278 ISeq form = (ISeq) x;
5279 Object op = RT.first(form);
5280 if(isSpecial(op))
5281 return x;
5282 //macro expansion
5283 Var v = isMacro(op);
5284 if(v != null)
5286 return v.applyTo(RT.cons(form,RT.cons(LOCAL_ENV.get(),form.next())));
5288 else
5290 if(op instanceof Symbol)
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) == '.')
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)
5304 target = ((IObj)RT.list(IDENTITY, target)).withMeta(RT.map(RT.TAG_KEY,CLASS));
5306 return preserveTag(form, RT.listStar(DOT, target, meth, form.next().next()));
5308 else if(namesStaticMember(sym))
5310 Symbol target = Symbol.intern(sym.ns);
5311 Class c = HostExpr.maybeClass(target, false);
5312 if(c != null)
5314 Symbol meth = Symbol.intern(sym.name);
5315 return preserveTag(form, RT.listStar(DOT, target, meth, form.next()));
5318 else
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 //else
5331 if(idx == sname.length() - 1)
5332 return RT.listStar(NEW, Symbol.intern(sname.substring(0, idx)), form.next());
5337 return x;
5340 static Object macroexpand(Object form) throws Exception{
5341 Object exf = macroexpand1(form);
5342 if(exf != form)
5343 return macroexpand(exf);
5344 return form;
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 try
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 else
5371 return InvokeExpr.parse(context, form);
5373 catch(Throwable e)
5375 if(!(e instanceof CompilerException))
5376 throw new CompilerException((String) SOURCE.deref(), (Integer) LINE.deref(), e);
5377 else
5378 throw (CompilerException) e;
5380 finally
5382 Var.popThreadBindings();
5386 static String errorMsg(String source, int line, String s){
5387 return String.format("%s (%s:%d)", s, source, line);
5390 public static Object eval(Object form) throws Exception{
5391 return eval(form, true);
5394 public static Object eval(Object form, boolean freshLoader) throws Exception{
5395 boolean createdLoader = false;
5396 if(true)//!LOADER.isBound())
5398 Var.pushThreadBindings(RT.map(LOADER, RT.makeClassLoader()));
5399 createdLoader = true;
5401 try
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 try
5409 form = macroexpand(form);
5410 if(form instanceof IPersistentCollection && Util.equals(RT.first(form), DO))
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);
5417 else if(form instanceof IPersistentCollection
5418 && !(RT.first(form) instanceof Symbol
5419 && ((Symbol) RT.first(form)).name.startsWith("def")))
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();
5426 else
5428 Expr expr = analyze(C.EVAL, form);
5429 return expr.eval();
5432 finally
5434 Var.popThreadBindings();
5437 catch(Throwable e)
5439 if(!(e instanceof CompilerException))
5440 throw new CompilerException((String) SOURCE.deref(), (Integer) LINE.deref(), e);
5441 else
5442 throw (CompilerException) e;
5444 finally
5446 if(createdLoader)
5447 Var.popThreadBindings();
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();
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)
5472 KEYWORDS.set(RT.assoc(keywordsMap, keyword, registerConstant(keyword)));
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;
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;
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;
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;
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;
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))
5528 xp = xp.next();
5529 yp = yp.next();
5531 return (PathNode) RT.first(xp);
5534 static void addAnnotation(Object visitor, IPersistentMap meta){
5535 try{
5536 if(meta != null && ADD_ANNOTATIONS.isBound())
5537 ADD_ANNOTATIONS.invoke(visitor, meta);
5539 catch (Exception e)
5541 throw new RuntimeException(e);
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);
5550 catch (Exception e)
5552 throw new RuntimeException(e);
5556 private static Expr analyzeSymbol(Symbol sym) throws Exception{
5557 Symbol tag = tagOf(sym);
5558 if(sym.ns == null) //ns-qualified syms are always Vars
5560 LocalBinding b = referenceLocal(sym);
5561 if(b != null)
5563 return new LocalBindingExpr(b, tag);
5566 else
5568 if(namespaceFor(sym) == null)
5570 Symbol nsSym = Symbol.create(sym.ns);
5571 Class c = HostExpr.maybeClass(nsSym, false);
5572 if(c != null)
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);
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)
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);
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");
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;
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);
5616 static Object resolve(Symbol sym, boolean allowPrivate) throws Exception{
5617 return resolveIn(currentNS(), sym, allowPrivate);
5620 static Object resolve(Symbol sym) throws Exception{
5621 return resolveIn(currentNS(), sym, false);
5624 static Namespace namespaceFor(Symbol sym){
5625 return namespaceFor(currentNS(), sym);
5628 static Namespace namespaceFor(Namespace inns, Symbol sym){
5629 //note, presumes non-nil sym.ns
5630 // first check against currentNS' aliases...
5631 Symbol nsSym = Symbol.create(sym.ns);
5632 Namespace ns = inns.lookupAlias(nsSym);
5633 if(ns == null)
5635 // ...otherwise check the Namespaces map.
5636 ns = Namespace.find(nsSym);
5638 return ns;
5641 static public Object resolveIn(Namespace n, Symbol sym, boolean allowPrivate) throws Exception{
5642 //note - ns-qualified vars must already exist
5643 if(sym.ns != null)
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;
5656 else if(sym.name.indexOf('.') > 0 || sym.name.charAt(0) == '[')
5658 return RT.classForName(sym.name);
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 else
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)
5671 if(RT.booleanCast(RT.ALLOW_UNRESOLVED_VARS.deref()))
5673 return sym;
5675 else
5677 throw new Exception("Unable to resolve symbol: " + sym + " in this context");
5680 return o;
5685 static public Object maybeResolveIn(Namespace n, Symbol sym) throws Exception{
5686 //note - ns-qualified vars must already exist
5687 if(sym.ns != null)
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;
5697 else if(sym.name.indexOf('.') > 0 && !sym.name.endsWith(".")
5698 || sym.name.charAt(0) == '[')
5700 return RT.classForName(sym.name);
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 else
5708 Object o = n.getMapping(sym);
5709 return o;
5714 static Var lookupVar(Symbol sym, boolean internNew) throws Exception{
5715 Var var = null;
5717 //note - ns-qualified vars in other namespaces must already exist
5718 if(sym.ns != null)
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 else
5728 var = ns.findInternedVar(name);
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 else
5736 //is it mapped?
5737 Object o = currentNS().getMapping(sym);
5738 if(o == null)
5740 //introduce a new var in the current ns
5741 if(internNew)
5742 var = currentNS().intern(Symbol.create(sym.name));
5744 else if(o instanceof Var)
5746 var = (Var) o;
5748 else
5750 throw new Exception("Expecting var, but " + sym + " is mapped to " + o);
5753 if(var != null)
5754 registerVar(var);
5755 return var;
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)
5765 VARS.set(RT.assoc(varsMap, var, registerConstant(var)));
5767 // if(varsMap != null && RT.get(varsMap, var) == null)
5768 // VARS.set(RT.assoc(varsMap, var, var));
5771 static Namespace currentNS(){
5772 return (Namespace) RT.CURRENT_NS.deref();
5775 static void closeOver(LocalBinding b, ObjMethod method){
5776 if(b != null && method != null)
5778 if(RT.get(method.locals, b) == null)
5780 method.objx.closes = (IPersistentMap) RT.assoc(method.objx.closes, b, b);
5781 closeOver(b, method.parent);
5783 else if(IN_CATCH_FINALLY.deref() != null)
5785 method.localsUsedInCatchFinally = (PersistentHashSet) method.localsUsedInCatchFinally.cons(b.idx);
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)
5797 ObjMethod method = (ObjMethod) METHOD.deref();
5798 closeOver(b, method);
5800 return b;
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;
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 try
5820 return load(new InputStreamReader(f, RT.UTF8), new File(file).getAbsolutePath(), (new File(file)).getName());
5822 finally
5824 f.close();
5828 public static Object load(Reader rdr) throws Exception{
5829 return load(rdr, null, "NO_SOURCE_FILE");
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 try
5853 for(Object r = LispReader.read(pushbackReader, false, EOF, false); r != EOF;
5854 r = LispReader.read(pushbackReader, false, EOF, false))
5856 LINE_AFTER.set(pushbackReader.getLineNumber());
5857 ret = eval(r,false);
5858 LINE_BEFORE.set(pushbackReader.getLineNumber());
5861 catch(LispReader.ReaderException e)
5863 throw new CompilerException(sourceName, e.line, e.getCause());
5865 finally
5867 Var.popThreadBindings();
5869 return ret;
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++)
5880 p += File.separator + dirs[i];
5881 (new File(p)).mkdir();
5883 String path = genPath + File.separator + internalName + ".class";
5884 File cf = new File(path);
5885 cf.createNewFile();
5886 FileOutputStream cfs = new FileOutputStream(cf);
5887 try
5889 cfs.write(bytecode);
5890 cfs.flush();
5891 cfs.getFD().sync();
5893 finally
5895 cfs.close();
5899 public static void pushNS(){
5900 Var.pushThreadBindings(PersistentHashMap.create(Var.intern(Symbol.create("clojure.core"),
5901 Symbol.create("*ns*")), null));
5904 public static ILookupThunk getLookupThunk(Object target, Keyword k){
5905 return null; //To change body of created methods use File | Settings | File Templates.
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, line
5914 ,LOADER, RT.makeClassLoader()
5915 ));
5916 try
5918 form = macroexpand(form);
5919 if(form instanceof IPersistentCollection && Util.equals(RT.first(form), DO))
5921 for(ISeq s = RT.next(form); s != null; s = RT.next(s))
5923 compile1(gen, objx, RT.first(s));
5926 else
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();
5936 finally
5938 Var.popThreadBindings();
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.EMPTY
5965 // ,LOADER, RT.makeClassLoader()
5966 ));
5968 try
5970 //generate loader class
5971 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 method
5981 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))
5991 LINE_AFTER.set(pushbackReader.getLineNumber());
5992 compile1(gen, objx, r);
5993 LINE_BEFORE.set(pushbackReader.getLineNumber());
5995 //end of load
5996 gen.returnValue();
5997 gen.endMethod();
5999 //static fields for constants
6000 for(int i = 0; i < objx.constants.count(); i++)
6002 cv.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, objx.constantName(i), objx.constantType(i).getDescriptor(),
6003 null, null);
6006 //static init for constants, keywords and vars
6007 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)
6020 objx.emitConstants(clinitgen);
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 stack
6031 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 init
6037 clinitgen.returnValue();
6038 clinitgen.endMethod();
6040 //end of class
6041 cv.visitEnd();
6043 writeClassFile(objx.internalName, cw.toByteArray());
6045 catch(LispReader.ReaderException e)
6047 throw new CompilerException(sourceName, e.line, e.getCause());
6049 finally
6051 Var.popThreadBindings();
6053 return ret;
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);
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)
6082 opts = opts.assoc(rform.first(), RT.second(rform));
6083 rform = rform.next().next();
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;
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) MapExpr
6114 .parse(context == C.EVAL ? context : C.EXPRESSION, ((IObj) frm).meta()));
6115 else
6116 return ret;
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)
6136 IPersistentMap fmap = PersistentHashMap.EMPTY;
6137 Object[] closesvec = new Object[2 * fieldSyms.count()];
6138 for(int i=0;i<fieldSyms.count();i++)
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;
6148 //todo - inject __meta et al into closes - when?
6149 //use array map to preserve ctor order
6150 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++;
6155 //todo - set up volatiles
6156 // 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())
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);
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 try
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.EMPTY
6188 ));
6189 if(ret.isDeftype())
6191 Var.pushThreadBindings(RT.map(METHOD, null,
6192 LOCAL_ENV, ret.fields
6193 , COMPILE_STUB_SYM, Symbol.intern(null, tagName)
6194 , COMPILE_STUB_CLASS, stub));
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))
6202 NewInstanceMethod m = NewInstanceMethod.parse(ret, (ISeq) RT.first(s),thistag, overrideables);
6203 methods = RT.conj(methods, m);
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();
6216 finally
6218 if(ret.isDeftype())
6219 Var.popThreadBindings();
6220 Var.popThreadBindings();
6223 ret.compile(slashname(superClass),inames,false);
6224 ret.getCompiledClass();
6225 return ret;
6228 /***
6229 * Current host interop uses reflection, which requires pre-existing classes
6230 * 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 classes
6234 */
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-overs
6242 for(ISeq s = RT.keys(ret.closes); s != null; s = s.next())
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(access
6250 , lb.name, Type.getType(lb.getPrimitiveType()).getDescriptor(),
6251 null, null);
6252 else
6253 //todo - when closed-overs are fields, use more specific types here and in ctor and emitLocal?
6254 cv.visitField(access
6255 , lb.name, OBJECT_TYPE.getDescriptor(), null, null);
6258 //ctor that takes closed-overs and does nothing
6259 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)
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();
6295 //end of class
6296 cv.visitEnd();
6298 byte[] bytecode = cw.toByteArray();
6299 DynamicClassLoader loader = (DynamicClassLoader) LOADER.deref();
6300 return loader.defineClass(COMPILE_STUB_PREFIX + "." + ret.name, bytecode, frm);
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;
6312 static String slashname(Class c){
6313 return c.getName().replace('.', '/');
6317 protected void emitMethods(ClassVisitor cv){
6318 for(ISeq s = RT.seq(methods); s != null; s = s.next())
6320 ObjMethod method = (ObjMethod) s.first();
6321 method.emit(this, cv);
6323 //emit bridge methods
6324 for(Map.Entry<IPersistentVector,Set<Class>> e : covariants.entrySet())
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++)
6332 argTypes[i] = Type.getType(params[i]);
6335 Method target = new Method(m.getName(), Type.getType(m.getReturnType()), argTypes);
6337 for(Class retType : e.getValue())
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 this
6345 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();
6357 static public IPersistentVector msig(java.lang.reflect.Method m){
6358 return RT.vector(m.getName(), RT.seq(m.getParameterTypes()),m.getReturnType());
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)))
6370 mm.put(mk, m);
6374 static void gatherMethods(Class c, Map mm){
6375 for(; c != null; c = c.getSuperclass())
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);
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())
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 return
6400 Set<Class> cvs = covariants.get(mk);
6401 if(cvs == null)
6403 cvs = new HashSet<Class>();
6404 covariants.put(mk,cvs);
6406 java.lang.reflect.Method om = mm.get(mk);
6407 if(om.getReturnType().isAssignableFrom(m.getReturnType()))
6409 cvs.add(om.getReturnType());
6410 mm.put(mk, m);
6412 else
6413 cvs.add(m.getReturnType());
6415 else
6416 mm.put(mk, m);
6418 return new Map[]{mm,covariants};
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);
6436 int numParams(){
6437 return argLocals.count();
6440 String getMethodName(){
6441 return name;
6444 Type getReturnType(){
6445 return retType;
6448 Type[] getArgTypes(){
6449 return argTypes;
6454 static public IPersistentVector msig(String name,Class[] paramTypes){
6455 return RT.vector(name,RT.seq(paramTypes));
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 nil
6462 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)
6468 throw new IllegalArgumentException("Must supply at least one argument for 'this' in: " + dotname);
6470 Symbol thisName = (Symbol) parms.nth(0);
6471 parms = RT.subvec(parms,1,parms.count());
6472 ISeq body = RT.next(RT.next(form));
6473 try
6475 method.line = (Integer) LINE.deref();
6476 //register as the current method and set up a new env frame
6477 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, 0
6484 ,CLEAR_PATH, pnode
6485 ,CLEAR_ROOT, pnode
6486 ,CLEAR_SITES, PersistentHashMap.EMPTY
6487 ));
6489 //register 'this' as local 0
6490 if(thisName != null)
6491 registerLocal((thisName == null) ? dummyThis:thisName,thistag, null,false);
6492 else
6493 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++)
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;
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)
6521 //multiple methods
6522 if(matches.size() > 1)
6524 //must be hinted and match one method
6525 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());
6534 else //one match
6536 //if hinted, validate match,
6537 if(hinted)
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());
6547 else //adopt found method sig
6549 m = (java.lang.reflect.Method) matches.values().iterator().next();
6550 method.retClass = m.getReturnType();
6551 pclasses = m.getParameterTypes();
6555 // else if(findMethodsWithName(name.name,allmethods).size()>0)
6556 // throw new IllegalArgumentException("Can't override/overload method: " + name.name);
6557 else
6558 throw new IllegalArgumentException("Can't define method not in interfaces: " + name.name);
6560 //else
6561 //validate unque name+arity among additional methods
6563 method.retType = Type.getType(method.retClass);
6564 method.exclasses = m.getExceptionTypes();
6566 for(int i = 0; i < parms.count(); i++)
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]);
6572 for(int i = 0; i < parms.count(); i++)
6574 if(pclasses[i] == long.class || pclasses[i] == double.class)
6575 getAndIncLocalNum();
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;
6585 finally
6587 Var.popThreadBindings();
6591 private static Map findMethodsWithNameAndArity(String name, int arity, Map mm){
6592 Map ret = new HashMap();
6593 for(Object o : mm.entrySet())
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());
6600 return ret;
6603 private static Map findMethodsWithName(String name, Map mm){
6604 Map ret = new HashMap();
6605 for(Object o : mm.entrySet())
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());
6612 return ret;
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)
6621 extypes = new Type[exclasses.length];
6622 for(int i=0;i<exclasses.length;i++)
6623 extypes[i] = Type.getType(exclasses[i]);
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++)
6633 IPersistentMap meta = RT.meta(parms.nth(i));
6634 addParameterAnnotation(gen, meta, i);
6636 gen.visitCode();
6637 Label loopLabel = gen.mark();
6638 gen.visitLineNumber(line, loopLabel);
6639 try
6641 Var.pushThreadBindings(RT.map(LOOP_LABEL, loopLabel, METHOD, this));
6642 MaybePrimitiveExpr be = (MaybePrimitiveExpr) body;
6643 if(Util.isPrimitive(retClass) && be.canEmitPrimitive())
6645 if(be.getJavaClass() == retClass)
6646 be.emitUnboxed(C.RETURN,obj,gen);
6647 //todo - support the standard widening conversions
6648 else
6649 throw new IllegalArgumentException("Mismatched primitive return, expected: "
6650 + retClass + ", had: " + be.getJavaClass());
6652 else
6654 body.emit(C.RETURN, obj, gen);
6655 if(retClass == void.class)
6657 gen.pop();
6659 else
6660 gen.unbox(retType);
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())
6667 LocalBinding lb = (LocalBinding) lbs.first();
6668 gen.visitLocalVariable(lb.name, argTypes[lb.idx-1].getDescriptor(), null, loopLabel, end, lb.idx);
6671 catch(Exception e)
6673 throw new RuntimeException(e);
6675 finally
6677 Var.popThreadBindings();
6680 gen.returnValue();
6681 //gen.visitMaxs(1, 1);
6682 gen.endMethod();
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;
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;
6722 static public class MethodParamExpr implements Expr, MaybePrimitiveExpr{
6723 final Class c;
6725 public MethodParamExpr(Class c){
6726 this.c = c;
6729 public Object eval() throws Exception{
6730 throw new Exception("Can't eval");
6733 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
6734 throw new RuntimeException("Can't emit");
6737 public boolean hasJavaClass() throws Exception{
6738 return c != null;
6741 public Class getJavaClass() throws Exception{
6742 return c;
6745 public boolean canEmitPrimitive(){
6746 return Util.isPrimitive(c);
6749 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){
6750 throw new RuntimeException("Can't emit");
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;
6783 public Object eval() throws Exception{
6784 throw new UnsupportedOperationException("Can't eval case");
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())
6794 labels.put(i, gen.newLabel());
6797 Label[] la = new Label[(high-low)+1];
6799 for(int i=low;i<=high;i++)
6801 la[i-low] = labels.containsKey(i) ? labels.get(i) : defaultLabel;
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())
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)
6820 gen.visitJumpInsn(IF_ACMPNE, defaultLabel);
6822 else
6824 gen.invokeStatic(UTIL_TYPE, equalsMethod);
6825 gen.ifZCmp(GeneratorAdapter.EQ, defaultLabel);
6827 thens.get(i).emit(C.EXPRESSION,objx,gen);
6828 gen.goTo(endLabel);
6831 gen.mark(defaultLabel);
6832 defaultExpr.emit(C.EXPRESSION, objx, gen);
6833 gen.mark(endLabel);
6834 if(context == C.STATEMENT)
6835 gen.pop();
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 correct
6841 //case macro binds actual expr in let so expr is always a local,
6842 //no need to worry about multiple evaluation
6843 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())
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());
6868 finally{
6869 Var.popThreadBindings();
6871 thens.put(minhash, thenExpr);
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));
6880 finally{
6881 Var.popThreadBindings();
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);