Mercurial > lasercutter
comparison 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 |
comparison
equal
deleted
inserted
replaced
9:35cf337adfcf | 10:ef7dbbd6452c |
---|---|
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 **/ | |
10 | |
11 /* rich Aug 21, 2007 */ | |
12 | |
13 package clojure.lang; | |
14 | |
15 //* | |
16 | |
17 import clojure.asm.*; | |
18 import clojure.asm.commons.Method; | |
19 import clojure.asm.commons.GeneratorAdapter; | |
20 //*/ | |
21 /* | |
22 | |
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 //*/ | |
29 | |
30 import java.io.*; | |
31 import java.util.*; | |
32 import java.lang.reflect.Constructor; | |
33 import java.lang.reflect.Modifier; | |
34 | |
35 public class Compiler implements Opcodes{ | |
36 | |
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*"); | |
60 | |
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"); | |
73 | |
74 static final Symbol _AMP_ = Symbol.create("&"); | |
75 static final Symbol ISEQ = Symbol.create("clojure.lang.ISeq"); | |
76 | |
77 static final Keyword inlineKey = Keyword.intern(null, "inline"); | |
78 static final Keyword inlineAritiesKey = Keyword.intern(null, "inline-arities"); | |
79 | |
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"; | |
83 | |
84 static final Keyword protocolKey = Keyword.intern(null, "protocol"); | |
85 static final Keyword onKey = Keyword.intern(null, "on"); | |
86 | |
87 static final Symbol NS = Symbol.create("ns"); | |
88 static final Symbol IN_NS = Symbol.create("in-ns"); | |
89 | |
90 //static final Symbol IMPORT = Symbol.create("import"); | |
91 //static final Symbol USE = Symbol.create("use"); | |
92 | |
93 //static final Symbol IFN = Symbol.create("clojure.lang", "IFn"); | |
94 | |
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 ); | |
129 | |
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); | |
147 | |
148 private static final Type[][] ARG_TYPES; | |
149 private static final Type[] EXCEPTION_TYPES = {Type.getType(Exception.class)}; | |
150 | |
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; | |
167 | |
168 | |
169 } | |
170 | |
171 | |
172 //symbol->localbinding | |
173 static final public Var LOCAL_ENV = Var.create(null); | |
174 | |
175 //vector<localbinding> | |
176 static final public Var LOOP_LOCALS = Var.create(); | |
177 | |
178 //Label | |
179 static final public Var LOOP_LABEL = Var.create(); | |
180 | |
181 //vector<object> | |
182 static final public Var CONSTANTS = Var.create(); | |
183 | |
184 //IdentityHashMap | |
185 static final public Var CONSTANT_IDS = Var.create(); | |
186 | |
187 //vector<keyword> | |
188 static final public Var KEYWORD_CALLSITES = Var.create(); | |
189 | |
190 //vector<var> | |
191 static final public Var PROTOCOL_CALLSITES = Var.create(); | |
192 | |
193 //vector<var> | |
194 static final public Var VAR_CALLSITES = Var.create(); | |
195 | |
196 //keyword->constid | |
197 static final public Var KEYWORDS = Var.create(); | |
198 | |
199 //var->constid | |
200 static final public Var VARS = Var.create(); | |
201 | |
202 //FnFrame | |
203 static final public Var METHOD = Var.create(null); | |
204 | |
205 //null or not | |
206 static final public Var IN_CATCH_FINALLY = Var.create(null); | |
207 | |
208 //DynamicClassLoader | |
209 static final public Var LOADER = Var.create(); | |
210 | |
211 //String | |
212 static final public Var SOURCE = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")), | |
213 Symbol.create("*source-path*"), "NO_SOURCE_FILE"); | |
214 | |
215 //String | |
216 static final public Var SOURCE_PATH = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")), | |
217 Symbol.create("*file*"), "NO_SOURCE_PATH"); | |
218 | |
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); | |
225 | |
226 static final public Var INSTANCE = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")), | |
227 Symbol.create("instance?")); | |
228 | |
229 static final public Var ADD_ANNOTATIONS = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")), | |
230 Symbol.create("add-annotations")); | |
231 | |
232 //Integer | |
233 static final public Var LINE = Var.create(0); | |
234 | |
235 //Integer | |
236 static final public Var LINE_BEFORE = Var.create(0); | |
237 static final public Var LINE_AFTER = Var.create(0); | |
238 | |
239 //Integer | |
240 static final public Var NEXT_LOCAL_NUM = Var.create(0); | |
241 | |
242 //Integer | |
243 static final public Var RET_LOCAL_NUM = Var.create(); | |
244 | |
245 | |
246 static final public Var COMPILE_STUB_SYM = Var.create(null); | |
247 static final public Var COMPILE_STUB_CLASS = Var.create(null); | |
248 | |
249 | |
250 //PathNode chain | |
251 static final public Var CLEAR_PATH = Var.create(null); | |
252 | |
253 //tail of PathNode chain | |
254 static final public Var CLEAR_ROOT = Var.create(null); | |
255 | |
256 //LocalBinding -> Set<LocalBindingExpr> | |
257 static final public Var CLEAR_SITES = Var.create(null); | |
258 | |
259 public enum C{ | |
260 STATEMENT, //value ignored | |
261 EXPRESSION, //value required | |
262 RETURN, //tail position relative to enclosing recur frame | |
263 EVAL | |
264 } | |
265 | |
266 interface Expr{ | |
267 Object eval() throws Exception; | |
268 | |
269 void emit(C context, ObjExpr objx, GeneratorAdapter gen); | |
270 | |
271 boolean hasJavaClass() throws Exception; | |
272 | |
273 Class getJavaClass() throws Exception; | |
274 } | |
275 | |
276 public static abstract class UntypedExpr implements Expr{ | |
277 | |
278 public Class getJavaClass(){ | |
279 throw new IllegalArgumentException("Has no Java class"); | |
280 } | |
281 | |
282 public boolean hasJavaClass(){ | |
283 return false; | |
284 } | |
285 } | |
286 | |
287 interface IParser{ | |
288 Expr parse(C context, Object form) throws Exception; | |
289 } | |
290 | |
291 static boolean isSpecial(Object sym){ | |
292 return specials.containsKey(sym); | |
293 } | |
294 | |
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; | |
317 | |
318 } | |
319 | |
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)"); | |
331 | |
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 } | |
340 | |
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 } | |
352 | |
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 } | |
379 | |
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 } | |
398 | |
399 if(context == C.STATEMENT) | |
400 gen.pop(); | |
401 } | |
402 | |
403 public boolean hasJavaClass(){ | |
404 return true; | |
405 } | |
406 | |
407 public Class getJavaClass(){ | |
408 return Var.class; | |
409 } | |
410 | |
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 } | |
444 | |
445 public static class AssignExpr implements Expr{ | |
446 public final AssignableExpr target; | |
447 public final Expr val; | |
448 | |
449 public AssignExpr(AssignableExpr target, Expr val){ | |
450 this.target = target; | |
451 this.val = val; | |
452 } | |
453 | |
454 public Object eval() throws Exception{ | |
455 return target.evalAssign(val); | |
456 } | |
457 | |
458 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
459 target.emitAssign(context, objx, gen, val); | |
460 } | |
461 | |
462 public boolean hasJavaClass() throws Exception{ | |
463 return val.hasJavaClass(); | |
464 } | |
465 | |
466 public Class getJavaClass() throws Exception{ | |
467 return val.getJavaClass(); | |
468 } | |
469 | |
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 } | |
482 | |
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)"); | |
488 | |
489 public VarExpr(Var var, Symbol tag){ | |
490 this.var = var; | |
491 this.tag = tag != null ? tag : var.getTag(); | |
492 } | |
493 | |
494 public Object eval() throws Exception{ | |
495 return var.deref(); | |
496 } | |
497 | |
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 } | |
506 | |
507 public boolean hasJavaClass(){ | |
508 return tag != null; | |
509 } | |
510 | |
511 public Class getJavaClass() throws Exception{ | |
512 return HostExpr.tagToClass(tag); | |
513 } | |
514 | |
515 public Object evalAssign(Expr val) throws Exception{ | |
516 return var.set(val.eval()); | |
517 } | |
518 | |
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 } | |
528 | |
529 public static class TheVarExpr implements Expr{ | |
530 public final Var var; | |
531 | |
532 public TheVarExpr(Var var){ | |
533 this.var = var; | |
534 } | |
535 | |
536 public Object eval() throws Exception{ | |
537 return var; | |
538 } | |
539 | |
540 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
541 objx.emitVar(gen, var); | |
542 if(context == C.STATEMENT) | |
543 gen.pop(); | |
544 } | |
545 | |
546 public boolean hasJavaClass(){ | |
547 return true; | |
548 } | |
549 | |
550 public Class getJavaClass() throws ClassNotFoundException{ | |
551 return Var.class; | |
552 } | |
553 | |
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 } | |
564 | |
565 public static class KeywordExpr implements Expr{ | |
566 public final Keyword k; | |
567 | |
568 public KeywordExpr(Keyword k){ | |
569 this.k = k; | |
570 } | |
571 | |
572 public Object eval() throws Exception{ | |
573 return k; | |
574 } | |
575 | |
576 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
577 objx.emitKeyword(gen, k); | |
578 if(context == C.STATEMENT) | |
579 gen.pop(); | |
580 | |
581 } | |
582 | |
583 public boolean hasJavaClass(){ | |
584 return true; | |
585 } | |
586 | |
587 public Class getJavaClass() throws ClassNotFoundException{ | |
588 return Keyword.class; | |
589 } | |
590 } | |
591 | |
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()"); | |
597 | |
598 public ImportExpr(String c){ | |
599 this.c = c; | |
600 } | |
601 | |
602 public Object eval() throws Exception{ | |
603 Namespace ns = (Namespace) RT.CURRENT_NS.deref(); | |
604 ns.importClass(RT.classForName(c)); | |
605 return null; | |
606 } | |
607 | |
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 } | |
618 | |
619 public boolean hasJavaClass(){ | |
620 return false; | |
621 } | |
622 | |
623 public Class getJavaClass() throws ClassNotFoundException{ | |
624 throw new IllegalArgumentException("ImportExpr has no Java class"); | |
625 } | |
626 | |
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 } | |
633 | |
634 public static abstract class LiteralExpr implements Expr{ | |
635 abstract Object val(); | |
636 | |
637 public Object eval(){ | |
638 return val(); | |
639 } | |
640 } | |
641 | |
642 static interface AssignableExpr{ | |
643 Object evalAssign(Expr val) throws Exception; | |
644 | |
645 void emitAssign(C context, ObjExpr objx, GeneratorAdapter gen, Expr val); | |
646 } | |
647 | |
648 static public interface MaybePrimitiveExpr extends Expr{ | |
649 public boolean canEmitPrimitive(); | |
650 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen); | |
651 } | |
652 | |
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); | |
663 | |
664 final static Method charValueMethod = Method.getMethod("char charValue()"); | |
665 final static Method booleanValueMethod = Method.getMethod("boolean booleanValue()"); | |
666 | |
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)"); | |
674 | |
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()"); | |
681 | |
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)"); | |
685 | |
686 | |
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 } | |
733 | |
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 } | |
780 | |
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 } | |
838 | |
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 } | |
864 | |
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 } | |
919 | |
920 static abstract class FieldExpr extends HostExpr{ | |
921 } | |
922 | |
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)"); | |
932 | |
933 | |
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 } | |
948 | |
949 public Object eval() throws Exception{ | |
950 return Reflector.invokeNoArgInstanceMember(target.eval(), fieldName); | |
951 } | |
952 | |
953 public boolean canEmitPrimitive(){ | |
954 return targetClass != null && field != null && | |
955 Util.isPrimitive(field.getType()); | |
956 } | |
957 | |
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 } | |
969 | |
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 } | |
993 | |
994 public boolean hasJavaClass() throws Exception{ | |
995 return field != null || tag != null; | |
996 } | |
997 | |
998 public Class getJavaClass() throws Exception{ | |
999 return tag != null ? HostExpr.tagToClass(tag) : field.getType(); | |
1000 } | |
1001 | |
1002 public Object evalAssign(Expr val) throws Exception{ | |
1003 return Reflector.setInstanceField(target.eval(), fieldName, val.eval()); | |
1004 } | |
1005 | |
1006 public void emitAssign(C context, ObjExpr objx, GeneratorAdapter gen, | |
1007 Expr val){ | |
1008 gen.visitLineNumber(line, gen.mark()); | |
1009 if(targetClass != null && field != null) | |
1010 { | |
1011 target.emit(C.EXPRESSION, objx, gen); | |
1012 gen.checkCast(Type.getType(targetClass)); | |
1013 val.emit(C.EXPRESSION, objx, gen); | |
1014 gen.dupX1(); | |
1015 HostExpr.emitUnboxArg(objx, gen, field.getType()); | |
1016 gen.putField(Type.getType(targetClass), fieldName, Type.getType(field.getType())); | |
1017 } | |
1018 else | |
1019 { | |
1020 target.emit(C.EXPRESSION, objx, gen); | |
1021 gen.push(fieldName); | |
1022 val.emit(C.EXPRESSION, objx, gen); | |
1023 gen.invokeStatic(REFLECTOR_TYPE, setInstanceFieldMethod); | |
1024 } | |
1025 if(context == C.STATEMENT) | |
1026 gen.pop(); | |
1027 } | |
1028 } | |
1029 | |
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; | |
1039 | |
1040 public StaticFieldExpr(int line, Class c, String fieldName, Symbol tag) throws Exception{ | |
1041 //this.className = className; | |
1042 this.fieldName = fieldName; | |
1043 this.line = line; | |
1044 //c = Class.forName(className); | |
1045 this.c = c; | |
1046 field = c.getField(fieldName); | |
1047 this.tag = tag; | |
1048 } | |
1049 | |
1050 public Object eval() throws Exception{ | |
1051 return Reflector.getStaticField(c, fieldName); | |
1052 } | |
1053 | |
1054 public boolean canEmitPrimitive(){ | |
1055 return Util.isPrimitive(field.getType()); | |
1056 } | |
1057 | |
1058 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){ | |
1059 gen.visitLineNumber(line, gen.mark()); | |
1060 gen.getStatic(Type.getType(c), fieldName, Type.getType(field.getType())); | |
1061 } | |
1062 | |
1063 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
1064 gen.visitLineNumber(line, gen.mark()); | |
1065 | |
1066 gen.getStatic(Type.getType(c), fieldName, Type.getType(field.getType())); | |
1067 //if(context != C.STATEMENT) | |
1068 HostExpr.emitBoxReturn(objx, gen, field.getType()); | |
1069 if(context == C.STATEMENT) | |
1070 { | |
1071 gen.pop(); | |
1072 } | |
1073 // gen.push(className); | |
1074 // gen.push(fieldName); | |
1075 // gen.invokeStatic(REFLECTOR_TYPE, getStaticFieldMethod); | |
1076 } | |
1077 | |
1078 public boolean hasJavaClass(){ | |
1079 return true; | |
1080 } | |
1081 | |
1082 public Class getJavaClass() throws Exception{ | |
1083 //Class c = Class.forName(className); | |
1084 //java.lang.reflect.Field field = c.getField(fieldName); | |
1085 return tag != null ? HostExpr.tagToClass(tag) : field.getType(); | |
1086 } | |
1087 | |
1088 public Object evalAssign(Expr val) throws Exception{ | |
1089 return Reflector.setStaticField(c, fieldName, val.eval()); | |
1090 } | |
1091 | |
1092 public void emitAssign(C context, ObjExpr objx, GeneratorAdapter gen, | |
1093 Expr val){ | |
1094 gen.visitLineNumber(line, gen.mark()); | |
1095 val.emit(C.EXPRESSION, objx, gen); | |
1096 gen.dup(); | |
1097 HostExpr.emitUnboxArg(objx, gen, field.getType()); | |
1098 gen.putStatic(Type.getType(c), fieldName, Type.getType(field.getType())); | |
1099 if(context == C.STATEMENT) | |
1100 gen.pop(); | |
1101 } | |
1102 | |
1103 | |
1104 } | |
1105 | |
1106 static Class maybePrimitiveType(Expr e){ | |
1107 try | |
1108 { | |
1109 if(e instanceof MaybePrimitiveExpr && e.hasJavaClass() && ((MaybePrimitiveExpr)e).canEmitPrimitive()) | |
1110 { | |
1111 Class c = e.getJavaClass(); | |
1112 if(Util.isPrimitive(c)) | |
1113 return c; | |
1114 } | |
1115 } | |
1116 catch(Exception ex) | |
1117 { | |
1118 throw new RuntimeException(ex); | |
1119 } | |
1120 return null; | |
1121 } | |
1122 | |
1123 static abstract class MethodExpr extends HostExpr{ | |
1124 static void emitArgsAsArray(IPersistentVector args, ObjExpr objx, GeneratorAdapter gen){ | |
1125 gen.push(args.count()); | |
1126 gen.newArray(OBJECT_TYPE); | |
1127 for(int i = 0; i < args.count(); i++) | |
1128 { | |
1129 gen.dup(); | |
1130 gen.push(i); | |
1131 ((Expr) args.nth(i)).emit(C.EXPRESSION, objx, gen); | |
1132 gen.arrayStore(OBJECT_TYPE); | |
1133 } | |
1134 } | |
1135 | |
1136 public static void emitTypedArgs(ObjExpr objx, GeneratorAdapter gen, Class[] parameterTypes, IPersistentVector args){ | |
1137 for(int i = 0; i < parameterTypes.length; i++) | |
1138 { | |
1139 Expr e = (Expr) args.nth(i); | |
1140 try | |
1141 { | |
1142 if(maybePrimitiveType(e) == parameterTypes[i]) | |
1143 { | |
1144 ((MaybePrimitiveExpr) e).emitUnboxed(C.EXPRESSION, objx, gen); | |
1145 } | |
1146 else | |
1147 { | |
1148 e.emit(C.EXPRESSION, objx, gen); | |
1149 HostExpr.emitUnboxArg(objx, gen, parameterTypes[i]); | |
1150 } | |
1151 } | |
1152 catch(Exception e1) | |
1153 { | |
1154 e1.printStackTrace(RT.errPrintWriter()); | |
1155 } | |
1156 | |
1157 } | |
1158 } | |
1159 } | |
1160 | |
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; | |
1169 | |
1170 final static Method invokeInstanceMethodMethod = | |
1171 Method.getMethod("Object invokeInstanceMethod(Object,String,Object[])"); | |
1172 | |
1173 | |
1174 public InstanceMethodExpr(String source, int line, Symbol tag, Expr target, String methodName, IPersistentVector args) | |
1175 throws Exception{ | |
1176 this.source = source; | |
1177 this.line = line; | |
1178 this.args = args; | |
1179 this.methodName = methodName; | |
1180 this.target = target; | |
1181 this.tag = tag; | |
1182 if(target.hasJavaClass() && target.getJavaClass() != null) | |
1183 { | |
1184 List methods = Reflector.getMethods(target.getJavaClass(), args.count(), methodName, false); | |
1185 if(methods.isEmpty()) | |
1186 method = null; | |
1187 //throw new IllegalArgumentException("No matching method found"); | |
1188 else | |
1189 { | |
1190 int methodidx = 0; | |
1191 if(methods.size() > 1) | |
1192 { | |
1193 ArrayList<Class[]> params = new ArrayList(); | |
1194 ArrayList<Class> rets = new ArrayList(); | |
1195 for(int i = 0; i < methods.size(); i++) | |
1196 { | |
1197 java.lang.reflect.Method m = (java.lang.reflect.Method) methods.get(i); | |
1198 params.add(m.getParameterTypes()); | |
1199 rets.add(m.getReturnType()); | |
1200 } | |
1201 methodidx = getMatchingParams(methodName, params, args, rets); | |
1202 } | |
1203 java.lang.reflect.Method m = | |
1204 (java.lang.reflect.Method) (methodidx >= 0 ? methods.get(methodidx) : null); | |
1205 if(m != null && !Modifier.isPublic(m.getDeclaringClass().getModifiers())) | |
1206 { | |
1207 //public method of non-public class, try to find it in hierarchy | |
1208 m = Reflector.getAsMethodOfPublicBase(m.getDeclaringClass(), m); | |
1209 } | |
1210 method = m; | |
1211 } | |
1212 } | |
1213 else | |
1214 method = null; | |
1215 | |
1216 if(method == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) | |
1217 { | |
1218 RT.errPrintWriter() | |
1219 .format("Reflection warning, %s:%d - call to %s can't be resolved.\n", | |
1220 SOURCE_PATH.deref(), line, methodName); | |
1221 } | |
1222 } | |
1223 | |
1224 public Object eval() throws Exception{ | |
1225 try | |
1226 { | |
1227 Object targetval = target.eval(); | |
1228 Object[] argvals = new Object[args.count()]; | |
1229 for(int i = 0; i < args.count(); i++) | |
1230 argvals[i] = ((Expr) args.nth(i)).eval(); | |
1231 if(method != null) | |
1232 { | |
1233 LinkedList ms = new LinkedList(); | |
1234 ms.add(method); | |
1235 return Reflector.invokeMatchingMethod(methodName, ms, targetval, argvals); | |
1236 } | |
1237 return Reflector.invokeInstanceMethod(targetval, methodName, argvals); | |
1238 } | |
1239 catch(Throwable e) | |
1240 { | |
1241 if(!(e instanceof CompilerException)) | |
1242 throw new CompilerException(source, line, e); | |
1243 else | |
1244 throw (CompilerException) e; | |
1245 } | |
1246 } | |
1247 | |
1248 public boolean canEmitPrimitive(){ | |
1249 return method != null && Util.isPrimitive(method.getReturnType()); | |
1250 } | |
1251 | |
1252 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){ | |
1253 gen.visitLineNumber(line, gen.mark()); | |
1254 if(method != null) | |
1255 { | |
1256 Type type = Type.getType(method.getDeclaringClass()); | |
1257 target.emit(C.EXPRESSION, objx, gen); | |
1258 //if(!method.getDeclaringClass().isInterface()) | |
1259 gen.checkCast(type); | |
1260 MethodExpr.emitTypedArgs(objx, gen, method.getParameterTypes(), args); | |
1261 if(context == C.RETURN) | |
1262 { | |
1263 ObjMethod method = (ObjMethod) METHOD.deref(); | |
1264 method.emitClearLocals(gen); | |
1265 } | |
1266 Method m = new Method(methodName, Type.getReturnType(method), Type.getArgumentTypes(method)); | |
1267 if(method.getDeclaringClass().isInterface()) | |
1268 gen.invokeInterface(type, m); | |
1269 else | |
1270 gen.invokeVirtual(type, m); | |
1271 } | |
1272 else | |
1273 throw new UnsupportedOperationException("Unboxed emit of unknown member"); | |
1274 } | |
1275 | |
1276 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
1277 gen.visitLineNumber(line, gen.mark()); | |
1278 if(method != null) | |
1279 { | |
1280 Type type = Type.getType(method.getDeclaringClass()); | |
1281 target.emit(C.EXPRESSION, objx, gen); | |
1282 //if(!method.getDeclaringClass().isInterface()) | |
1283 gen.checkCast(type); | |
1284 MethodExpr.emitTypedArgs(objx, gen, method.getParameterTypes(), args); | |
1285 if(context == C.RETURN) | |
1286 { | |
1287 ObjMethod method = (ObjMethod) METHOD.deref(); | |
1288 method.emitClearLocals(gen); | |
1289 } | |
1290 Method m = new Method(methodName, Type.getReturnType(method), Type.getArgumentTypes(method)); | |
1291 if(method.getDeclaringClass().isInterface()) | |
1292 gen.invokeInterface(type, m); | |
1293 else | |
1294 gen.invokeVirtual(type, m); | |
1295 //if(context != C.STATEMENT || method.getReturnType() == Void.TYPE) | |
1296 HostExpr.emitBoxReturn(objx, gen, method.getReturnType()); | |
1297 } | |
1298 else | |
1299 { | |
1300 target.emit(C.EXPRESSION, objx, gen); | |
1301 gen.push(methodName); | |
1302 emitArgsAsArray(args, objx, gen); | |
1303 if(context == C.RETURN) | |
1304 { | |
1305 ObjMethod method = (ObjMethod) METHOD.deref(); | |
1306 method.emitClearLocals(gen); | |
1307 } | |
1308 gen.invokeStatic(REFLECTOR_TYPE, invokeInstanceMethodMethod); | |
1309 } | |
1310 if(context == C.STATEMENT) | |
1311 gen.pop(); | |
1312 } | |
1313 | |
1314 public boolean hasJavaClass(){ | |
1315 return method != null || tag != null; | |
1316 } | |
1317 | |
1318 public Class getJavaClass() throws Exception{ | |
1319 return tag != null ? HostExpr.tagToClass(tag) : method.getReturnType(); | |
1320 } | |
1321 } | |
1322 | |
1323 | |
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[])"); | |
1336 | |
1337 | |
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; | |
1346 | |
1347 List methods = Reflector.getMethods(c, args.count(), methodName, true); | |
1348 if(methods.isEmpty()) | |
1349 throw new IllegalArgumentException("No matching method: " + methodName); | |
1350 | |
1351 int methodidx = 0; | |
1352 if(methods.size() > 1) | |
1353 { | |
1354 ArrayList<Class[]> params = new ArrayList(); | |
1355 ArrayList<Class> rets = new ArrayList(); | |
1356 for(int i = 0; i < methods.size(); i++) | |
1357 { | |
1358 java.lang.reflect.Method m = (java.lang.reflect.Method) methods.get(i); | |
1359 params.add(m.getParameterTypes()); | |
1360 rets.add(m.getReturnType()); | |
1361 } | |
1362 methodidx = getMatchingParams(methodName, params, args, rets); | |
1363 } | |
1364 method = (java.lang.reflect.Method) (methodidx >= 0 ? methods.get(methodidx) : null); | |
1365 if(method == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) | |
1366 { | |
1367 RT.errPrintWriter() | |
1368 .format("Reflection warning, %s:%d - call to %s can't be resolved.\n", | |
1369 SOURCE_PATH.deref(), line, methodName); | |
1370 } | |
1371 } | |
1372 | |
1373 public Object eval() throws Exception{ | |
1374 try | |
1375 { | |
1376 Object[] argvals = new Object[args.count()]; | |
1377 for(int i = 0; i < args.count(); i++) | |
1378 argvals[i] = ((Expr) args.nth(i)).eval(); | |
1379 if(method != null) | |
1380 { | |
1381 LinkedList ms = new LinkedList(); | |
1382 ms.add(method); | |
1383 return Reflector.invokeMatchingMethod(methodName, ms, null, argvals); | |
1384 } | |
1385 return Reflector.invokeStaticMethod(c, methodName, argvals); | |
1386 } | |
1387 catch(Throwable e) | |
1388 { | |
1389 if(!(e instanceof CompilerException)) | |
1390 throw new CompilerException(source, line, e); | |
1391 else | |
1392 throw (CompilerException) e; | |
1393 } | |
1394 } | |
1395 | |
1396 public boolean canEmitPrimitive(){ | |
1397 return method != null && Util.isPrimitive(method.getReturnType()); | |
1398 } | |
1399 | |
1400 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){ | |
1401 gen.visitLineNumber(line, gen.mark()); | |
1402 if(method != null) | |
1403 { | |
1404 MethodExpr.emitTypedArgs(objx, gen, method.getParameterTypes(), args); | |
1405 //Type type = Type.getObjectType(className.replace('.', '/')); | |
1406 if(context == C.RETURN) | |
1407 { | |
1408 ObjMethod method = (ObjMethod) METHOD.deref(); | |
1409 method.emitClearLocals(gen); | |
1410 } | |
1411 Type type = Type.getType(c); | |
1412 Method m = new Method(methodName, Type.getReturnType(method), Type.getArgumentTypes(method)); | |
1413 gen.invokeStatic(type, m); | |
1414 } | |
1415 else | |
1416 throw new UnsupportedOperationException("Unboxed emit of unknown member"); | |
1417 } | |
1418 | |
1419 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
1420 gen.visitLineNumber(line, gen.mark()); | |
1421 if(method != null) | |
1422 { | |
1423 MethodExpr.emitTypedArgs(objx, gen, method.getParameterTypes(), args); | |
1424 //Type type = Type.getObjectType(className.replace('.', '/')); | |
1425 if(context == C.RETURN) | |
1426 { | |
1427 ObjMethod method = (ObjMethod) METHOD.deref(); | |
1428 method.emitClearLocals(gen); | |
1429 } | |
1430 Type type = Type.getType(c); | |
1431 Method m = new Method(methodName, Type.getReturnType(method), Type.getArgumentTypes(method)); | |
1432 gen.invokeStatic(type, m); | |
1433 //if(context != C.STATEMENT || method.getReturnType() == Void.TYPE) | |
1434 HostExpr.emitBoxReturn(objx, gen, method.getReturnType()); | |
1435 } | |
1436 else | |
1437 { | |
1438 gen.push(c.getName()); | |
1439 gen.invokeStatic(CLASS_TYPE, forNameMethod); | |
1440 gen.push(methodName); | |
1441 emitArgsAsArray(args, objx, gen); | |
1442 if(context == C.RETURN) | |
1443 { | |
1444 ObjMethod method = (ObjMethod) METHOD.deref(); | |
1445 method.emitClearLocals(gen); | |
1446 } | |
1447 gen.invokeStatic(REFLECTOR_TYPE, invokeStaticMethodMethod); | |
1448 } | |
1449 if(context == C.STATEMENT) | |
1450 gen.pop(); | |
1451 } | |
1452 | |
1453 public boolean hasJavaClass(){ | |
1454 return method != null || tag != null; | |
1455 } | |
1456 | |
1457 public Class getJavaClass() throws Exception{ | |
1458 return tag != null ? HostExpr.tagToClass(tag) : method.getReturnType(); | |
1459 } | |
1460 } | |
1461 | |
1462 static class UnresolvedVarExpr implements Expr{ | |
1463 public final Symbol symbol; | |
1464 | |
1465 public UnresolvedVarExpr(Symbol symbol){ | |
1466 this.symbol = symbol; | |
1467 } | |
1468 | |
1469 public boolean hasJavaClass(){ | |
1470 return false; | |
1471 } | |
1472 | |
1473 public Class getJavaClass() throws Exception{ | |
1474 throw new IllegalArgumentException( | |
1475 "UnresolvedVarExpr has no Java class"); | |
1476 } | |
1477 | |
1478 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
1479 } | |
1480 | |
1481 public Object eval() throws Exception{ | |
1482 throw new IllegalArgumentException( | |
1483 "UnresolvedVarExpr cannot be evalled"); | |
1484 } | |
1485 } | |
1486 | |
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; | |
1492 | |
1493 public ConstantExpr(Object v){ | |
1494 this.v = v; | |
1495 this.id = registerConstant(v); | |
1496 // this.id = RT.nextID(); | |
1497 // DynamicClassLoader loader = (DynamicClassLoader) LOADER.get(); | |
1498 // loader.registerQuotedVal(id, v); | |
1499 } | |
1500 | |
1501 Object val(){ | |
1502 return v; | |
1503 } | |
1504 | |
1505 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
1506 objx.emitConstant(gen, id); | |
1507 if(context == C.STATEMENT) | |
1508 { | |
1509 gen.pop(); | |
1510 // gen.loadThis(); | |
1511 // gen.invokeVirtual(OBJECT_TYPE, getClassMethod); | |
1512 // gen.invokeVirtual(CLASS_TYPE, getClassLoaderMethod); | |
1513 // gen.checkCast(DYNAMIC_CLASSLOADER_TYPE); | |
1514 // gen.push(id); | |
1515 // gen.invokeVirtual(DYNAMIC_CLASSLOADER_TYPE, getQuotedValMethod); | |
1516 } | |
1517 } | |
1518 | |
1519 public boolean hasJavaClass(){ | |
1520 return Modifier.isPublic(v.getClass().getModifiers()); | |
1521 //return false; | |
1522 } | |
1523 | |
1524 public Class getJavaClass() throws Exception{ | |
1525 return v.getClass(); | |
1526 //throw new IllegalArgumentException("Has no Java class"); | |
1527 } | |
1528 | |
1529 static class Parser implements IParser{ | |
1530 public Expr parse(C context, Object form){ | |
1531 Object v = RT.second(form); | |
1532 | |
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); | |
1548 } | |
1549 } | |
1550 } | |
1551 | |
1552 static class NilExpr extends LiteralExpr{ | |
1553 Object val(){ | |
1554 return null; | |
1555 } | |
1556 | |
1557 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
1558 gen.visitInsn(Opcodes.ACONST_NULL); | |
1559 if(context == C.STATEMENT) | |
1560 gen.pop(); | |
1561 } | |
1562 | |
1563 public boolean hasJavaClass(){ | |
1564 return true; | |
1565 } | |
1566 | |
1567 public Class getJavaClass() throws Exception{ | |
1568 return null; | |
1569 } | |
1570 } | |
1571 | |
1572 final static NilExpr NIL_EXPR = new NilExpr(); | |
1573 | |
1574 static class BooleanExpr extends LiteralExpr{ | |
1575 public final boolean val; | |
1576 | |
1577 | |
1578 public BooleanExpr(boolean val){ | |
1579 this.val = val; | |
1580 } | |
1581 | |
1582 Object val(){ | |
1583 return val ? RT.T : RT.F; | |
1584 } | |
1585 | |
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) | |
1592 { | |
1593 gen.pop(); | |
1594 } | |
1595 } | |
1596 | |
1597 public boolean hasJavaClass(){ | |
1598 return true; | |
1599 } | |
1600 | |
1601 public Class getJavaClass() throws Exception{ | |
1602 return Boolean.class; | |
1603 } | |
1604 } | |
1605 | |
1606 final static BooleanExpr TRUE_EXPR = new BooleanExpr(true); | |
1607 final static BooleanExpr FALSE_EXPR = new BooleanExpr(false); | |
1608 | |
1609 static class StringExpr extends LiteralExpr{ | |
1610 public final String str; | |
1611 | |
1612 public StringExpr(String str){ | |
1613 this.str = str; | |
1614 } | |
1615 | |
1616 Object val(){ | |
1617 return str; | |
1618 } | |
1619 | |
1620 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
1621 if(context != C.STATEMENT) | |
1622 gen.push(str); | |
1623 } | |
1624 | |
1625 public boolean hasJavaClass(){ | |
1626 return true; | |
1627 } | |
1628 | |
1629 public Class getJavaClass() throws Exception{ | |
1630 return String.class; | |
1631 } | |
1632 } | |
1633 | |
1634 | |
1635 static class MonitorEnterExpr extends UntypedExpr{ | |
1636 final Expr target; | |
1637 | |
1638 public MonitorEnterExpr(Expr target){ | |
1639 this.target = target; | |
1640 } | |
1641 | |
1642 public Object eval() throws Exception{ | |
1643 throw new UnsupportedOperationException("Can't eval monitor-enter"); | |
1644 } | |
1645 | |
1646 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
1647 target.emit(C.EXPRESSION, objx, gen); | |
1648 gen.monitorEnter(); | |
1649 NIL_EXPR.emit(context, objx, gen); | |
1650 } | |
1651 | |
1652 static class Parser implements IParser{ | |
1653 public Expr parse(C context, Object form) throws Exception{ | |
1654 return new MonitorEnterExpr(analyze(C.EXPRESSION, RT.second(form))); | |
1655 } | |
1656 } | |
1657 } | |
1658 | |
1659 static class MonitorExitExpr extends UntypedExpr{ | |
1660 final Expr target; | |
1661 | |
1662 public MonitorExitExpr(Expr target){ | |
1663 this.target = target; | |
1664 } | |
1665 | |
1666 public Object eval() throws Exception{ | |
1667 throw new UnsupportedOperationException("Can't eval monitor-exit"); | |
1668 } | |
1669 | |
1670 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
1671 target.emit(C.EXPRESSION, objx, gen); | |
1672 gen.monitorExit(); | |
1673 NIL_EXPR.emit(context, objx, gen); | |
1674 } | |
1675 | |
1676 static class Parser implements IParser{ | |
1677 public Expr parse(C context, Object form) throws Exception{ | |
1678 return new MonitorExitExpr(analyze(C.EXPRESSION, RT.second(form))); | |
1679 } | |
1680 } | |
1681 | |
1682 } | |
1683 | |
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; | |
1690 | |
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; | |
1698 | |
1699 | |
1700 public CatchClause(Class c, LocalBinding lb, Expr handler){ | |
1701 this.c = c; | |
1702 this.lb = lb; | |
1703 this.handler = handler; | |
1704 } | |
1705 } | |
1706 | |
1707 public TryExpr(Expr tryExpr, PersistentVector catchExprs, Expr finallyExpr, int retLocal, int finallyLocal){ | |
1708 this.tryExpr = tryExpr; | |
1709 this.catchExprs = catchExprs; | |
1710 this.finallyExpr = finallyExpr; | |
1711 this.retLocal = retLocal; | |
1712 this.finallyLocal = finallyLocal; | |
1713 } | |
1714 | |
1715 public Object eval() throws Exception{ | |
1716 throw new UnsupportedOperationException("Can't eval try"); | |
1717 } | |
1718 | |
1719 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
1720 Label startTry = gen.newLabel(); | |
1721 Label endTry = gen.newLabel(); | |
1722 Label end = gen.newLabel(); | |
1723 Label ret = gen.newLabel(); | |
1724 Label finallyLabel = gen.newLabel(); | |
1725 for(int i = 0; i < catchExprs.count(); i++) | |
1726 { | |
1727 CatchClause clause = (CatchClause) catchExprs.nth(i); | |
1728 clause.label = gen.newLabel(); | |
1729 clause.endLabel = gen.newLabel(); | |
1730 } | |
1731 | |
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); | |
1740 | |
1741 for(int i = 0; i < catchExprs.count(); i++) | |
1742 { | |
1743 CatchClause clause = (CatchClause) catchExprs.nth(i); | |
1744 gen.mark(clause.label); | |
1745 //exception should be on 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); | |
1752 | |
1753 if(finallyExpr != null) | |
1754 finallyExpr.emit(C.STATEMENT, objx, gen); | |
1755 gen.goTo(ret); | |
1756 } | |
1757 if(finallyExpr != null) | |
1758 { | |
1759 gen.mark(finallyLabel); | |
1760 //exception should be on 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(); | |
1765 } | |
1766 gen.mark(ret); | |
1767 if(context != C.STATEMENT) | |
1768 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), retLocal); | |
1769 gen.mark(end); | |
1770 for(int i = 0; i < catchExprs.count(); i++) | |
1771 { | |
1772 CatchClause clause = (CatchClause) catchExprs.nth(i); | |
1773 gen.visitTryCatchBlock(startTry, endTry, clause.label, clause.c.getName().replace('.', '/')); | |
1774 } | |
1775 if(finallyExpr != null) | |
1776 { | |
1777 gen.visitTryCatchBlock(startTry, endTry, finallyLabel, null); | |
1778 for(int i = 0; i < catchExprs.count(); i++) | |
1779 { | |
1780 CatchClause clause = (CatchClause) catchExprs.nth(i); | |
1781 gen.visitTryCatchBlock(clause.label, clause.endLabel, finallyLabel, null); | |
1782 } | |
1783 } | |
1784 for(int i = 0; i < catchExprs.count(); i++) | |
1785 { | |
1786 CatchClause clause = (CatchClause) catchExprs.nth(i); | |
1787 gen.visitLocalVariable(clause.lb.name, "Ljava/lang/Object;", null, clause.label, clause.endLabel, | |
1788 clause.lb.idx); | |
1789 } | |
1790 } | |
1791 | |
1792 public boolean hasJavaClass() throws Exception{ | |
1793 return tryExpr.hasJavaClass(); | |
1794 } | |
1795 | |
1796 public Class getJavaClass() throws Exception{ | |
1797 return tryExpr.getJavaClass(); | |
1798 } | |
1799 | |
1800 static class Parser implements IParser{ | |
1801 | |
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))); | |
1807 | |
1808 //(try try-expr* catch-expr* finally-expr?) | |
1809 //catch-expr: (catch class sym expr*) | |
1810 //finally-expr: (finally expr*) | |
1811 | |
1812 PersistentVector body = PersistentVector.EMPTY; | |
1813 PersistentVector catches = PersistentVector.EMPTY; | |
1814 Expr bodyExpr = null; | |
1815 Expr finallyExpr = null; | |
1816 boolean caught = false; | |
1817 | |
1818 int retLocal = getAndIncLocalNum(); | |
1819 int finallyLocal = getAndIncLocalNum(); | |
1820 for(ISeq fs = form.next(); fs != null; fs = fs.next()) | |
1821 { | |
1822 Object f = fs.first(); | |
1823 Object op = (f instanceof ISeq) ? ((ISeq) f).first() : null; | |
1824 if(!Util.equals(op, CATCH) && !Util.equals(op, FINALLY)) | |
1825 { | |
1826 if(caught) | |
1827 throw new Exception("Only catch or finally clause can follow catch in try expression"); | |
1828 body = body.cons(f); | |
1829 } | |
1830 else | |
1831 { | |
1832 if(bodyExpr == null) | |
1833 bodyExpr = (new BodyExpr.Parser()).parse(context, RT.seq(body)); | |
1834 if(Util.equals(op, CATCH)) | |
1835 { | |
1836 Class c = HostExpr.maybeClass(RT.second(f), false); | |
1837 if(c == null) | |
1838 throw new IllegalArgumentException("Unable to resolve classname: " + RT.second(f)); | |
1839 if(!(RT.third(f) instanceof Symbol)) | |
1840 throw new IllegalArgumentException( | |
1841 "Bad binding form, expected symbol, got: " + RT.third(f)); | |
1842 Symbol sym = (Symbol) RT.third(f); | |
1843 if(sym.getNamespace() != null) | |
1844 throw new Exception("Can't bind qualified name:" + sym); | |
1845 | |
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 | |
1850 { | |
1851 Var.pushThreadBindings(dynamicBindings); | |
1852 LocalBinding lb = registerLocal(sym, | |
1853 (Symbol) (RT.second(f) instanceof Symbol ? RT.second(f) | |
1854 : null), | |
1855 null,false); | |
1856 Expr handler = (new BodyExpr.Parser()).parse(context, RT.next(RT.next(RT.next(f)))); | |
1857 catches = catches.cons(new CatchClause(c, lb, handler)); | |
1858 } | |
1859 finally | |
1860 { | |
1861 Var.popThreadBindings(); | |
1862 } | |
1863 caught = true; | |
1864 } | |
1865 else //finally | |
1866 { | |
1867 if(fs.next() != null) | |
1868 throw new Exception("finally clause must be last in try expression"); | |
1869 try | |
1870 { | |
1871 Var.pushThreadBindings(RT.map(IN_CATCH_FINALLY, RT.T)); | |
1872 finallyExpr = (new BodyExpr.Parser()).parse(C.STATEMENT, RT.next(f)); | |
1873 } | |
1874 finally | |
1875 { | |
1876 Var.popThreadBindings(); | |
1877 } | |
1878 } | |
1879 } | |
1880 } | |
1881 if(bodyExpr == null) | |
1882 bodyExpr = (new BodyExpr.Parser()).parse(context, RT.seq(body)); | |
1883 | |
1884 return new TryExpr(bodyExpr, catches, finallyExpr, retLocal, | |
1885 finallyLocal); | |
1886 } | |
1887 } | |
1888 } | |
1889 | |
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 //} | |
1946 | |
1947 static class ThrowExpr extends UntypedExpr{ | |
1948 public final Expr excExpr; | |
1949 | |
1950 public ThrowExpr(Expr excExpr){ | |
1951 this.excExpr = excExpr; | |
1952 } | |
1953 | |
1954 | |
1955 public Object eval() throws Exception{ | |
1956 throw new Exception("Can't eval throw"); | |
1957 } | |
1958 | |
1959 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
1960 excExpr.emit(C.EXPRESSION, objx, gen); | |
1961 gen.checkCast(THROWABLE_TYPE); | |
1962 gen.throwException(); | |
1963 } | |
1964 | |
1965 static class Parser implements IParser{ | |
1966 public Expr parse(C context, Object form) throws Exception{ | |
1967 if(context == C.EVAL) | |
1968 return analyze(context, RT.list(RT.list(FN, PersistentVector.EMPTY, form))); | |
1969 return new ThrowExpr(analyze(C.EXPRESSION, RT.second(form))); | |
1970 } | |
1971 } | |
1972 } | |
1973 | |
1974 | |
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++) | |
1979 { | |
1980 if(c1[i] != c2[i])// || c2[i].isPrimitive() && c1[i] == Object.class)) | |
1981 { | |
1982 if(!c1[i].isPrimitive() && c2[i].isPrimitive() | |
1983 //|| Number.class.isAssignableFrom(c1[i]) && c2[i].isPrimitive() | |
1984 || | |
1985 c2[i].isAssignableFrom(c1[i])) | |
1986 better = true; | |
1987 else | |
1988 return false; | |
1989 } | |
1990 } | |
1991 return better; | |
1992 } | |
1993 | |
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++) | |
2002 { | |
2003 boolean match = true; | |
2004 ISeq aseq = argexprs.seq(); | |
2005 int exact = 0; | |
2006 for(int p = 0; match && p < argexprs.count() && aseq != null; ++p, aseq = aseq.next()) | |
2007 { | |
2008 Expr arg = (Expr) aseq.first(); | |
2009 Class aclass = arg.hasJavaClass() ? arg.getJavaClass() : Object.class; | |
2010 Class pclass = paramlists.get(i)[p]; | |
2011 if(arg.hasJavaClass() && aclass == pclass) | |
2012 exact++; | |
2013 else | |
2014 match = Reflector.paramArgTypeMatch(pclass, aclass); | |
2015 } | |
2016 if(exact == argexprs.count()) | |
2017 { | |
2018 if(!foundExact || matchIdx == -1 || rets.get(matchIdx).isAssignableFrom(rets.get(i))) | |
2019 matchIdx = i; | |
2020 foundExact = true; | |
2021 } | |
2022 else if(match && !foundExact) | |
2023 { | |
2024 if(matchIdx == -1) | |
2025 matchIdx = i; | |
2026 else | |
2027 { | |
2028 if(subsumes(paramlists.get(i), paramlists.get(matchIdx))) | |
2029 { | |
2030 matchIdx = i; | |
2031 tied = false; | |
2032 } | |
2033 else if(Arrays.equals(paramlists.get(matchIdx), paramlists.get(i))) | |
2034 { | |
2035 if(rets.get(matchIdx).isAssignableFrom(rets.get(i))) | |
2036 matchIdx = i; | |
2037 } | |
2038 else if(!(subsumes(paramlists.get(matchIdx), paramlists.get(i)))) | |
2039 tied = true; | |
2040 } | |
2041 } | |
2042 } | |
2043 if(tied) | |
2044 throw new IllegalArgumentException("More than one matching method found: " + methodName); | |
2045 | |
2046 return matchIdx; | |
2047 } | |
2048 | |
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)"); | |
2057 | |
2058 | |
2059 public NewExpr(Class c, IPersistentVector args, int line) throws Exception{ | |
2060 this.args = args; | |
2061 this.c = c; | |
2062 Constructor[] allctors = c.getConstructors(); | |
2063 ArrayList ctors = new ArrayList(); | |
2064 ArrayList<Class[]> params = new ArrayList(); | |
2065 ArrayList<Class> rets = new ArrayList(); | |
2066 for(int i = 0; i < allctors.length; i++) | |
2067 { | |
2068 Constructor ctor = allctors[i]; | |
2069 if(ctor.getParameterTypes().length == args.count()) | |
2070 { | |
2071 ctors.add(ctor); | |
2072 params.add(ctor.getParameterTypes()); | |
2073 rets.add(c); | |
2074 } | |
2075 } | |
2076 if(ctors.isEmpty()) | |
2077 throw new IllegalArgumentException("No matching ctor found for " + c); | |
2078 | |
2079 int ctoridx = 0; | |
2080 if(ctors.size() > 1) | |
2081 { | |
2082 ctoridx = getMatchingParams(c.getName(), params, args, rets); | |
2083 } | |
2084 | |
2085 this.ctor = ctoridx >= 0 ? (Constructor) ctors.get(ctoridx) : null; | |
2086 if(ctor == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) | |
2087 { | |
2088 RT.errPrintWriter() | |
2089 .format("Reflection warning, %s:%d - call to %s ctor can't be resolved.\n", | |
2090 SOURCE_PATH.deref(), line, c.getName()); | |
2091 } | |
2092 } | |
2093 | |
2094 public Object eval() throws Exception{ | |
2095 Object[] argvals = new Object[args.count()]; | |
2096 for(int i = 0; i < args.count(); i++) | |
2097 argvals[i] = ((Expr) args.nth(i)).eval(); | |
2098 if(this.ctor != null) | |
2099 { | |
2100 return ctor.newInstance(Reflector.boxArgs(ctor.getParameterTypes(), argvals)); | |
2101 } | |
2102 return Reflector.invokeConstructor(c, argvals); | |
2103 } | |
2104 | |
2105 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
2106 if(this.ctor != null) | |
2107 { | |
2108 Type type = getType(c); | |
2109 gen.newInstance(type); | |
2110 gen.dup(); | |
2111 MethodExpr.emitTypedArgs(objx, gen, ctor.getParameterTypes(), args); | |
2112 if(context == C.RETURN) | |
2113 { | |
2114 ObjMethod method = (ObjMethod) METHOD.deref(); | |
2115 method.emitClearLocals(gen); | |
2116 } | |
2117 gen.invokeConstructor(type, new Method("<init>", Type.getConstructorDescriptor(ctor))); | |
2118 } | |
2119 else | |
2120 { | |
2121 gen.push(destubClassName(c.getName())); | |
2122 gen.invokeStatic(CLASS_TYPE, forNameMethod); | |
2123 MethodExpr.emitArgsAsArray(args, objx, gen); | |
2124 if(context == C.RETURN) | |
2125 { | |
2126 ObjMethod method = (ObjMethod) METHOD.deref(); | |
2127 method.emitClearLocals(gen); | |
2128 } | |
2129 gen.invokeStatic(REFLECTOR_TYPE, invokeConstructorMethod); | |
2130 } | |
2131 if(context == C.STATEMENT) | |
2132 gen.pop(); | |
2133 } | |
2134 | |
2135 public boolean hasJavaClass(){ | |
2136 return true; | |
2137 } | |
2138 | |
2139 public Class getJavaClass() throws Exception{ | |
2140 return c; | |
2141 } | |
2142 | |
2143 static class Parser implements IParser{ | |
2144 public Expr parse(C context, Object frm) throws Exception{ | |
2145 int line = (Integer) LINE.deref(); | |
2146 ISeq form = (ISeq) frm; | |
2147 //(new Classname args...) | |
2148 if(form.count() < 2) | |
2149 throw new Exception("wrong number of arguments, expecting: (new Classname args...)"); | |
2150 Class c = HostExpr.maybeClass(RT.second(form), false); | |
2151 if(c == null) | |
2152 throw new IllegalArgumentException("Unable to resolve classname: " + RT.second(form)); | |
2153 PersistentVector args = PersistentVector.EMPTY; | |
2154 for(ISeq s = RT.next(RT.next(form)); s != null; s = s.next()) | |
2155 args = args.cons(analyze(context == C.EVAL ? context : C.EXPRESSION, s.first())); | |
2156 return new NewExpr(c, args, line); | |
2157 } | |
2158 } | |
2159 | |
2160 } | |
2161 | |
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)"); | |
2167 | |
2168 | |
2169 public MetaExpr(Expr expr, MapExpr meta){ | |
2170 this.expr = expr; | |
2171 this.meta = meta; | |
2172 } | |
2173 | |
2174 public Object eval() throws Exception{ | |
2175 return ((IObj) expr.eval()).withMeta((IPersistentMap) meta.eval()); | |
2176 } | |
2177 | |
2178 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
2179 expr.emit(C.EXPRESSION, objx, gen); | |
2180 gen.checkCast(IOBJ_TYPE); | |
2181 meta.emit(C.EXPRESSION, objx, gen); | |
2182 gen.checkCast(IPERSISTENTMAP_TYPE); | |
2183 gen.invokeInterface(IOBJ_TYPE, withMetaMethod); | |
2184 if(context == C.STATEMENT) | |
2185 { | |
2186 gen.pop(); | |
2187 } | |
2188 } | |
2189 | |
2190 public boolean hasJavaClass() throws Exception{ | |
2191 return expr.hasJavaClass(); | |
2192 } | |
2193 | |
2194 public Class getJavaClass() throws Exception{ | |
2195 return expr.getJavaClass(); | |
2196 } | |
2197 } | |
2198 | |
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; | |
2204 | |
2205 | |
2206 public IfExpr(int line, Expr testExpr, Expr thenExpr, Expr elseExpr){ | |
2207 this.testExpr = testExpr; | |
2208 this.thenExpr = thenExpr; | |
2209 this.elseExpr = elseExpr; | |
2210 this.line = line; | |
2211 } | |
2212 | |
2213 public Object eval() throws Exception{ | |
2214 Object t = testExpr.eval(); | |
2215 if(t != null && t != Boolean.FALSE) | |
2216 return thenExpr.eval(); | |
2217 return elseExpr.eval(); | |
2218 } | |
2219 | |
2220 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
2221 doEmit(context, objx, gen,false); | |
2222 } | |
2223 | |
2224 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){ | |
2225 doEmit(context, objx, gen, true); | |
2226 } | |
2227 | |
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(); | |
2232 | |
2233 gen.visitLineNumber(line, gen.mark()); | |
2234 | |
2235 try | |
2236 { | |
2237 if(maybePrimitiveType(testExpr) == boolean.class) | |
2238 { | |
2239 ((MaybePrimitiveExpr) testExpr).emitUnboxed(C.EXPRESSION, objx, gen); | |
2240 gen.ifZCmp(gen.EQ, falseLabel); | |
2241 } | |
2242 else | |
2243 { | |
2244 testExpr.emit(C.EXPRESSION, objx, gen); | |
2245 gen.dup(); | |
2246 gen.ifNull(nullLabel); | |
2247 gen.getStatic(BOOLEAN_OBJECT_TYPE, "FALSE", BOOLEAN_OBJECT_TYPE); | |
2248 gen.visitJumpInsn(IF_ACMPEQ, falseLabel); | |
2249 } | |
2250 } | |
2251 catch(Exception e) | |
2252 { | |
2253 throw new RuntimeException(e); | |
2254 } | |
2255 if(emitUnboxed) | |
2256 ((MaybePrimitiveExpr)thenExpr).emitUnboxed(context, objx, gen); | |
2257 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); | |
2268 } | |
2269 | |
2270 public boolean hasJavaClass() throws Exception{ | |
2271 return thenExpr.hasJavaClass() | |
2272 && elseExpr.hasJavaClass() | |
2273 && | |
2274 (thenExpr.getJavaClass() == elseExpr.getJavaClass() | |
2275 || (thenExpr.getJavaClass() == null && !elseExpr.getJavaClass().isPrimitive()) | |
2276 || (elseExpr.getJavaClass() == null && !thenExpr.getJavaClass().isPrimitive())); | |
2277 } | |
2278 | |
2279 public boolean canEmitPrimitive(){ | |
2280 try | |
2281 { | |
2282 return thenExpr instanceof MaybePrimitiveExpr | |
2283 && elseExpr instanceof MaybePrimitiveExpr | |
2284 && thenExpr.getJavaClass() == elseExpr.getJavaClass() | |
2285 && ((MaybePrimitiveExpr)thenExpr).canEmitPrimitive() | |
2286 && ((MaybePrimitiveExpr)elseExpr).canEmitPrimitive(); | |
2287 } | |
2288 catch(Exception e) | |
2289 { | |
2290 return false; | |
2291 } | |
2292 } | |
2293 | |
2294 public Class getJavaClass() throws Exception{ | |
2295 Class thenClass = thenExpr.getJavaClass(); | |
2296 if(thenClass != null) | |
2297 return thenClass; | |
2298 return elseExpr.getJavaClass(); | |
2299 } | |
2300 | |
2301 static class Parser implements IParser{ | |
2302 public Expr parse(C context, Object frm) throws Exception{ | |
2303 ISeq form = (ISeq) frm; | |
2304 //(if test then) or (if test then else) | |
2305 if(form.count() > 4) | |
2306 throw new Exception("Too many arguments to if"); | |
2307 else if(form.count() < 3) | |
2308 throw new Exception("Too few arguments to if"); | |
2309 PathNode branch = new PathNode(PATHTYPE.BRANCH, (PathNode) CLEAR_PATH.get()); | |
2310 Expr testexpr = analyze(context == C.EVAL ? context : C.EXPRESSION, RT.second(form)); | |
2311 Expr thenexpr, elseexpr; | |
2312 try { | |
2313 Var.pushThreadBindings( | |
2314 RT.map(CLEAR_PATH, new PathNode(PATHTYPE.PATH,branch))); | |
2315 thenexpr = analyze(context, RT.third(form)); | |
2316 } | |
2317 finally{ | |
2318 Var.popThreadBindings(); | |
2319 } | |
2320 try { | |
2321 Var.pushThreadBindings( | |
2322 RT.map(CLEAR_PATH, new PathNode(PATHTYPE.PATH,branch))); | |
2323 elseexpr = analyze(context, RT.fourth(form)); | |
2324 } | |
2325 finally{ | |
2326 Var.popThreadBindings(); | |
2327 } | |
2328 return new IfExpr((Integer) LINE.deref(), | |
2329 testexpr, | |
2330 thenexpr, | |
2331 elseexpr); | |
2332 } | |
2333 } | |
2334 } | |
2335 | |
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_"); | |
2361 | |
2362 static public String munge(String name){ | |
2363 StringBuilder sb = new StringBuilder(); | |
2364 for(char c : name.toCharArray()) | |
2365 { | |
2366 String sub = (String) CHAR_MAP.valAt(c); | |
2367 if(sub != null) | |
2368 sb.append(sub); | |
2369 else | |
2370 sb.append(c); | |
2371 } | |
2372 return sb.toString(); | |
2373 } | |
2374 | |
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); | |
2382 | |
2383 | |
2384 public EmptyExpr(Object coll){ | |
2385 this.coll = coll; | |
2386 } | |
2387 | |
2388 public Object eval() throws Exception{ | |
2389 return coll; | |
2390 } | |
2391 | |
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) | |
2404 { | |
2405 gen.pop(); | |
2406 } | |
2407 } | |
2408 | |
2409 public boolean hasJavaClass() throws Exception{ | |
2410 return true; | |
2411 } | |
2412 | |
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"); | |
2424 } | |
2425 } | |
2426 | |
2427 public static class ListExpr implements Expr{ | |
2428 public final IPersistentVector args; | |
2429 final static Method arrayToListMethod = Method.getMethod("clojure.lang.ISeq arrayToList(Object[])"); | |
2430 | |
2431 | |
2432 public ListExpr(IPersistentVector args){ | |
2433 this.args = args; | |
2434 } | |
2435 | |
2436 public Object eval() throws Exception{ | |
2437 IPersistentVector ret = PersistentVector.EMPTY; | |
2438 for(int i = 0; i < args.count(); i++) | |
2439 ret = (IPersistentVector) ret.cons(((Expr) args.nth(i)).eval()); | |
2440 return ret.seq(); | |
2441 } | |
2442 | |
2443 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
2444 MethodExpr.emitArgsAsArray(args, objx, gen); | |
2445 gen.invokeStatic(RT_TYPE, arrayToListMethod); | |
2446 if(context == C.STATEMENT) | |
2447 gen.pop(); | |
2448 } | |
2449 | |
2450 public boolean hasJavaClass() throws Exception{ | |
2451 return true; | |
2452 } | |
2453 | |
2454 public Class getJavaClass() throws Exception{ | |
2455 return IPersistentList.class; | |
2456 } | |
2457 | |
2458 } | |
2459 | |
2460 public static class MapExpr implements Expr{ | |
2461 public final IPersistentVector keyvals; | |
2462 final static Method mapMethod = Method.getMethod("clojure.lang.IPersistentMap map(Object[])"); | |
2463 | |
2464 | |
2465 public MapExpr(IPersistentVector keyvals){ | |
2466 this.keyvals = keyvals; | |
2467 } | |
2468 | |
2469 public Object eval() throws Exception{ | |
2470 Object[] ret = new Object[keyvals.count()]; | |
2471 for(int i = 0; i < keyvals.count(); i++) | |
2472 ret[i] = ((Expr) keyvals.nth(i)).eval(); | |
2473 return RT.map(ret); | |
2474 } | |
2475 | |
2476 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
2477 MethodExpr.emitArgsAsArray(keyvals, objx, gen); | |
2478 gen.invokeStatic(RT_TYPE, mapMethod); | |
2479 if(context == C.STATEMENT) | |
2480 gen.pop(); | |
2481 } | |
2482 | |
2483 public boolean hasJavaClass() throws Exception{ | |
2484 return true; | |
2485 } | |
2486 | |
2487 public Class getJavaClass() throws Exception{ | |
2488 return IPersistentMap.class; | |
2489 } | |
2490 | |
2491 | |
2492 static public Expr parse(C context, IPersistentMap form) throws Exception{ | |
2493 IPersistentVector keyvals = PersistentVector.EMPTY; | |
2494 for(ISeq s = RT.seq(form); s != null; s = s.next()) | |
2495 { | |
2496 IMapEntry e = (IMapEntry) s.first(); | |
2497 keyvals = (IPersistentVector) keyvals.cons(analyze(context == C.EVAL ? context : C.EXPRESSION, e.key())); | |
2498 keyvals = (IPersistentVector) keyvals.cons(analyze(context == C.EVAL ? context : C.EXPRESSION, e.val())); | |
2499 } | |
2500 Expr ret = new MapExpr(keyvals); | |
2501 if(form instanceof IObj && ((IObj) form).meta() != null) | |
2502 return new MetaExpr(ret, (MapExpr) MapExpr | |
2503 .parse(context == C.EVAL ? context : C.EXPRESSION, ((IObj) form).meta())); | |
2504 else | |
2505 return ret; | |
2506 } | |
2507 } | |
2508 | |
2509 public static class SetExpr implements Expr{ | |
2510 public final IPersistentVector keys; | |
2511 final static Method setMethod = Method.getMethod("clojure.lang.IPersistentSet set(Object[])"); | |
2512 | |
2513 | |
2514 public SetExpr(IPersistentVector keys){ | |
2515 this.keys = keys; | |
2516 } | |
2517 | |
2518 public Object eval() throws Exception{ | |
2519 Object[] ret = new Object[keys.count()]; | |
2520 for(int i = 0; i < keys.count(); i++) | |
2521 ret[i] = ((Expr) keys.nth(i)).eval(); | |
2522 return RT.set(ret); | |
2523 } | |
2524 | |
2525 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
2526 MethodExpr.emitArgsAsArray(keys, objx, gen); | |
2527 gen.invokeStatic(RT_TYPE, setMethod); | |
2528 if(context == C.STATEMENT) | |
2529 gen.pop(); | |
2530 } | |
2531 | |
2532 public boolean hasJavaClass() throws Exception{ | |
2533 return true; | |
2534 } | |
2535 | |
2536 public Class getJavaClass() throws Exception{ | |
2537 return IPersistentSet.class; | |
2538 } | |
2539 | |
2540 | |
2541 static public Expr parse(C context, IPersistentSet form) throws Exception{ | |
2542 IPersistentVector keys = PersistentVector.EMPTY; | |
2543 for(ISeq s = RT.seq(form); s != null; s = s.next()) | |
2544 { | |
2545 Object e = s.first(); | |
2546 keys = (IPersistentVector) keys.cons(analyze(context == C.EVAL ? context : C.EXPRESSION, e)); | |
2547 } | |
2548 Expr ret = new SetExpr(keys); | |
2549 if(form instanceof IObj && ((IObj) form).meta() != null) | |
2550 return new MetaExpr(ret, (MapExpr) MapExpr | |
2551 .parse(context == C.EVAL ? context : C.EXPRESSION, ((IObj) form).meta())); | |
2552 else | |
2553 return ret; | |
2554 } | |
2555 } | |
2556 | |
2557 public static class VectorExpr implements Expr{ | |
2558 public final IPersistentVector args; | |
2559 final static Method vectorMethod = Method.getMethod("clojure.lang.IPersistentVector vector(Object[])"); | |
2560 | |
2561 | |
2562 public VectorExpr(IPersistentVector args){ | |
2563 this.args = args; | |
2564 } | |
2565 | |
2566 public Object eval() throws Exception{ | |
2567 IPersistentVector ret = PersistentVector.EMPTY; | |
2568 for(int i = 0; i < args.count(); i++) | |
2569 ret = (IPersistentVector) ret.cons(((Expr) args.nth(i)).eval()); | |
2570 return ret; | |
2571 } | |
2572 | |
2573 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
2574 MethodExpr.emitArgsAsArray(args, objx, gen); | |
2575 gen.invokeStatic(RT_TYPE, vectorMethod); | |
2576 if(context == C.STATEMENT) | |
2577 gen.pop(); | |
2578 } | |
2579 | |
2580 public boolean hasJavaClass() throws Exception{ | |
2581 return true; | |
2582 } | |
2583 | |
2584 public Class getJavaClass() throws Exception{ | |
2585 return IPersistentVector.class; | |
2586 } | |
2587 | |
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; | |
2598 } | |
2599 | |
2600 } | |
2601 | |
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); | |
2610 | |
2611 public KeywordInvokeExpr(String source, int line, Symbol tag, KeywordExpr kw, Expr target){ | |
2612 this.source = source; | |
2613 this.kw = kw; | |
2614 this.target = target; | |
2615 this.line = line; | |
2616 this.tag = tag; | |
2617 this.siteIndex = registerKeywordCallsite(kw.k); | |
2618 } | |
2619 | |
2620 public Object eval() throws Exception{ | |
2621 try | |
2622 { | |
2623 return kw.k.invoke(target.eval()); | |
2624 } | |
2625 catch(Throwable e) | |
2626 { | |
2627 if(!(e instanceof CompilerException)) | |
2628 throw new CompilerException(source, line, e); | |
2629 else | |
2630 throw (CompilerException) e; | |
2631 } | |
2632 } | |
2633 | |
2634 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
2635 Label endLabel = gen.newLabel(); | |
2636 Label faultLabel = gen.newLabel(); | |
2637 | |
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); | |
2648 | |
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)")); | |
2657 | |
2658 gen.mark(endLabel); | |
2659 if(context == C.STATEMENT) | |
2660 gen.pop(); | |
2661 } | |
2662 | |
2663 public void emit2(C context, ObjExpr objx, GeneratorAdapter gen){ | |
2664 Label endLabel = gen.newLabel(); | |
2665 Label faultLabel = gen.newLabel(); | |
2666 | |
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); | |
2684 | |
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)")); | |
2690 | |
2691 gen.mark(endLabel); | |
2692 if(context == C.STATEMENT) | |
2693 gen.pop(); | |
2694 } | |
2695 | |
2696 public void emitInstance(C context, ObjExpr objx, GeneratorAdapter gen){ | |
2697 gen.visitLineNumber(line, gen.mark()); | |
2698 gen.loadThis(); | |
2699 gen.getField(objx.objtype, objx.thunkName(siteIndex),ObjExpr.ILOOKUP_THUNK_TYPE); | |
2700 target.emit(C.EXPRESSION, objx, gen); | |
2701 gen.loadThis(); | |
2702 gen.getField(objx.objtype, objx.siteName(siteIndex),ObjExpr.ILOOKUP_SITE_TYPE); | |
2703 gen.loadThis(); | |
2704 gen.checkCast(Type.getType(ILookupHost.class)); | |
2705 gen.invokeInterface(ObjExpr.ILOOKUP_THUNK_TYPE, | |
2706 Method.getMethod("Object get(Object,clojure.lang.ILookupSite,clojure.lang.ILookupHost)")); | |
2707 if(context == C.STATEMENT) | |
2708 gen.pop(); | |
2709 } | |
2710 | |
2711 public void emitNormal(C context, ObjExpr objx, GeneratorAdapter gen){ | |
2712 Label slowLabel = gen.newLabel(); | |
2713 Label endLabel = gen.newLabel(); | |
2714 | |
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); | |
2723 | |
2724 gen.mark(slowLabel); | |
2725 kw.emit(C.EXPRESSION, objx, gen); | |
2726 gen.invokeStatic(RT_TYPE, new Method("get", OBJECT_TYPE, ARG_TYPES[2])); | |
2727 | |
2728 gen.mark(endLabel); | |
2729 | |
2730 if(context == C.STATEMENT) | |
2731 gen.pop(); | |
2732 } | |
2733 | |
2734 public boolean hasJavaClass() throws Exception{ | |
2735 return tag != null; | |
2736 } | |
2737 | |
2738 public Class getJavaClass() throws Exception{ | |
2739 return HostExpr.tagToClass(tag); | |
2740 } | |
2741 | |
2742 } | |
2743 //static class KeywordSiteInvokeExpr implements Expr{ | |
2744 // public final Expr site; | |
2745 // public final Object tag; | |
2746 // public final Expr target; | |
2747 // public final int line; | |
2748 // public final String source; | |
2749 // | |
2750 // public KeywordSiteInvokeExpr(String source, int line, Symbol tag, Expr site, Expr target){ | |
2751 // this.source = source; | |
2752 // this.site = site; | |
2753 // this.target = target; | |
2754 // this.line = line; | |
2755 // this.tag = tag; | |
2756 // } | |
2757 // | |
2758 // public Object eval() throws Exception{ | |
2759 // 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 //} | |
2795 | |
2796 public static class InstanceOfExpr implements Expr, MaybePrimitiveExpr{ | |
2797 Expr expr; | |
2798 Class c; | |
2799 | |
2800 public InstanceOfExpr(Class c, Expr expr){ | |
2801 this.expr = expr; | |
2802 this.c = c; | |
2803 } | |
2804 | |
2805 public Object eval() throws Exception{ | |
2806 if(c.isInstance(expr.eval())) | |
2807 return RT.T; | |
2808 return RT.F; | |
2809 } | |
2810 | |
2811 public boolean canEmitPrimitive(){ | |
2812 return true; | |
2813 } | |
2814 | |
2815 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){ | |
2816 expr.emit(C.EXPRESSION,objx,gen); | |
2817 gen.instanceOf(Type.getType(c)); | |
2818 } | |
2819 | |
2820 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
2821 emitUnboxed(context,objx,gen); | |
2822 HostExpr.emitBoxReturn(objx,gen,Boolean.TYPE); | |
2823 if(context == C.STATEMENT) | |
2824 gen.pop(); | |
2825 } | |
2826 | |
2827 public boolean hasJavaClass() throws Exception{ | |
2828 return true; | |
2829 } | |
2830 | |
2831 public Class getJavaClass() throws Exception{ | |
2832 return Boolean.TYPE; | |
2833 } | |
2834 | |
2835 } | |
2836 | |
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"); | |
2851 | |
2852 public InvokeExpr(String source, int line, Symbol tag, Expr fexpr, IPersistentVector args) throws Exception{ | |
2853 this.source = source; | |
2854 this.fexpr = fexpr; | |
2855 this.args = args; | |
2856 this.line = line; | |
2857 if(fexpr instanceof VarExpr) | |
2858 { | |
2859 Var fvar = ((VarExpr)fexpr).var; | |
2860 Var pvar = (Var)RT.get(fvar.meta(), protocolKey); | |
2861 if(pvar != null && PROTOCOL_CALLSITES.isBound()) | |
2862 { | |
2863 this.isProtocol = true; | |
2864 this.siteIndex = registerProtocolCallsite(((VarExpr)fexpr).var); | |
2865 Object pon = RT.get(pvar.get(), onKey); | |
2866 this.protocolOn = HostExpr.maybeClass(pon,false); | |
2867 if(this.protocolOn != null) | |
2868 { | |
2869 IPersistentMap mmap = (IPersistentMap) RT.get(pvar.get(), methodMapKey); | |
2870 Keyword mmapVal = (Keyword) mmap.valAt(Keyword.intern(fvar.sym)); | |
2871 if (mmapVal == null) { | |
2872 throw new IllegalArgumentException( | |
2873 "No method of interface: " + protocolOn.getName() + | |
2874 " found for function: " + fvar.sym + " of protocol: " + pvar.sym + | |
2875 " (The protocol method may have been defined before and removed.)"); | |
2876 } | |
2877 String mname = munge(mmapVal.sym.toString()); | |
2878 List methods = Reflector.getMethods(protocolOn, args.count() - 1, mname, false); | |
2879 if(methods.size() != 1) | |
2880 throw new IllegalArgumentException( | |
2881 "No single method: " + mname + " of interface: " + protocolOn.getName() + | |
2882 " found for function: " + fvar.sym + " of protocol: " + pvar.sym); | |
2883 this.onMethod = (java.lang.reflect.Method) methods.get(0); | |
2884 } | |
2885 } | |
2886 // else if(pvar == null && VAR_CALLSITES.isBound() | |
2887 // && fvar.ns.name.name.startsWith("clojure") | |
2888 // && !RT.booleanCast(RT.get(RT.meta(fvar),dynamicKey)) | |
2889 // ) | |
2890 // { | |
2891 // //todo - more specific criteria for binding these | |
2892 // this.isDirect = true; | |
2893 // this.siteIndex = registerVarCallsite(((VarExpr) fexpr).var); | |
2894 // } | |
2895 } | |
2896 this.tag = tag != null ? tag : (fexpr instanceof VarExpr ? ((VarExpr) fexpr).tag : null); | |
2897 } | |
2898 | |
2899 public Object eval() throws Exception{ | |
2900 try | |
2901 { | |
2902 IFn fn = (IFn) fexpr.eval(); | |
2903 PersistentVector argvs = PersistentVector.EMPTY; | |
2904 for(int i = 0; i < args.count(); i++) | |
2905 argvs = argvs.cons(((Expr) args.nth(i)).eval()); | |
2906 return fn.applyTo(RT.seq(argvs)); | |
2907 } | |
2908 catch(Throwable e) | |
2909 { | |
2910 if(!(e instanceof CompilerException)) | |
2911 throw new CompilerException(source, line, e); | |
2912 else | |
2913 throw (CompilerException) e; | |
2914 } | |
2915 } | |
2916 | |
2917 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
2918 gen.visitLineNumber(line, gen.mark()); | |
2919 if(isProtocol) | |
2920 { | |
2921 emitProto(context,objx,gen); | |
2922 } | |
2923 else if(isDirect) | |
2924 { | |
2925 Label callLabel = gen.newLabel(); | |
2926 | |
2927 gen.getStatic(objx.objtype, objx.varCallsiteName(siteIndex), IFN_TYPE); | |
2928 gen.dup(); | |
2929 gen.ifNonNull(callLabel); | |
2930 | |
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); | |
2936 | |
2937 gen.mark(callLabel); | |
2938 emitArgsAndCall(0, context,objx,gen); | |
2939 } | |
2940 else | |
2941 { | |
2942 fexpr.emit(C.EXPRESSION, objx, gen); | |
2943 gen.checkCast(IFN_TYPE); | |
2944 emitArgsAndCall(0, context,objx,gen); | |
2945 } | |
2946 if(context == C.STATEMENT) | |
2947 gen.pop(); | |
2948 } | |
2949 | |
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(); | |
2954 | |
2955 Var v = ((VarExpr)fexpr).var; | |
2956 | |
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) | |
2965 { | |
2966 gen.dup(); //target, target | |
2967 gen.instanceOf(Type.getType(protocolOn)); | |
2968 gen.ifZCmp(GeneratorAdapter.NE, onLabel); | |
2969 } | |
2970 | |
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); | |
2982 | |
2983 gen.mark(onLabel); //target | |
2984 if(protocolOn != null) | |
2985 { | |
2986 MethodExpr.emitTypedArgs(objx, gen, onMethod.getParameterTypes(), RT.subvec(args,1,args.count())); | |
2987 if(context == C.RETURN) | |
2988 { | |
2989 ObjMethod method = (ObjMethod) METHOD.deref(); | |
2990 method.emitClearLocals(gen); | |
2991 } | |
2992 Method m = new Method(onMethod.getName(), Type.getReturnType(onMethod), Type.getArgumentTypes(onMethod)); | |
2993 gen.invokeInterface(Type.getType(protocolOn), m); | |
2994 HostExpr.emitBoxReturn(objx, gen, onMethod.getReturnType()); | |
2995 } | |
2996 gen.mark(endLabel); | |
2997 } | |
2998 | |
2999 void emitArgsAndCall(int firstArgToEmit, C context, ObjExpr objx, GeneratorAdapter gen){ | |
3000 for(int i = firstArgToEmit; i < Math.min(MAX_POSITIONAL_ARITY, args.count()); i++) | |
3001 { | |
3002 Expr e = (Expr) args.nth(i); | |
3003 e.emit(C.EXPRESSION, objx, gen); | |
3004 } | |
3005 if(args.count() > MAX_POSITIONAL_ARITY) | |
3006 { | |
3007 PersistentVector restArgs = PersistentVector.EMPTY; | |
3008 for(int i = MAX_POSITIONAL_ARITY; i < args.count(); i++) | |
3009 { | |
3010 restArgs = restArgs.cons(args.nth(i)); | |
3011 } | |
3012 MethodExpr.emitArgsAsArray(restArgs, objx, gen); | |
3013 } | |
3014 | |
3015 if(context == C.RETURN) | |
3016 { | |
3017 ObjMethod method = (ObjMethod) METHOD.deref(); | |
3018 method.emitClearLocals(gen); | |
3019 } | |
3020 | |
3021 gen.invokeInterface(IFN_TYPE, new Method("invoke", OBJECT_TYPE, ARG_TYPES[Math.min(MAX_POSITIONAL_ARITY + 1, | |
3022 args.count())])); | |
3023 } | |
3024 | |
3025 public boolean hasJavaClass() throws Exception{ | |
3026 return tag != null; | |
3027 } | |
3028 | |
3029 public Class getJavaClass() throws Exception{ | |
3030 return HostExpr.tagToClass(tag); | |
3031 } | |
3032 | |
3033 static public Expr parse(C context, ISeq form) throws Exception{ | |
3034 if(context != C.EVAL) | |
3035 context = C.EXPRESSION; | |
3036 Expr fexpr = analyze(context, form.first()); | |
3037 if(fexpr instanceof VarExpr && ((VarExpr)fexpr).var.equals(INSTANCE)) | |
3038 { | |
3039 if(RT.second(form) instanceof Symbol) | |
3040 { | |
3041 Class c = HostExpr.maybeClass(RT.second(form),false); | |
3042 if(c != null) | |
3043 return new InstanceOfExpr(c, analyze(context, RT.third(form))); | |
3044 } | |
3045 } | |
3046 | |
3047 if(fexpr instanceof KeywordExpr && RT.count(form) == 2 && KEYWORD_CALLSITES.isBound()) | |
3048 { | |
3049 // fexpr = new ConstantExpr(new KeywordCallSite(((KeywordExpr)fexpr).k)); | |
3050 Expr target = analyze(context, RT.second(form)); | |
3051 return new KeywordInvokeExpr((String) SOURCE.deref(), (Integer) LINE.deref(), tagOf(form), | |
3052 (KeywordExpr) fexpr, target); | |
3053 } | |
3054 PersistentVector args = PersistentVector.EMPTY; | |
3055 for(ISeq s = RT.seq(form.next()); s != null; s = s.next()) | |
3056 { | |
3057 args = args.cons(analyze(context, s.first())); | |
3058 } | |
3059 // if(args.count() > MAX_POSITIONAL_ARITY) | |
3060 // throw new IllegalArgumentException( | |
3061 // String.format("No more than %d args supported", MAX_POSITIONAL_ARITY)); | |
3062 | |
3063 return new InvokeExpr((String) SOURCE.deref(), (Integer) LINE.deref(), tagOf(form), fexpr, args); | |
3064 } | |
3065 } | |
3066 | |
3067 static class SourceDebugExtensionAttribute extends Attribute{ | |
3068 public SourceDebugExtensionAttribute(){ | |
3069 super("SourceDebugExtension"); | |
3070 } | |
3071 | |
3072 void writeSMAP(ClassWriter cw, String smap){ | |
3073 ByteVector bv = write(cw, null, -1, -1, -1); | |
3074 bv.putUTF8(smap); | |
3075 } | |
3076 } | |
3077 | |
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; | |
3085 | |
3086 public FnExpr(Object tag){ | |
3087 super(tag); | |
3088 } | |
3089 | |
3090 public boolean hasJavaClass() throws Exception{ | |
3091 return true; | |
3092 } | |
3093 | |
3094 public Class getJavaClass() throws Exception{ | |
3095 return AFunction.class; | |
3096 } | |
3097 | |
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()) | |
3101 { | |
3102 ObjMethod method = (ObjMethod) s.first(); | |
3103 method.emit(this, cv); | |
3104 } | |
3105 | |
3106 if(isVariadic()) | |
3107 { | |
3108 GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC, | |
3109 Method.getMethod("int getRequiredArity()"), | |
3110 null, | |
3111 null, | |
3112 cv); | |
3113 gen.visitCode(); | |
3114 gen.push(variadicMethod.reqParms.count()); | |
3115 gen.returnValue(); | |
3116 gen.endMethod(); | |
3117 } | |
3118 } | |
3119 | |
3120 static Expr parse(C context, ISeq form, String name) throws Exception{ | |
3121 ISeq origForm = form; | |
3122 FnExpr fn = new FnExpr(tagOf(form)); | |
3123 fn.src = form; | |
3124 ObjMethod enclosingMethod = (ObjMethod) METHOD.deref(); | |
3125 if(((IMeta) form.first()).meta() != null) | |
3126 { | |
3127 fn.onceOnly = RT.booleanCast(RT.get(RT.meta(form.first()), Keyword.intern(null, "once"))); | |
3128 // fn.superName = (String) RT.get(RT.meta(form.first()), Keyword.intern(null, "super-name")); | |
3129 } | |
3130 //fn.thisName = name; | |
3131 String basename = enclosingMethod != null ? | |
3132 (enclosingMethod.objx.name + "$") | |
3133 : //"clojure.fns." + | |
3134 (munge(currentNS().name.name) + "$"); | |
3135 if(RT.second(form) instanceof Symbol) | |
3136 name = ((Symbol) RT.second(form)).name; | |
3137 String simpleName = name != null ? | |
3138 (munge(name).replace(".", "_DOT_") | |
3139 + (enclosingMethod != null ? "__" + RT.nextID() : "")) | |
3140 : ("fn" | |
3141 + "__" + RT.nextID()); | |
3142 fn.name = basename + simpleName; | |
3143 fn.internalName = fn.name.replace('.', '/'); | |
3144 fn.objtype = Type.getObjectType(fn.internalName); | |
3145 try | |
3146 { | |
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 )); | |
3156 | |
3157 //arglist might be preceded by symbol naming this fn | |
3158 if(RT.second(form) instanceof Symbol) | |
3159 { | |
3160 fn.thisName = ((Symbol) RT.second(form)).name; | |
3161 form = RT.cons(FN, RT.next(RT.next(form))); | |
3162 } | |
3163 | |
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)) | |
3172 { | |
3173 FnMethod f = FnMethod.parse(fn, (ISeq) RT.first(s)); | |
3174 if(f.isVariadic()) | |
3175 { | |
3176 if(variadicMethod == null) | |
3177 variadicMethod = f; | |
3178 else | |
3179 throw new Exception("Can't have more than 1 variadic overload"); | |
3180 } | |
3181 else if(methodArray[f.reqParms.count()] == null) | |
3182 methodArray[f.reqParms.count()] = f; | |
3183 else | |
3184 throw new Exception("Can't have 2 overloads with same arity"); | |
3185 } | |
3186 if(variadicMethod != null) | |
3187 { | |
3188 for(int i = variadicMethod.reqParms.count() + 1; i <= MAX_POSITIONAL_ARITY; i++) | |
3189 if(methodArray[i] != null) | |
3190 throw new Exception( | |
3191 "Can't have fixed arity function with more params than variadic function"); | |
3192 } | |
3193 | |
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); | |
3200 | |
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(); | |
3209 | |
3210 fn.constantsID = RT.nextID(); | |
3211 // DynamicClassLoader loader = (DynamicClassLoader) LOADER.get(); | |
3212 // loader.registerConstants(fn.constantsID, fn.constants.toArray()); | |
3213 } | |
3214 finally | |
3215 { | |
3216 Var.popThreadBindings(); | |
3217 } | |
3218 fn.compile(fn.isVariadic() ? "clojure/lang/RestFn" : "clojure/lang/AFunction",null,fn.onceOnly); | |
3219 fn.getCompiledClass(); | |
3220 | |
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; | |
3226 } | |
3227 | |
3228 public final ObjMethod variadicMethod(){ | |
3229 return variadicMethod; | |
3230 } | |
3231 | |
3232 boolean isVariadic(){ | |
3233 return variadicMethod != null; | |
3234 } | |
3235 | |
3236 public final IPersistentCollection methods(){ | |
3237 return methods; | |
3238 } | |
3239 } | |
3240 | |
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; | |
3255 | |
3256 //symbol->lb | |
3257 IPersistentMap fields = null; | |
3258 | |
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; | |
3267 | |
3268 IPersistentVector keywordCallsites; | |
3269 IPersistentVector protocolCallsites; | |
3270 IPersistentVector varCallsites; | |
3271 boolean onceOnly = false; | |
3272 | |
3273 Object src; | |
3274 | |
3275 final static Method voidctor = Method.getMethod("void <init>()"); | |
3276 protected IPersistentMap classMeta; | |
3277 | |
3278 public final String name(){ | |
3279 return name; | |
3280 } | |
3281 | |
3282 // public final String simpleName(){ | |
3283 // return simpleName; | |
3284 // } | |
3285 | |
3286 public final String internalName(){ | |
3287 return internalName; | |
3288 } | |
3289 | |
3290 public final String thisName(){ | |
3291 return thisName; | |
3292 } | |
3293 | |
3294 public final Type objtype(){ | |
3295 return objtype; | |
3296 } | |
3297 | |
3298 public final IPersistentMap closes(){ | |
3299 return closes; | |
3300 } | |
3301 | |
3302 public final IPersistentMap keywords(){ | |
3303 return keywords; | |
3304 } | |
3305 | |
3306 public final IPersistentMap vars(){ | |
3307 return vars; | |
3308 } | |
3309 | |
3310 public final Class compiledClass(){ | |
3311 return compiledClass; | |
3312 } | |
3313 | |
3314 public final int line(){ | |
3315 return line; | |
3316 } | |
3317 | |
3318 public final PersistentVector constants(){ | |
3319 return constants; | |
3320 } | |
3321 | |
3322 public final int constantsID(){ | |
3323 return constantsID; | |
3324 } | |
3325 | |
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)"); | |
3330 | |
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)"); | |
3336 | |
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); | |
3340 | |
3341 private DynamicClassLoader loader; | |
3342 private byte[] bytecode; | |
3343 | |
3344 public ObjExpr(Object tag){ | |
3345 this.tag = tag; | |
3346 } | |
3347 | |
3348 static String trimGenID(String name){ | |
3349 int i = name.lastIndexOf("__"); | |
3350 return i==-1?name:name.substring(0,i); | |
3351 } | |
3352 | |
3353 | |
3354 | |
3355 Type[] ctorTypes(){ | |
3356 IPersistentVector tv = isDeftype()?PersistentVector.EMPTY:RT.vector(IPERSISTENTMAP_TYPE); | |
3357 for(ISeq s = RT.keys(closes); s != null; s = s.next()) | |
3358 { | |
3359 LocalBinding lb = (LocalBinding) s.first(); | |
3360 if(lb.getPrimitiveType() != null) | |
3361 tv = tv.cons(Type.getType(lb.getPrimitiveType())); | |
3362 else | |
3363 tv = tv.cons(OBJECT_TYPE); | |
3364 } | |
3365 Type[] ret = new Type[tv.count()]; | |
3366 for(int i = 0; i < tv.count(); i++) | |
3367 ret[i] = (Type) tv.nth(i); | |
3368 return ret; | |
3369 } | |
3370 | |
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) | |
3377 { | |
3378 if(interfaceNames == null) | |
3379 interfaceNames = new String[]{"clojure/lang/ILookupHost"}; | |
3380 else | |
3381 { | |
3382 String[] inames = new String[interfaceNames.length + 1]; | |
3383 System.arraycopy(interfaceNames,0,inames,0,interfaceNames.length); | |
3384 inames[interfaceNames.length] = "clojure/lang/ILookupHost"; | |
3385 interfaceNames = inames; | |
3386 } | |
3387 } | |
3388 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); | |
3389 // ClassWriter cw = new ClassWriter(0); | |
3390 ClassVisitor cv = cw; | |
3391 // ClassVisitor cv = new TraceClassVisitor(new CheckClassAdapter(cw), new PrintWriter(System.out)); | |
3392 //ClassVisitor cv = new TraceClassVisitor(cw, new PrintWriter(System.out)); | |
3393 cv.visit(V1_5, ACC_PUBLIC + ACC_SUPER + ACC_FINAL, internalName, null,superName,interfaceNames); | |
3394 // superName != null ? superName : | |
3395 // (isVariadic() ? "clojure/lang/RestFn" : "clojure/lang/AFunction"), null); | |
3396 String source = (String) SOURCE.deref(); | |
3397 int lineBefore = (Integer) LINE_BEFORE.deref(); | |
3398 int lineAfter = (Integer) LINE_AFTER.deref() + 1; | |
3399 | |
3400 if(source != null && SOURCE_PATH.deref() != null) | |
3401 { | |
3402 //cv.visitSource(source, null); | |
3403 String smap = "SMAP\n" + | |
3404 ((source.lastIndexOf('.') > 0) ? | |
3405 source.substring(0, source.lastIndexOf('.')) | |
3406 :source) | |
3407 // : simpleName) | |
3408 + ".java\n" + | |
3409 "Clojure\n" + | |
3410 "*S Clojure\n" + | |
3411 "*F\n" + | |
3412 "+ 1 " + source + "\n" + | |
3413 (String) SOURCE_PATH.deref() + "\n" + | |
3414 "*L\n" + | |
3415 String.format("%d#1,%d:%d\n", lineBefore, lineAfter - lineBefore, lineBefore) + | |
3416 "*E"; | |
3417 cv.visitSource(source, smap); | |
3418 } | |
3419 addAnnotation(cv, classMeta); | |
3420 //static fields for constants | |
3421 for(int i = 0; i < constants.count(); i++) | |
3422 { | |
3423 cv.visitField(ACC_PUBLIC + ACC_FINAL | |
3424 + ACC_STATIC, constantName(i), constantType(i).getDescriptor(), | |
3425 null, null); | |
3426 } | |
3427 | |
3428 //static fields for lookup sites | |
3429 for(int i = 0; i < keywordCallsites.count(); i++) | |
3430 { | |
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); | |
3436 } | |
3437 | |
3438 for(int i=0;i<varCallsites.count();i++) | |
3439 { | |
3440 cv.visitField(ACC_PRIVATE + ACC_STATIC + ACC_FINAL | |
3441 , varCallsiteName(i), IFN_TYPE.getDescriptor(), null, null); | |
3442 } | |
3443 | |
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()); | |
3452 | |
3453 if(constants.count() > 0) | |
3454 { | |
3455 emitConstants(clinitgen); | |
3456 } | |
3457 | |
3458 if(keywordCallsites.count() > 0) | |
3459 emitKeywordCallsites(clinitgen); | |
3460 | |
3461 for(int i=0;i<varCallsites.count();i++) | |
3462 { | |
3463 Label skipLabel = clinitgen.newLabel(); | |
3464 Label endLabel = clinitgen.newLabel(); | |
3465 Var var = (Var) varCallsites.nth(i); | |
3466 clinitgen.push(var.ns.name.toString()); | |
3467 clinitgen.push(var.sym.toString()); | |
3468 clinitgen.invokeStatic(RT_TYPE, Method.getMethod("clojure.lang.Var var(String,String)")); | |
3469 clinitgen.dup(); | |
3470 clinitgen.invokeVirtual(VAR_TYPE,Method.getMethod("boolean hasRoot()")); | |
3471 clinitgen.ifZCmp(GeneratorAdapter.EQ,skipLabel); | |
3472 | |
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); | |
3480 | |
3481 clinitgen.mark(skipLabel); | |
3482 clinitgen.pop(); | |
3483 | |
3484 clinitgen.mark(endLabel); | |
3485 } | |
3486 | |
3487 clinitgen.returnValue(); | |
3488 | |
3489 clinitgen.endMethod(); | |
3490 if(!isDeftype()) | |
3491 { | |
3492 cv.visitField(ACC_FINAL, "__meta", IPERSISTENTMAP_TYPE.getDescriptor(), null, null); | |
3493 } | |
3494 //instance fields for closed-overs | |
3495 for(ISeq s = RT.keys(closes); s != null; s = s.next()) | |
3496 { | |
3497 LocalBinding lb = (LocalBinding) s.first(); | |
3498 if(isDeftype()) | |
3499 { | |
3500 int access = isVolatile(lb) ? ACC_VOLATILE : | |
3501 isMutable(lb) ? 0 : | |
3502 (ACC_PUBLIC + ACC_FINAL); | |
3503 FieldVisitor fv; | |
3504 if(lb.getPrimitiveType() != null) | |
3505 fv = cv.visitField(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)); | |
3513 } | |
3514 else | |
3515 { | |
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); | |
3524 } | |
3525 } | |
3526 | |
3527 //instance fields for callsites and thunks | |
3528 for(int i=0;i<protocolCallsites.count();i++) | |
3529 { | |
3530 cv.visitField(ACC_PRIVATE, cachedClassName(i), CLASS_TYPE.getDescriptor(), null, null); | |
3531 cv.visitField(ACC_PRIVATE, cachedProtoFnName(i), AFUNCTION_TYPE.getDescriptor(), null, null); | |
3532 cv.visitField(ACC_PRIVATE, cachedProtoImplName(i), IFN_TYPE.getDescriptor(), null, null); | |
3533 } | |
3534 | |
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()) | |
3558 { | |
3559 ctorgen.loadThis(); | |
3560 ctorgen.visitVarInsn(IPERSISTENTMAP_TYPE.getOpcode(Opcodes.ILOAD), 1); | |
3561 ctorgen.putField(objtype, "__meta", IPERSISTENTMAP_TYPE); | |
3562 } | |
3563 | |
3564 int a = isDeftype()?1:2; | |
3565 for(ISeq s = RT.keys(closes); s != null; s = s.next(), ++a) | |
3566 { | |
3567 LocalBinding lb = (LocalBinding) s.first(); | |
3568 ctorgen.loadThis(); | |
3569 Class primc = lb.getPrimitiveType(); | |
3570 if(primc != null) | |
3571 { | |
3572 ctorgen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ILOAD), a); | |
3573 ctorgen.putField(objtype, lb.name, Type.getType(primc)); | |
3574 if(primc == Long.TYPE || primc == Double.TYPE) | |
3575 ++a; | |
3576 } | |
3577 else | |
3578 { | |
3579 ctorgen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), a); | |
3580 ctorgen.putField(objtype, lb.name, OBJECT_TYPE); | |
3581 } | |
3582 closesExprs = closesExprs.cons(new LocalBindingExpr(lb, null)); | |
3583 } | |
3584 | |
3585 | |
3586 ctorgen.visitLabel(end); | |
3587 | |
3588 ctorgen.returnValue(); | |
3589 | |
3590 ctorgen.endMethod(); | |
3591 | |
3592 if(altCtorDrops > 0) | |
3593 { | |
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); | |
3610 | |
3611 ctorgen.invokeConstructor(objtype, new Method("<init>", Type.VOID_TYPE, ctorTypes)); | |
3612 | |
3613 ctorgen.returnValue(); | |
3614 ctorgen.endMethod(); | |
3615 } | |
3616 | |
3617 if(!isDeftype()) | |
3618 { | |
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)); | |
3635 | |
3636 ctorgen.returnValue(); | |
3637 ctorgen.endMethod(); | |
3638 | |
3639 //meta() | |
3640 Method meth = Method.getMethod("clojure.lang.IPersistentMap meta()"); | |
3641 | |
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); | |
3650 | |
3651 gen.returnValue(); | |
3652 gen.endMethod(); | |
3653 | |
3654 //withMeta() | |
3655 meth = Method.getMethod("clojure.lang.IObj withMeta(clojure.lang.IPersistentMap)"); | |
3656 | |
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); | |
3666 | |
3667 for(ISeq s = RT.keys(closes); s != null; s = s.next(), ++a) | |
3668 { | |
3669 LocalBinding lb = (LocalBinding) s.first(); | |
3670 gen.loadThis(); | |
3671 Class primc = lb.getPrimitiveType(); | |
3672 if(primc != null) | |
3673 { | |
3674 gen.getField(objtype, lb.name, Type.getType(primc)); | |
3675 } | |
3676 else | |
3677 { | |
3678 gen.getField(objtype, lb.name, OBJECT_TYPE); | |
3679 } | |
3680 } | |
3681 | |
3682 gen.invokeConstructor(objtype, new Method("<init>", Type.VOID_TYPE, ctorTypes)); | |
3683 gen.returnValue(); | |
3684 gen.endMethod(); | |
3685 } | |
3686 | |
3687 emitMethods(cv); | |
3688 | |
3689 if(keywordCallsites.count() > 0) | |
3690 { | |
3691 Method meth = Method.getMethod("void swapThunk(int,clojure.lang.ILookupThunk)"); | |
3692 | |
3693 GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC, | |
3694 meth, | |
3695 null, | |
3696 null, | |
3697 cv); | |
3698 gen.visitCode(); | |
3699 Label endLabel = gen.newLabel(); | |
3700 | |
3701 Label[] labels = new Label[keywordCallsites.count()]; | |
3702 for(int i = 0; i < keywordCallsites.count();i++) | |
3703 { | |
3704 labels[i] = gen.newLabel(); | |
3705 } | |
3706 gen.loadArg(0); | |
3707 gen.visitTableSwitchInsn(0,keywordCallsites.count()-1,endLabel,labels); | |
3708 | |
3709 for(int i = 0; i < keywordCallsites.count();i++) | |
3710 { | |
3711 gen.mark(labels[i]); | |
3712 // gen.loadThis(); | |
3713 gen.loadArg(1); | |
3714 gen.putStatic(objtype, thunkNameStatic(i),ILOOKUP_THUNK_TYPE); | |
3715 gen.goTo(endLabel); | |
3716 } | |
3717 | |
3718 gen.mark(endLabel); | |
3719 | |
3720 gen.returnValue(); | |
3721 gen.endMethod(); | |
3722 } | |
3723 | |
3724 //end of class | |
3725 cv.visitEnd(); | |
3726 | |
3727 bytecode = cw.toByteArray(); | |
3728 if(RT.booleanCast(COMPILE_FILES.deref())) | |
3729 writeClassFile(internalName, bytecode); | |
3730 // else | |
3731 // getCompiledClass(); | |
3732 } | |
3733 | |
3734 private void emitKeywordCallsites(GeneratorAdapter clinitgen){ | |
3735 for(int i=0;i<keywordCallsites.count();i++) | |
3736 { | |
3737 Keyword k = (Keyword) keywordCallsites.nth(i); | |
3738 clinitgen.newInstance(KEYWORD_LOOKUPSITE_TYPE); | |
3739 clinitgen.dup(); | |
3740 clinitgen.push(i); | |
3741 emitValue(k,clinitgen); | |
3742 clinitgen.invokeConstructor(KEYWORD_LOOKUPSITE_TYPE, | |
3743 Method.getMethod("void <init>(int,clojure.lang.Keyword)")); | |
3744 clinitgen.dup(); | |
3745 clinitgen.putStatic(objtype, siteNameStatic(i), KEYWORD_LOOKUPSITE_TYPE); | |
3746 clinitgen.putStatic(objtype, thunkNameStatic(i), ILOOKUP_THUNK_TYPE); | |
3747 } | |
3748 } | |
3749 | |
3750 protected void emitMethods(ClassVisitor gen){ | |
3751 } | |
3752 | |
3753 void emitListAsObjectArray(Object value, GeneratorAdapter gen){ | |
3754 gen.push(((List) value).size()); | |
3755 gen.newArray(OBJECT_TYPE); | |
3756 int i = 0; | |
3757 for(Iterator it = ((List) value).iterator(); it.hasNext(); i++) | |
3758 { | |
3759 gen.dup(); | |
3760 gen.push(i); | |
3761 emitValue(it.next(), gen); | |
3762 gen.arrayStore(OBJECT_TYPE); | |
3763 } | |
3764 } | |
3765 | |
3766 void emitValue(Object value, GeneratorAdapter gen){ | |
3767 boolean partial = true; | |
3768 //System.out.println(value.getClass().toString()); | |
3769 | |
3770 if(value instanceof String) | |
3771 { | |
3772 gen.push((String) value); | |
3773 } | |
3774 else if(value instanceof Integer) | |
3775 { | |
3776 gen.push(((Integer) value).intValue()); | |
3777 gen.invokeStatic(Type.getType(Integer.class), Method.getMethod("Integer valueOf(int)")); | |
3778 } | |
3779 else if(value instanceof Double) | |
3780 { | |
3781 gen.push(((Double) value).doubleValue()); | |
3782 gen.invokeStatic(Type.getType(Double.class), Method.getMethod("Double valueOf(double)")); | |
3783 } | |
3784 else if(value instanceof Character) | |
3785 { | |
3786 gen.push(((Character) value).charValue()); | |
3787 gen.invokeStatic(Type.getType(Character.class), Method.getMethod("Character valueOf(char)")); | |
3788 } | |
3789 else if(value instanceof Class) | |
3790 { | |
3791 Class cc = (Class)value; | |
3792 if(cc.isPrimitive()) | |
3793 { | |
3794 Type bt; | |
3795 if ( cc == boolean.class ) bt = Type.getType(Boolean.class); | |
3796 else if ( cc == byte.class ) bt = Type.getType(Byte.class); | |
3797 else if ( cc == char.class ) bt = Type.getType(Character.class); | |
3798 else if ( cc == double.class ) bt = Type.getType(Double.class); | |
3799 else if ( cc == float.class ) bt = Type.getType(Float.class); | |
3800 else if ( cc == int.class ) bt = Type.getType(Integer.class); | |
3801 else if ( cc == long.class ) bt = Type.getType(Long.class); | |
3802 else if ( cc == short.class ) bt = Type.getType(Short.class); | |
3803 else throw new RuntimeException( | |
3804 "Can't embed unknown primitive in code: " + value); | |
3805 gen.getStatic( bt, "TYPE", Type.getType(Class.class) ); | |
3806 } | |
3807 else | |
3808 { | |
3809 gen.push(destubClassName(cc.getName())); | |
3810 gen.invokeStatic(Type.getType(Class.class), Method.getMethod("Class forName(String)")); | |
3811 } | |
3812 } | |
3813 else if(value instanceof Symbol) | |
3814 { | |
3815 gen.push(((Symbol) value).ns); | |
3816 gen.push(((Symbol) value).name); | |
3817 gen.invokeStatic(Type.getType(Symbol.class), | |
3818 Method.getMethod("clojure.lang.Symbol create(String,String)")); | |
3819 } | |
3820 else if(value instanceof Keyword) | |
3821 { | |
3822 emitValue(((Keyword) value).sym, gen); | |
3823 gen.invokeStatic(Type.getType(Keyword.class), | |
3824 Method.getMethod("clojure.lang.Keyword intern(clojure.lang.Symbol)")); | |
3825 } | |
3826 // else if(value instanceof KeywordCallSite) | |
3827 // { | |
3828 // emitValue(((KeywordCallSite) value).k.sym, gen); | |
3829 // gen.invokeStatic(Type.getType(KeywordCallSite.class), | |
3830 // Method.getMethod("clojure.lang.KeywordCallSite create(clojure.lang.Symbol)")); | |
3831 // } | |
3832 else if(value instanceof Var) | |
3833 { | |
3834 Var var = (Var) value; | |
3835 gen.push(var.ns.name.toString()); | |
3836 gen.push(var.sym.toString()); | |
3837 gen.invokeStatic(RT_TYPE, Method.getMethod("clojure.lang.Var var(String,String)")); | |
3838 } | |
3839 else if(value instanceof IPersistentMap) | |
3840 { | |
3841 List entries = new ArrayList(); | |
3842 for(Map.Entry entry : (Set<Map.Entry>) ((Map) value).entrySet()) | |
3843 { | |
3844 entries.add(entry.getKey()); | |
3845 entries.add(entry.getValue()); | |
3846 } | |
3847 emitListAsObjectArray(entries, gen); | |
3848 gen.invokeStatic(RT_TYPE, | |
3849 Method.getMethod("clojure.lang.IPersistentMap map(Object[])")); | |
3850 } | |
3851 else if(value instanceof IPersistentVector) | |
3852 { | |
3853 emitListAsObjectArray(value, gen); | |
3854 gen.invokeStatic(RT_TYPE, Method.getMethod( | |
3855 "clojure.lang.IPersistentVector vector(Object[])")); | |
3856 } | |
3857 else if(value instanceof ISeq || value instanceof IPersistentList) | |
3858 { | |
3859 emitListAsObjectArray(value, gen); | |
3860 gen.invokeStatic(Type.getType(java.util.Arrays.class), | |
3861 Method.getMethod("java.util.List asList(Object[])")); | |
3862 gen.invokeStatic(Type.getType(PersistentList.class), | |
3863 Method.getMethod( | |
3864 "clojure.lang.IPersistentList create(java.util.List)")); | |
3865 } | |
3866 else | |
3867 { | |
3868 String cs = null; | |
3869 try | |
3870 { | |
3871 cs = RT.printString(value); | |
3872 //System.out.println("WARNING SLOW CODE: " + value.getClass() + " -> " + cs); | |
3873 } | |
3874 catch(Exception e) | |
3875 { | |
3876 throw new RuntimeException( | |
3877 "Can't embed object in code, maybe print-dup not defined: " + | |
3878 value); | |
3879 } | |
3880 if(cs.length() == 0) | |
3881 throw new RuntimeException( | |
3882 "Can't embed unreadable object in code: " + value); | |
3883 | |
3884 if(cs.startsWith("#<")) | |
3885 throw new RuntimeException( | |
3886 "Can't embed unreadable object in code: " + cs); | |
3887 | |
3888 gen.push(cs); | |
3889 gen.invokeStatic(RT_TYPE, readStringMethod); | |
3890 partial = false; | |
3891 } | |
3892 | |
3893 if(partial) | |
3894 { | |
3895 if(value instanceof IObj && RT.count(((IObj) value).meta()) > 0) | |
3896 { | |
3897 gen.checkCast(IOBJ_TYPE); | |
3898 emitValue(((IObj) value).meta(), gen); | |
3899 gen.checkCast(IPERSISTENTMAP_TYPE); | |
3900 gen.invokeInterface(IOBJ_TYPE, | |
3901 Method.getMethod("clojure.lang.IObj withMeta(clojure.lang.IPersistentMap)")); | |
3902 } | |
3903 } | |
3904 } | |
3905 | |
3906 | |
3907 void emitConstants(GeneratorAdapter clinitgen){ | |
3908 try | |
3909 { | |
3910 Var.pushThreadBindings(RT.map(RT.PRINT_DUP, RT.T)); | |
3911 | |
3912 for(int i = 0; i < constants.count(); i++) | |
3913 { | |
3914 emitValue(constants.nth(i), clinitgen); | |
3915 clinitgen.checkCast(constantType(i)); | |
3916 clinitgen.putStatic(objtype, constantName(i), constantType(i)); | |
3917 } | |
3918 } | |
3919 finally | |
3920 { | |
3921 Var.popThreadBindings(); | |
3922 } | |
3923 } | |
3924 | |
3925 boolean isMutable(LocalBinding lb){ | |
3926 return isVolatile(lb) || | |
3927 RT.booleanCast(RT.contains(fields, lb.sym)) && | |
3928 RT.booleanCast(RT.get(lb.sym.meta(), Keyword.intern("unsynchronized-mutable"))); | |
3929 } | |
3930 | |
3931 boolean isVolatile(LocalBinding lb){ | |
3932 return RT.booleanCast(RT.contains(fields, lb.sym)) && | |
3933 RT.booleanCast(RT.get(lb.sym.meta(), Keyword.intern("volatile-mutable"))); | |
3934 } | |
3935 | |
3936 boolean isDeftype(){ | |
3937 return fields != null; | |
3938 } | |
3939 | |
3940 void emitClearCloses(GeneratorAdapter gen){ | |
3941 // int a = 1; | |
3942 // for(ISeq s = RT.keys(closes); s != null; s = s.next(), ++a) | |
3943 // { | |
3944 // LocalBinding lb = (LocalBinding) s.first(); | |
3945 // Class primc = lb.getPrimitiveType(); | |
3946 // if(primc == null) | |
3947 // { | |
3948 // gen.loadThis(); | |
3949 // gen.visitInsn(Opcodes.ACONST_NULL); | |
3950 // gen.putField(objtype, lb.name, OBJECT_TYPE); | |
3951 // } | |
3952 // } | |
3953 } | |
3954 | |
3955 synchronized Class getCompiledClass(){ | |
3956 if(compiledClass == null) | |
3957 try | |
3958 { | |
3959 // if(RT.booleanCast(COMPILE_FILES.deref())) | |
3960 // compiledClass = RT.classForName(name);//loader.defineClass(name, bytecode); | |
3961 // else | |
3962 { | |
3963 loader = (DynamicClassLoader) LOADER.deref(); | |
3964 compiledClass = loader.defineClass(name, bytecode, src); | |
3965 } | |
3966 } | |
3967 catch(Exception e) | |
3968 { | |
3969 throw new RuntimeException(e); | |
3970 } | |
3971 return compiledClass; | |
3972 } | |
3973 | |
3974 public Object eval() throws Exception{ | |
3975 if(isDeftype()) | |
3976 return null; | |
3977 return getCompiledClass().newInstance(); | |
3978 } | |
3979 | |
3980 public void emitLetFnInits(GeneratorAdapter gen, ObjExpr objx, IPersistentSet letFnLocals){ | |
3981 //objx arg is enclosing objx, not this | |
3982 gen.checkCast(objtype); | |
3983 | |
3984 for(ISeq s = RT.keys(closes); s != null; s = s.next()) | |
3985 { | |
3986 LocalBinding lb = (LocalBinding) s.first(); | |
3987 if(letFnLocals.contains(lb)) | |
3988 { | |
3989 Class primc = lb.getPrimitiveType(); | |
3990 gen.dup(); | |
3991 if(primc != null) | |
3992 { | |
3993 objx.emitUnboxedLocal(gen, lb); | |
3994 gen.putField(objtype, lb.name, Type.getType(primc)); | |
3995 } | |
3996 else | |
3997 { | |
3998 objx.emitLocal(gen, lb, false); | |
3999 gen.putField(objtype, lb.name, OBJECT_TYPE); | |
4000 } | |
4001 } | |
4002 } | |
4003 gen.pop(); | |
4004 | |
4005 } | |
4006 | |
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()) | |
4012 { | |
4013 gen.visitInsn(Opcodes.ACONST_NULL); | |
4014 } | |
4015 else | |
4016 { | |
4017 gen.newInstance(objtype); | |
4018 gen.dup(); | |
4019 gen.visitInsn(Opcodes.ACONST_NULL); | |
4020 for(ISeq s = RT.seq(closesExprs); s != null; s = s.next()) | |
4021 { | |
4022 LocalBindingExpr lbe = (LocalBindingExpr) s.first(); | |
4023 LocalBinding lb = lbe.b; | |
4024 if(lb.getPrimitiveType() != null) | |
4025 objx.emitUnboxedLocal(gen, lb); | |
4026 else | |
4027 objx.emitLocal(gen, lb, lbe.shouldClear); | |
4028 } | |
4029 gen.invokeConstructor(objtype, new Method("<init>", Type.VOID_TYPE, ctorTypes())); | |
4030 } | |
4031 if(context == C.STATEMENT) | |
4032 gen.pop(); | |
4033 } | |
4034 | |
4035 public boolean hasJavaClass() throws Exception{ | |
4036 return true; | |
4037 } | |
4038 | |
4039 public Class getJavaClass() throws Exception{ | |
4040 return (compiledClass != null) ? compiledClass | |
4041 : (tag != null) ? HostExpr.tagToClass(tag) | |
4042 : IFn.class; | |
4043 } | |
4044 | |
4045 public void emitAssignLocal(GeneratorAdapter gen, LocalBinding lb,Expr val){ | |
4046 if(!isMutable(lb)) | |
4047 throw new IllegalArgumentException("Cannot assign to non-mutable: " + lb.name); | |
4048 Class primc = lb.getPrimitiveType(); | |
4049 gen.loadThis(); | |
4050 if(primc != null) | |
4051 { | |
4052 if(!(val instanceof MaybePrimitiveExpr && ((MaybePrimitiveExpr) val).canEmitPrimitive())) | |
4053 throw new IllegalArgumentException("Must assign primitive to primitive mutable: " + lb.name); | |
4054 MaybePrimitiveExpr me = (MaybePrimitiveExpr) val; | |
4055 me.emitUnboxed(C.EXPRESSION, this, gen); | |
4056 gen.putField(objtype, lb.name, Type.getType(primc)); | |
4057 } | |
4058 else | |
4059 { | |
4060 val.emit(C.EXPRESSION, this, gen); | |
4061 gen.putField(objtype, lb.name, OBJECT_TYPE); | |
4062 } | |
4063 } | |
4064 | |
4065 private void emitLocal(GeneratorAdapter gen, LocalBinding lb, boolean clear){ | |
4066 if(closes.containsKey(lb)) | |
4067 { | |
4068 Class primc = lb.getPrimitiveType(); | |
4069 gen.loadThis(); | |
4070 if(primc != null) | |
4071 { | |
4072 gen.getField(objtype, lb.name, Type.getType(primc)); | |
4073 HostExpr.emitBoxReturn(this, gen, primc); | |
4074 } | |
4075 else | |
4076 { | |
4077 gen.getField(objtype, lb.name, OBJECT_TYPE); | |
4078 if(onceOnly && clear && lb.canBeCleared) | |
4079 { | |
4080 gen.loadThis(); | |
4081 gen.visitInsn(Opcodes.ACONST_NULL); | |
4082 gen.putField(objtype, lb.name, OBJECT_TYPE); | |
4083 } | |
4084 } | |
4085 } | |
4086 else | |
4087 { | |
4088 Class primc = lb.getPrimitiveType(); | |
4089 // String rep = lb.sym.name + " " + lb.toString().substring(lb.toString().lastIndexOf('@')); | |
4090 if(lb.isArg) | |
4091 { | |
4092 gen.loadArg(lb.idx-1); | |
4093 if(primc != null) | |
4094 HostExpr.emitBoxReturn(this, gen, primc); | |
4095 else | |
4096 { | |
4097 if(clear && lb.canBeCleared) | |
4098 { | |
4099 // System.out.println("clear: " + rep); | |
4100 gen.visitInsn(Opcodes.ACONST_NULL); | |
4101 gen.storeArg(lb.idx - 1); | |
4102 } | |
4103 else | |
4104 { | |
4105 // System.out.println("use: " + rep); | |
4106 } | |
4107 } | |
4108 } | |
4109 else | |
4110 { | |
4111 if(primc != null) | |
4112 { | |
4113 gen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ILOAD), lb.idx); | |
4114 HostExpr.emitBoxReturn(this, gen, primc); | |
4115 } | |
4116 else | |
4117 { | |
4118 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), lb.idx); | |
4119 if(clear && lb.canBeCleared) | |
4120 { | |
4121 // System.out.println("clear: " + rep); | |
4122 gen.visitInsn(Opcodes.ACONST_NULL); | |
4123 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), lb.idx); | |
4124 } | |
4125 else | |
4126 { | |
4127 // System.out.println("use: " + rep); | |
4128 } | |
4129 } | |
4130 } | |
4131 } | |
4132 } | |
4133 | |
4134 private void emitUnboxedLocal(GeneratorAdapter gen, LocalBinding lb){ | |
4135 Class primc = lb.getPrimitiveType(); | |
4136 if(closes.containsKey(lb)) | |
4137 { | |
4138 gen.loadThis(); | |
4139 gen.getField(objtype, lb.name, Type.getType(primc)); | |
4140 } | |
4141 else if(lb.isArg) | |
4142 gen.loadArg(lb.idx-1); | |
4143 else | |
4144 gen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ILOAD), lb.idx); | |
4145 } | |
4146 | |
4147 public void emitVar(GeneratorAdapter gen, Var var){ | |
4148 Integer i = (Integer) vars.valAt(var); | |
4149 emitConstant(gen, i); | |
4150 //gen.getStatic(fntype, munge(var.sym.toString()), VAR_TYPE); | |
4151 } | |
4152 | |
4153 public void emitKeyword(GeneratorAdapter gen, Keyword k){ | |
4154 Integer i = (Integer) keywords.valAt(k); | |
4155 emitConstant(gen, i); | |
4156 // gen.getStatic(fntype, munge(k.sym.toString()), KEYWORD_TYPE); | |
4157 } | |
4158 | |
4159 public void emitConstant(GeneratorAdapter gen, int id){ | |
4160 gen.getStatic(objtype, constantName(id), constantType(id)); | |
4161 } | |
4162 | |
4163 | |
4164 String constantName(int id){ | |
4165 return CONST_PREFIX + id; | |
4166 } | |
4167 | |
4168 String siteName(int n){ | |
4169 return "__site__" + n; | |
4170 } | |
4171 | |
4172 String siteNameStatic(int n){ | |
4173 return siteName(n) + "__"; | |
4174 } | |
4175 | |
4176 String thunkName(int n){ | |
4177 return "__thunk__" + n; | |
4178 } | |
4179 | |
4180 String cachedClassName(int n){ | |
4181 return "__cached_class__" + n; | |
4182 } | |
4183 | |
4184 String cachedProtoFnName(int n){ | |
4185 return "__cached_proto_fn__" + n; | |
4186 } | |
4187 | |
4188 String cachedProtoImplName(int n){ | |
4189 return "__cached_proto_impl__" + n; | |
4190 } | |
4191 | |
4192 String varCallsiteName(int n){ | |
4193 return "__var__callsite__" + n; | |
4194 } | |
4195 | |
4196 String thunkNameStatic(int n){ | |
4197 return thunkName(n) + "__"; | |
4198 } | |
4199 | |
4200 Type constantType(int id){ | |
4201 Object o = constants.nth(id); | |
4202 Class c = o.getClass(); | |
4203 if(Modifier.isPublic(c.getModifiers())) | |
4204 { | |
4205 //can't emit derived fn types due to 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); | |
4220 | |
4221 // return Type.getType(c); | |
4222 } | |
4223 return OBJECT_TYPE; | |
4224 } | |
4225 | |
4226 } | |
4227 | |
4228 enum PATHTYPE { | |
4229 PATH, BRANCH; | |
4230 } | |
4231 | |
4232 static class PathNode{ | |
4233 final PATHTYPE type; | |
4234 final PathNode parent; | |
4235 | |
4236 PathNode(PATHTYPE type, PathNode parent) { | |
4237 this.type = type; | |
4238 this.parent = parent; | |
4239 } | |
4240 } | |
4241 | |
4242 static PathNode clearPathRoot(){ | |
4243 return (PathNode) CLEAR_ROOT.get(); | |
4244 } | |
4245 | |
4246 enum PSTATE{ | |
4247 REQ, REST, DONE | |
4248 } | |
4249 | |
4250 public static class FnMethod extends ObjMethod{ | |
4251 //localbinding->localbinding | |
4252 PersistentVector reqParms = PersistentVector.EMPTY; | |
4253 LocalBinding restParm = null; | |
4254 | |
4255 public FnMethod(ObjExpr objx, ObjMethod parent){ | |
4256 super(objx, parent); | |
4257 } | |
4258 | |
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 | |
4264 { | |
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 )); | |
4281 | |
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++) | |
4291 { | |
4292 if(!(parms.nth(i) instanceof Symbol)) | |
4293 throw new IllegalArgumentException("fn params must be Symbols"); | |
4294 Symbol p = (Symbol) parms.nth(i); | |
4295 if(p.getNamespace() != null) | |
4296 throw new Exception("Can't use qualified name as parameter: " + p); | |
4297 if(p.equals(_AMP_)) | |
4298 { | |
4299 if(state == PSTATE.REQ) | |
4300 state = PSTATE.REST; | |
4301 else | |
4302 throw new Exception("Invalid parameter list"); | |
4303 } | |
4304 | |
4305 else | |
4306 { | |
4307 LocalBinding lb = registerLocal(p, state == PSTATE.REST ? ISEQ : tagOf(p), null,true); | |
4308 argLocals = argLocals.cons(lb); | |
4309 switch(state) | |
4310 { | |
4311 case REQ: | |
4312 method.reqParms = method.reqParms.cons(lb); | |
4313 break; | |
4314 case REST: | |
4315 method.restParm = lb; | |
4316 state = PSTATE.DONE; | |
4317 break; | |
4318 | |
4319 default: | |
4320 throw new Exception("Unexpected parameter"); | |
4321 } | |
4322 } | |
4323 } | |
4324 if(method.reqParms.count() > MAX_POSITIONAL_ARITY) | |
4325 throw new Exception("Can't specify more than " + MAX_POSITIONAL_ARITY + " params"); | |
4326 LOOP_LOCALS.set(argLocals); | |
4327 method.argLocals = argLocals; | |
4328 method.body = (new BodyExpr.Parser()).parse(C.RETURN, body); | |
4329 return method; | |
4330 } | |
4331 finally | |
4332 { | |
4333 Var.popThreadBindings(); | |
4334 } | |
4335 } | |
4336 | |
4337 public final PersistentVector reqParms(){ | |
4338 return reqParms; | |
4339 } | |
4340 | |
4341 public final LocalBinding restParm(){ | |
4342 return restParm; | |
4343 } | |
4344 | |
4345 boolean isVariadic(){ | |
4346 return restParm != null; | |
4347 } | |
4348 | |
4349 int numParams(){ | |
4350 return reqParms.count() + (isVariadic() ? 1 : 0); | |
4351 } | |
4352 | |
4353 String getMethodName(){ | |
4354 return isVariadic()?"doInvoke":"invoke"; | |
4355 } | |
4356 | |
4357 Type getReturnType(){ | |
4358 return OBJECT_TYPE; | |
4359 } | |
4360 | |
4361 Type[] getArgTypes(){ | |
4362 if(isVariadic() && reqParms.count() == MAX_POSITIONAL_ARITY) | |
4363 { | |
4364 Type[] ret = new Type[MAX_POSITIONAL_ARITY + 1]; | |
4365 for(int i = 0;i<MAX_POSITIONAL_ARITY + 1;i++) | |
4366 ret[i] = OBJECT_TYPE; | |
4367 return ret; | |
4368 } | |
4369 return ARG_TYPES[numParams()]; | |
4370 } | |
4371 | |
4372 void emitClearLocals(GeneratorAdapter gen){ | |
4373 // for(int i = 1; i < numParams() + 1; i++) | |
4374 // { | |
4375 // if(!localsUsedInCatchFinally.contains(i)) | |
4376 // { | |
4377 // gen.visitInsn(Opcodes.ACONST_NULL); | |
4378 // gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), i); | |
4379 // } | |
4380 // } | |
4381 // for(int i = numParams() + 1; i < maxLocal + 1; i++) | |
4382 // { | |
4383 // if(!localsUsedInCatchFinally.contains(i)) | |
4384 // { | |
4385 // LocalBinding b = (LocalBinding) RT.get(indexlocals, i); | |
4386 // if(b == null || maybePrimitiveType(b.init) == null) | |
4387 // { | |
4388 // gen.visitInsn(Opcodes.ACONST_NULL); | |
4389 // gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), i); | |
4390 // } | |
4391 // } | |
4392 // } | |
4393 // if(((FnExpr)objx).onceOnly) | |
4394 // { | |
4395 // objx.emitClearCloses(gen); | |
4396 // } | |
4397 } | |
4398 } | |
4399 | |
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; | |
4415 | |
4416 public final IPersistentMap locals(){ | |
4417 return locals; | |
4418 } | |
4419 | |
4420 public final Expr body(){ | |
4421 return body; | |
4422 } | |
4423 | |
4424 public final ObjExpr objx(){ | |
4425 return objx; | |
4426 } | |
4427 | |
4428 public final PersistentVector argLocals(){ | |
4429 return argLocals; | |
4430 } | |
4431 | |
4432 public final int maxLocal(){ | |
4433 return maxLocal; | |
4434 } | |
4435 | |
4436 public final int line(){ | |
4437 return line; | |
4438 } | |
4439 | |
4440 public ObjMethod(ObjExpr objx, ObjMethod parent){ | |
4441 this.parent = parent; | |
4442 this.objx = objx; | |
4443 } | |
4444 | |
4445 abstract int numParams(); | |
4446 abstract String getMethodName(); | |
4447 abstract Type getReturnType(); | |
4448 abstract Type[] getArgTypes(); | |
4449 | |
4450 public void emit(ObjExpr fn, ClassVisitor cv){ | |
4451 Method m = new Method(getMethodName(), getReturnType(), getArgTypes()); | |
4452 | |
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 | |
4463 { | |
4464 Var.pushThreadBindings(RT.map(LOOP_LABEL, loopLabel, METHOD, this)); | |
4465 body.emit(C.RETURN, fn, gen); | |
4466 Label end = gen.mark(); | |
4467 gen.visitLocalVariable("this", "Ljava/lang/Object;", null, loopLabel, end, 0); | |
4468 for(ISeq lbs = argLocals.seq(); lbs != null; lbs = lbs.next()) | |
4469 { | |
4470 LocalBinding lb = (LocalBinding) lbs.first(); | |
4471 gen.visitLocalVariable(lb.name, "Ljava/lang/Object;", null, loopLabel, end, lb.idx); | |
4472 } | |
4473 } | |
4474 finally | |
4475 { | |
4476 Var.popThreadBindings(); | |
4477 } | |
4478 | |
4479 gen.returnValue(); | |
4480 //gen.visitMaxs(1, 1); | |
4481 gen.endMethod(); | |
4482 } | |
4483 | |
4484 void emitClearLocals(GeneratorAdapter gen){ | |
4485 } | |
4486 | |
4487 void emitClearLocalsOld(GeneratorAdapter gen){ | |
4488 for(int i=0;i<argLocals.count();i++) | |
4489 { | |
4490 LocalBinding lb = (LocalBinding) argLocals.nth(i); | |
4491 if(!localsUsedInCatchFinally.contains(lb.idx) && lb.getPrimitiveType() == null) | |
4492 { | |
4493 gen.visitInsn(Opcodes.ACONST_NULL); | |
4494 gen.storeArg(lb.idx - 1); | |
4495 } | |
4496 | |
4497 } | |
4498 // for(int i = 1; i < numParams() + 1; i++) | |
4499 // { | |
4500 // if(!localsUsedInCatchFinally.contains(i)) | |
4501 // { | |
4502 // gen.visitInsn(Opcodes.ACONST_NULL); | |
4503 // gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), i); | |
4504 // } | |
4505 // } | |
4506 for(int i = numParams() + 1; i < maxLocal + 1; i++) | |
4507 { | |
4508 if(!localsUsedInCatchFinally.contains(i)) | |
4509 { | |
4510 LocalBinding b = (LocalBinding) RT.get(indexlocals, i); | |
4511 if(b == null || maybePrimitiveType(b.init) == null) | |
4512 { | |
4513 gen.visitInsn(Opcodes.ACONST_NULL); | |
4514 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), i); | |
4515 } | |
4516 } | |
4517 } | |
4518 } | |
4519 } | |
4520 | |
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; | |
4530 | |
4531 public LocalBinding(int num, Symbol sym, Symbol tag, Expr init, boolean isArg,PathNode clearPathRoot) | |
4532 throws Exception{ | |
4533 if(maybePrimitiveType(init) != null && tag != null) | |
4534 throw new UnsupportedOperationException("Can't type hint a local with a primitive initializer"); | |
4535 this.idx = num; | |
4536 this.sym = sym; | |
4537 this.tag = tag; | |
4538 this.init = init; | |
4539 this.isArg = isArg; | |
4540 this.clearPathRoot = clearPathRoot; | |
4541 name = munge(sym.name); | |
4542 } | |
4543 | |
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()); | |
4551 } | |
4552 | |
4553 public Class getJavaClass() throws Exception{ | |
4554 return tag != null ? HostExpr.tagToClass(tag) | |
4555 : init.getJavaClass(); | |
4556 } | |
4557 | |
4558 public Class getPrimitiveType(){ | |
4559 return maybePrimitiveType(init); | |
4560 } | |
4561 } | |
4562 | |
4563 public static class LocalBindingExpr implements Expr, MaybePrimitiveExpr, AssignableExpr{ | |
4564 public final LocalBinding b; | |
4565 public final Symbol tag; | |
4566 | |
4567 public final PathNode clearPath; | |
4568 public final PathNode clearRoot; | |
4569 public boolean shouldClear = false; | |
4570 | |
4571 | |
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; | |
4578 | |
4579 this.clearPath = (PathNode)CLEAR_PATH.get(); | |
4580 this.clearRoot = (PathNode)CLEAR_ROOT.get(); | |
4581 IPersistentCollection sites = (IPersistentCollection) RT.get(CLEAR_SITES.get(),b); | |
4582 | |
4583 if(b.idx > 0) | |
4584 { | |
4585 // Object dummy; | |
4586 | |
4587 if(sites != null) | |
4588 { | |
4589 for(ISeq s = sites.seq();s!=null;s = s.next()) | |
4590 { | |
4591 LocalBindingExpr o = (LocalBindingExpr) s.first(); | |
4592 PathNode common = commonPath(clearPath,o.clearPath); | |
4593 if(common != null && common.type == PATHTYPE.PATH) | |
4594 o.shouldClear = false; | |
4595 // else | |
4596 // dummy = null; | |
4597 } | |
4598 } | |
4599 | |
4600 if(clearRoot == b.clearPathRoot) | |
4601 { | |
4602 this.shouldClear = true; | |
4603 sites = RT.conj(sites,this); | |
4604 CLEAR_SITES.set(RT.assoc(CLEAR_SITES.get(), b, sites)); | |
4605 } | |
4606 // else | |
4607 // dummy = null; | |
4608 } | |
4609 } | |
4610 | |
4611 public Object eval() throws Exception{ | |
4612 throw new UnsupportedOperationException("Can't eval locals"); | |
4613 } | |
4614 | |
4615 public boolean canEmitPrimitive(){ | |
4616 return b.getPrimitiveType() != null; | |
4617 } | |
4618 | |
4619 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){ | |
4620 objx.emitUnboxedLocal(gen, b); | |
4621 } | |
4622 | |
4623 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
4624 if(context != C.STATEMENT) | |
4625 objx.emitLocal(gen, b, shouldClear); | |
4626 } | |
4627 | |
4628 public Object evalAssign(Expr val) throws Exception{ | |
4629 throw new UnsupportedOperationException("Can't eval locals"); | |
4630 } | |
4631 | |
4632 public void emitAssign(C context, ObjExpr objx, GeneratorAdapter gen, Expr val){ | |
4633 objx.emitAssignLocal(gen, b,val); | |
4634 if(context != C.STATEMENT) | |
4635 objx.emitLocal(gen, b, false); | |
4636 } | |
4637 | |
4638 public boolean hasJavaClass() throws Exception{ | |
4639 return tag != null || b.hasJavaClass(); | |
4640 } | |
4641 | |
4642 public Class getJavaClass() throws Exception{ | |
4643 if(tag != null) | |
4644 return HostExpr.tagToClass(tag); | |
4645 return b.getJavaClass(); | |
4646 } | |
4647 | |
4648 | |
4649 } | |
4650 | |
4651 public static class BodyExpr implements Expr, MaybePrimitiveExpr{ | |
4652 PersistentVector exprs; | |
4653 | |
4654 public final PersistentVector exprs(){ | |
4655 return exprs; | |
4656 } | |
4657 | |
4658 public BodyExpr(PersistentVector exprs){ | |
4659 this.exprs = exprs; | |
4660 } | |
4661 | |
4662 static class Parser implements IParser{ | |
4663 public Expr parse(C context, Object frms) throws Exception{ | |
4664 ISeq forms = (ISeq) frms; | |
4665 if(Util.equals(RT.first(forms), DO)) | |
4666 forms = RT.next(forms); | |
4667 PersistentVector exprs = PersistentVector.EMPTY; | |
4668 for(; forms != null; forms = forms.next()) | |
4669 { | |
4670 Expr e = (context != C.EVAL && | |
4671 (context == C.STATEMENT || forms.next() != null)) ? | |
4672 analyze(C.STATEMENT, forms.first()) | |
4673 : | |
4674 analyze(context, forms.first()); | |
4675 exprs = exprs.cons(e); | |
4676 } | |
4677 if(exprs.count() == 0) | |
4678 exprs = exprs.cons(NIL_EXPR); | |
4679 return new BodyExpr(exprs); | |
4680 } | |
4681 } | |
4682 | |
4683 public Object eval() throws Exception{ | |
4684 Object ret = null; | |
4685 for(Object o : exprs) | |
4686 { | |
4687 Expr e = (Expr) o; | |
4688 ret = e.eval(); | |
4689 } | |
4690 return ret; | |
4691 } | |
4692 | |
4693 public boolean canEmitPrimitive(){ | |
4694 return lastExpr() instanceof MaybePrimitiveExpr && ((MaybePrimitiveExpr)lastExpr()).canEmitPrimitive(); | |
4695 } | |
4696 | |
4697 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){ | |
4698 for(int i = 0; i < exprs.count() - 1; i++) | |
4699 { | |
4700 Expr e = (Expr) exprs.nth(i); | |
4701 e.emit(C.STATEMENT, objx, gen); | |
4702 } | |
4703 MaybePrimitiveExpr last = (MaybePrimitiveExpr) exprs.nth(exprs.count() - 1); | |
4704 last.emitUnboxed(context, objx, gen); | |
4705 } | |
4706 | |
4707 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
4708 for(int i = 0; i < exprs.count() - 1; i++) | |
4709 { | |
4710 Expr e = (Expr) exprs.nth(i); | |
4711 e.emit(C.STATEMENT, objx, gen); | |
4712 } | |
4713 Expr last = (Expr) exprs.nth(exprs.count() - 1); | |
4714 last.emit(context, objx, gen); | |
4715 } | |
4716 | |
4717 public boolean hasJavaClass() throws Exception{ | |
4718 return lastExpr().hasJavaClass(); | |
4719 } | |
4720 | |
4721 public Class getJavaClass() throws Exception{ | |
4722 return lastExpr().getJavaClass(); | |
4723 } | |
4724 | |
4725 private Expr lastExpr(){ | |
4726 return (Expr) exprs.nth(exprs.count() - 1); | |
4727 } | |
4728 } | |
4729 | |
4730 public static class BindingInit{ | |
4731 LocalBinding binding; | |
4732 Expr init; | |
4733 | |
4734 public final LocalBinding binding(){ | |
4735 return binding; | |
4736 } | |
4737 | |
4738 public final Expr init(){ | |
4739 return init; | |
4740 } | |
4741 | |
4742 public BindingInit(LocalBinding binding, Expr init){ | |
4743 this.binding = binding; | |
4744 this.init = init; | |
4745 } | |
4746 } | |
4747 | |
4748 public static class LetFnExpr implements Expr{ | |
4749 public final PersistentVector bindingInits; | |
4750 public final Expr body; | |
4751 | |
4752 public LetFnExpr(PersistentVector bindingInits, Expr body){ | |
4753 this.bindingInits = bindingInits; | |
4754 this.body = body; | |
4755 } | |
4756 | |
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"); | |
4763 | |
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"); | |
4767 | |
4768 ISeq body = RT.next(RT.next(form)); | |
4769 | |
4770 if(context == C.EVAL) | |
4771 return analyze(context, RT.list(RT.list(FN, PersistentVector.EMPTY, form))); | |
4772 | |
4773 IPersistentMap dynamicBindings = RT.map(LOCAL_ENV, LOCAL_ENV.deref(), | |
4774 NEXT_LOCAL_NUM, NEXT_LOCAL_NUM.deref()); | |
4775 | |
4776 try | |
4777 { | |
4778 Var.pushThreadBindings(dynamicBindings); | |
4779 | |
4780 //pre-seed env (like Lisp labels) | |
4781 PersistentVector lbs = PersistentVector.EMPTY; | |
4782 for(int i = 0; i < bindings.count(); i += 2) | |
4783 { | |
4784 if(!(bindings.nth(i) instanceof Symbol)) | |
4785 throw new IllegalArgumentException( | |
4786 "Bad binding form, expected symbol, got: " + bindings.nth(i)); | |
4787 Symbol sym = (Symbol) bindings.nth(i); | |
4788 if(sym.getNamespace() != null) | |
4789 throw new Exception("Can't let qualified name: " + sym); | |
4790 LocalBinding lb = registerLocal(sym, tagOf(sym), null,false); | |
4791 lb.canBeCleared = false; | |
4792 lbs = lbs.cons(lb); | |
4793 } | |
4794 PersistentVector bindingInits = PersistentVector.EMPTY; | |
4795 for(int i = 0; i < bindings.count(); i += 2) | |
4796 { | |
4797 Symbol sym = (Symbol) bindings.nth(i); | |
4798 Expr init = analyze(C.EXPRESSION, bindings.nth(i + 1), sym.name); | |
4799 LocalBinding lb = (LocalBinding) lbs.nth(i / 2); | |
4800 lb.init = init; | |
4801 BindingInit bi = new BindingInit(lb, init); | |
4802 bindingInits = bindingInits.cons(bi); | |
4803 } | |
4804 return new LetFnExpr(bindingInits, (new BodyExpr.Parser()).parse(context, body)); | |
4805 } | |
4806 finally | |
4807 { | |
4808 Var.popThreadBindings(); | |
4809 } | |
4810 } | |
4811 } | |
4812 | |
4813 public Object eval() throws Exception{ | |
4814 throw new UnsupportedOperationException("Can't eval letfns"); | |
4815 } | |
4816 | |
4817 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
4818 for(int i = 0; i < bindingInits.count(); i++) | |
4819 { | |
4820 BindingInit bi = (BindingInit) bindingInits.nth(i); | |
4821 gen.visitInsn(Opcodes.ACONST_NULL); | |
4822 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), bi.binding.idx); | |
4823 } | |
4824 | |
4825 IPersistentSet lbset = PersistentHashSet.EMPTY; | |
4826 | |
4827 for(int i = 0; i < bindingInits.count(); i++) | |
4828 { | |
4829 BindingInit bi = (BindingInit) bindingInits.nth(i); | |
4830 lbset = (IPersistentSet) lbset.cons(bi.binding); | |
4831 bi.init.emit(C.EXPRESSION, objx, gen); | |
4832 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), bi.binding.idx); | |
4833 } | |
4834 | |
4835 for(int i = 0; i < bindingInits.count(); i++) | |
4836 { | |
4837 BindingInit bi = (BindingInit) bindingInits.nth(i); | |
4838 ObjExpr fe = (ObjExpr) bi.init; | |
4839 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), bi.binding.idx); | |
4840 fe.emitLetFnInits(gen, objx, lbset); | |
4841 } | |
4842 | |
4843 Label loopLabel = gen.mark(); | |
4844 | |
4845 body.emit(context, objx, gen); | |
4846 | |
4847 Label end = gen.mark(); | |
4848 // gen.visitLocalVariable("this", "Ljava/lang/Object;", null, loopLabel, end, 0); | |
4849 for(ISeq bis = bindingInits.seq(); bis != null; bis = bis.next()) | |
4850 { | |
4851 BindingInit bi = (BindingInit) bis.first(); | |
4852 String lname = bi.binding.name; | |
4853 if(lname.endsWith("__auto__")) | |
4854 lname += RT.nextID(); | |
4855 Class primc = maybePrimitiveType(bi.init); | |
4856 if(primc != null) | |
4857 gen.visitLocalVariable(lname, Type.getDescriptor(primc), null, loopLabel, end, | |
4858 bi.binding.idx); | |
4859 else | |
4860 gen.visitLocalVariable(lname, "Ljava/lang/Object;", null, loopLabel, end, bi.binding.idx); | |
4861 } | |
4862 } | |
4863 | |
4864 public boolean hasJavaClass() throws Exception{ | |
4865 return body.hasJavaClass(); | |
4866 } | |
4867 | |
4868 public Class getJavaClass() throws Exception{ | |
4869 return body.getJavaClass(); | |
4870 } | |
4871 } | |
4872 | |
4873 public static class LetExpr implements Expr, MaybePrimitiveExpr{ | |
4874 public final PersistentVector bindingInits; | |
4875 public final Expr body; | |
4876 public final boolean isLoop; | |
4877 | |
4878 public LetExpr(PersistentVector bindingInits, Expr body, boolean isLoop){ | |
4879 this.bindingInits = bindingInits; | |
4880 this.body = body; | |
4881 this.isLoop = isLoop; | |
4882 } | |
4883 | |
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"); | |
4891 | |
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"); | |
4895 | |
4896 ISeq body = RT.next(RT.next(form)); | |
4897 | |
4898 if(context == C.EVAL | |
4899 || (context == C.EXPRESSION && isLoop)) | |
4900 return analyze(context, RT.list(RT.list(FN, PersistentVector.EMPTY, form))); | |
4901 | |
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); | |
4906 | |
4907 try | |
4908 { | |
4909 Var.pushThreadBindings(dynamicBindings); | |
4910 | |
4911 PersistentVector bindingInits = PersistentVector.EMPTY; | |
4912 PersistentVector loopLocals = PersistentVector.EMPTY; | |
4913 for(int i = 0; i < bindings.count(); i += 2) | |
4914 { | |
4915 if(!(bindings.nth(i) instanceof Symbol)) | |
4916 throw new IllegalArgumentException( | |
4917 "Bad binding form, expected symbol, got: " + bindings.nth(i)); | |
4918 Symbol sym = (Symbol) bindings.nth(i); | |
4919 if(sym.getNamespace() != null) | |
4920 throw new Exception("Can't let qualified name: " + sym); | |
4921 Expr init = analyze(C.EXPRESSION, bindings.nth(i + 1), sym.name); | |
4922 //sequential enhancement of env (like Lisp let*) | |
4923 LocalBinding lb = registerLocal(sym, tagOf(sym), init,false); | |
4924 BindingInit bi = new BindingInit(lb, init); | |
4925 bindingInits = bindingInits.cons(bi); | |
4926 | |
4927 if(isLoop) | |
4928 loopLocals = loopLocals.cons(lb); | |
4929 } | |
4930 if(isLoop) | |
4931 LOOP_LOCALS.set(loopLocals); | |
4932 Expr bodyExpr; | |
4933 try { | |
4934 if(isLoop) | |
4935 { | |
4936 PathNode root = new PathNode(PATHTYPE.PATH, (PathNode) CLEAR_PATH.get()); | |
4937 Var.pushThreadBindings( | |
4938 RT.map(CLEAR_PATH, new PathNode(PATHTYPE.PATH,root), | |
4939 CLEAR_ROOT, new PathNode(PATHTYPE.PATH,root))); | |
4940 } | |
4941 bodyExpr = (new BodyExpr.Parser()).parse(isLoop ? C.RETURN : context, body); | |
4942 } | |
4943 finally{ | |
4944 if(isLoop) | |
4945 Var.popThreadBindings(); | |
4946 } | |
4947 return new LetExpr(bindingInits, bodyExpr, | |
4948 isLoop); | |
4949 } | |
4950 finally | |
4951 { | |
4952 Var.popThreadBindings(); | |
4953 } | |
4954 } | |
4955 } | |
4956 | |
4957 public Object eval() throws Exception{ | |
4958 throw new UnsupportedOperationException("Can't eval let/loop"); | |
4959 } | |
4960 | |
4961 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
4962 doEmit(context, objx, gen, false); | |
4963 } | |
4964 | |
4965 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){ | |
4966 doEmit(context, objx, gen, true); | |
4967 } | |
4968 | |
4969 | |
4970 public void doEmit(C context, ObjExpr objx, GeneratorAdapter gen, boolean emitUnboxed){ | |
4971 for(int i = 0; i < bindingInits.count(); i++) | |
4972 { | |
4973 BindingInit bi = (BindingInit) bindingInits.nth(i); | |
4974 Class primc = maybePrimitiveType(bi.init); | |
4975 if(primc != null) | |
4976 { | |
4977 ((MaybePrimitiveExpr) bi.init).emitUnboxed(C.EXPRESSION, objx, gen); | |
4978 gen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ISTORE), bi.binding.idx); | |
4979 } | |
4980 else | |
4981 { | |
4982 bi.init.emit(C.EXPRESSION, objx, gen); | |
4983 gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), bi.binding.idx); | |
4984 } | |
4985 } | |
4986 Label loopLabel = gen.mark(); | |
4987 if(isLoop) | |
4988 { | |
4989 try | |
4990 { | |
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); | |
4996 } | |
4997 finally | |
4998 { | |
4999 Var.popThreadBindings(); | |
5000 } | |
5001 } | |
5002 else | |
5003 { | |
5004 if(emitUnboxed) | |
5005 ((MaybePrimitiveExpr)body).emitUnboxed(context, objx, gen); | |
5006 else | |
5007 body.emit(context, objx, gen); | |
5008 } | |
5009 Label end = gen.mark(); | |
5010 // gen.visitLocalVariable("this", "Ljava/lang/Object;", null, loopLabel, end, 0); | |
5011 for(ISeq bis = bindingInits.seq(); bis != null; bis = bis.next()) | |
5012 { | |
5013 BindingInit bi = (BindingInit) bis.first(); | |
5014 String lname = bi.binding.name; | |
5015 if(lname.endsWith("__auto__")) | |
5016 lname += RT.nextID(); | |
5017 Class primc = maybePrimitiveType(bi.init); | |
5018 if(primc != null) | |
5019 gen.visitLocalVariable(lname, Type.getDescriptor(primc), null, loopLabel, end, | |
5020 bi.binding.idx); | |
5021 else | |
5022 gen.visitLocalVariable(lname, "Ljava/lang/Object;", null, loopLabel, end, bi.binding.idx); | |
5023 } | |
5024 } | |
5025 | |
5026 public boolean hasJavaClass() throws Exception{ | |
5027 return body.hasJavaClass(); | |
5028 } | |
5029 | |
5030 public Class getJavaClass() throws Exception{ | |
5031 return body.getJavaClass(); | |
5032 } | |
5033 | |
5034 public boolean canEmitPrimitive(){ | |
5035 return body instanceof MaybePrimitiveExpr && ((MaybePrimitiveExpr)body).canEmitPrimitive(); | |
5036 } | |
5037 | |
5038 } | |
5039 | |
5040 public static class RecurExpr implements Expr{ | |
5041 public final IPersistentVector args; | |
5042 public final IPersistentVector loopLocals; | |
5043 | |
5044 public RecurExpr(IPersistentVector loopLocals, IPersistentVector args){ | |
5045 this.loopLocals = loopLocals; | |
5046 this.args = args; | |
5047 } | |
5048 | |
5049 public Object eval() throws Exception{ | |
5050 throw new UnsupportedOperationException("Can't eval recur"); | |
5051 } | |
5052 | |
5053 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
5054 Label loopLabel = (Label) LOOP_LABEL.deref(); | |
5055 if(loopLabel == null) | |
5056 throw new IllegalStateException(); | |
5057 for(int i = 0; i < loopLocals.count(); i++) | |
5058 { | |
5059 LocalBinding lb = (LocalBinding) loopLocals.nth(i); | |
5060 Expr arg = (Expr) args.nth(i); | |
5061 if(lb.getPrimitiveType() != null) | |
5062 { | |
5063 Class primc = lb.getPrimitiveType(); | |
5064 try | |
5065 { | |
5066 if(!(arg instanceof MaybePrimitiveExpr && arg.hasJavaClass() && arg.getJavaClass() == primc)) | |
5067 throw new IllegalArgumentException("recur arg for primitive local: " + | |
5068 lb.name + " must be matching primitive"); | |
5069 } | |
5070 catch(Exception e) | |
5071 { | |
5072 throw new RuntimeException(e); | |
5073 } | |
5074 ((MaybePrimitiveExpr) arg).emitUnboxed(C.EXPRESSION, objx, gen); | |
5075 } | |
5076 else | |
5077 { | |
5078 arg.emit(C.EXPRESSION, objx, gen); | |
5079 } | |
5080 } | |
5081 | |
5082 for(int i = loopLocals.count() - 1; i >= 0; i--) | |
5083 { | |
5084 LocalBinding lb = (LocalBinding) loopLocals.nth(i); | |
5085 Class primc = lb.getPrimitiveType(); | |
5086 if(lb.isArg) | |
5087 gen.storeArg(lb.idx-1); | |
5088 else | |
5089 { | |
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); | |
5094 } | |
5095 } | |
5096 | |
5097 gen.goTo(loopLabel); | |
5098 } | |
5099 | |
5100 public boolean hasJavaClass() throws Exception{ | |
5101 return true; | |
5102 } | |
5103 | |
5104 public Class getJavaClass() throws Exception{ | |
5105 return null; | |
5106 } | |
5107 | |
5108 static class Parser implements IParser{ | |
5109 public Expr parse(C context, Object frm) throws Exception{ | |
5110 ISeq form = (ISeq) frm; | |
5111 IPersistentVector loopLocals = (IPersistentVector) LOOP_LOCALS.deref(); | |
5112 if(context != C.RETURN || loopLocals == null) | |
5113 throw new UnsupportedOperationException("Can only recur from tail position"); | |
5114 if(IN_CATCH_FINALLY.deref() != null) | |
5115 throw new UnsupportedOperationException("Cannot recur from catch/finally"); | |
5116 PersistentVector args = PersistentVector.EMPTY; | |
5117 for(ISeq s = RT.seq(form.next()); s != null; s = s.next()) | |
5118 { | |
5119 args = args.cons(analyze(C.EXPRESSION, s.first())); | |
5120 } | |
5121 if(args.count() != loopLocals.count()) | |
5122 throw new IllegalArgumentException( | |
5123 String.format("Mismatched argument count to recur, expected: %d args, got: %d", | |
5124 loopLocals.count(), args.count())); | |
5125 return new RecurExpr(loopLocals, args); | |
5126 } | |
5127 } | |
5128 } | |
5129 | |
5130 private static LocalBinding registerLocal(Symbol sym, Symbol tag, Expr init, boolean isArg) throws Exception{ | |
5131 int num = getAndIncLocalNum(); | |
5132 LocalBinding b = new LocalBinding(num, sym, tag, init, isArg, clearPathRoot()); | |
5133 IPersistentMap localsMap = (IPersistentMap) LOCAL_ENV.deref(); | |
5134 LOCAL_ENV.set(RT.assoc(localsMap, b.sym, b)); | |
5135 ObjMethod method = (ObjMethod) METHOD.deref(); | |
5136 method.locals = (IPersistentMap) RT.assoc(method.locals, b, b); | |
5137 method.indexlocals = (IPersistentMap) RT.assoc(method.indexlocals, num, b); | |
5138 return b; | |
5139 } | |
5140 | |
5141 private static int getAndIncLocalNum(){ | |
5142 int num = ((Number) NEXT_LOCAL_NUM.deref()).intValue(); | |
5143 ObjMethod m = (ObjMethod) METHOD.deref(); | |
5144 if(num > m.maxLocal) | |
5145 m.maxLocal = num; | |
5146 NEXT_LOCAL_NUM.set(num + 1); | |
5147 return num; | |
5148 } | |
5149 | |
5150 public static Expr analyze(C context, Object form) throws Exception{ | |
5151 return analyze(context, form, null); | |
5152 } | |
5153 | |
5154 private static Expr analyze(C context, Object form, String name) throws Exception{ | |
5155 //todo symbol macro expansion? | |
5156 try | |
5157 { | |
5158 if(form instanceof LazySeq) | |
5159 { | |
5160 form = RT.seq(form); | |
5161 if(form == null) | |
5162 form = PersistentList.EMPTY; | |
5163 } | |
5164 if(form == null) | |
5165 return NIL_EXPR; | |
5166 else if(form == Boolean.TRUE) | |
5167 return TRUE_EXPR; | |
5168 else if(form == Boolean.FALSE) | |
5169 return FALSE_EXPR; | |
5170 Class fclass = form.getClass(); | |
5171 if(fclass == Symbol.class) | |
5172 return analyzeSymbol((Symbol) form); | |
5173 else if(fclass == Keyword.class) | |
5174 return registerKeyword((Keyword) form); | |
5175 // else if(form instanceof Num) | |
5176 // return new NumExpr((Num) form); | |
5177 else if(fclass == String.class) | |
5178 return new StringExpr(((String) form).intern()); | |
5179 // else if(fclass == Character.class) | |
5180 // return new CharExpr((Character) form); | |
5181 else if(form instanceof IPersistentCollection && ((IPersistentCollection) form).count() == 0) | |
5182 { | |
5183 Expr ret = new EmptyExpr(form); | |
5184 if(RT.meta(form) != null) | |
5185 ret = new MetaExpr(ret, (MapExpr) MapExpr | |
5186 .parse(context == C.EVAL ? context : C.EXPRESSION, ((IObj) form).meta())); | |
5187 return ret; | |
5188 } | |
5189 else if(form instanceof ISeq) | |
5190 return analyzeSeq(context, (ISeq) form, name); | |
5191 else if(form instanceof IPersistentVector) | |
5192 return VectorExpr.parse(context, (IPersistentVector) form); | |
5193 else if(form instanceof IPersistentMap) | |
5194 return MapExpr.parse(context, (IPersistentMap) form); | |
5195 else if(form instanceof IPersistentSet) | |
5196 return SetExpr.parse(context, (IPersistentSet) form); | |
5197 | |
5198 // else | |
5199 //throw new UnsupportedOperationException(); | |
5200 return new ConstantExpr(form); | |
5201 } | |
5202 catch(Throwable e) | |
5203 { | |
5204 if(!(e instanceof CompilerException)) | |
5205 throw new CompilerException((String) SOURCE.deref(), (Integer) LINE.deref(), e); | |
5206 else | |
5207 throw (CompilerException) e; | |
5208 } | |
5209 } | |
5210 | |
5211 static public class CompilerException extends Exception{ | |
5212 | |
5213 public CompilerException(String source, int line, Throwable cause){ | |
5214 super(errorMsg(source, line, cause.toString()), cause); | |
5215 } | |
5216 | |
5217 public String toString(){ | |
5218 return getMessage(); | |
5219 } | |
5220 } | |
5221 | |
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) | |
5227 { | |
5228 Var v = (op instanceof Var) ? (Var) op : lookupVar((Symbol) op, false); | |
5229 if(v != null && v.isMacro()) | |
5230 { | |
5231 if(v.ns != currentNS() && !v.isPublic()) | |
5232 throw new IllegalStateException("var: " + v + " is not public"); | |
5233 return v; | |
5234 } | |
5235 } | |
5236 return null; | |
5237 } | |
5238 | |
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) | |
5244 { | |
5245 Var v = (op instanceof Var) ? (Var) op : lookupVar((Symbol) op, false); | |
5246 if(v != null) | |
5247 { | |
5248 if(v.ns != currentNS() && !v.isPublic()) | |
5249 throw new IllegalStateException("var: " + v + " is not public"); | |
5250 IFn ret = (IFn) RT.get(v.meta(), inlineKey); | |
5251 if(ret != null) | |
5252 { | |
5253 IFn arityPred = (IFn) RT.get(v.meta(), inlineAritiesKey); | |
5254 if(arityPred == null || RT.booleanCast(arityPred.invoke(arity))) | |
5255 return ret; | |
5256 } | |
5257 } | |
5258 } | |
5259 return null; | |
5260 } | |
5261 | |
5262 public static boolean namesStaticMember(Symbol sym){ | |
5263 return sym.ns != null && namespaceFor(sym) == null; | |
5264 } | |
5265 | |
5266 public static Object preserveTag(ISeq src, Object dst) { | |
5267 Symbol tag = tagOf(src); | |
5268 if (tag != null && dst instanceof IObj) { | |
5269 IPersistentMap meta = RT.meta(dst); | |
5270 return ((IObj) dst).withMeta((IPersistentMap) RT.assoc(meta, RT.TAG_KEY, tag)); | |
5271 } | |
5272 return dst; | |
5273 } | |
5274 | |
5275 public static Object macroexpand1(Object x) throws Exception{ | |
5276 if(x instanceof ISeq) | |
5277 { | |
5278 ISeq form = (ISeq) x; | |
5279 Object op = RT.first(form); | |
5280 if(isSpecial(op)) | |
5281 return x; | |
5282 //macro expansion | |
5283 Var v = isMacro(op); | |
5284 if(v != null) | |
5285 { | |
5286 return v.applyTo(RT.cons(form,RT.cons(LOCAL_ENV.get(),form.next()))); | |
5287 } | |
5288 else | |
5289 { | |
5290 if(op instanceof Symbol) | |
5291 { | |
5292 Symbol sym = (Symbol) op; | |
5293 String sname = sym.name; | |
5294 //(.substring s 2 5) => (. s substring 2 5) | |
5295 if(sym.name.charAt(0) == '.') | |
5296 { | |
5297 if(RT.length(form) < 2) | |
5298 throw new IllegalArgumentException( | |
5299 "Malformed member expression, expecting (.member target ...)"); | |
5300 Symbol meth = Symbol.intern(sname.substring(1)); | |
5301 Object target = RT.second(form); | |
5302 if(HostExpr.maybeClass(target, false) != null) | |
5303 { | |
5304 target = ((IObj)RT.list(IDENTITY, target)).withMeta(RT.map(RT.TAG_KEY,CLASS)); | |
5305 } | |
5306 return preserveTag(form, RT.listStar(DOT, target, meth, form.next().next())); | |
5307 } | |
5308 else if(namesStaticMember(sym)) | |
5309 { | |
5310 Symbol target = Symbol.intern(sym.ns); | |
5311 Class c = HostExpr.maybeClass(target, false); | |
5312 if(c != null) | |
5313 { | |
5314 Symbol meth = Symbol.intern(sym.name); | |
5315 return preserveTag(form, RT.listStar(DOT, target, meth, form.next())); | |
5316 } | |
5317 } | |
5318 else | |
5319 { | |
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()); | |
5333 } | |
5334 } | |
5335 } | |
5336 } | |
5337 return x; | |
5338 } | |
5339 | |
5340 static Object macroexpand(Object form) throws Exception{ | |
5341 Object exf = macroexpand1(form); | |
5342 if(exf != form) | |
5343 return macroexpand(exf); | |
5344 return form; | |
5345 } | |
5346 | |
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 | |
5354 { | |
5355 Object me = macroexpand1(form); | |
5356 if(me != form) | |
5357 return analyze(context, me, name); | |
5358 | |
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); | |
5372 } | |
5373 catch(Throwable e) | |
5374 { | |
5375 if(!(e instanceof CompilerException)) | |
5376 throw new CompilerException((String) SOURCE.deref(), (Integer) LINE.deref(), e); | |
5377 else | |
5378 throw (CompilerException) e; | |
5379 } | |
5380 finally | |
5381 { | |
5382 Var.popThreadBindings(); | |
5383 } | |
5384 } | |
5385 | |
5386 static String errorMsg(String source, int line, String s){ | |
5387 return String.format("%s (%s:%d)", s, source, line); | |
5388 } | |
5389 | |
5390 public static Object eval(Object form) throws Exception{ | |
5391 return eval(form, true); | |
5392 } | |
5393 | |
5394 public static Object eval(Object form, boolean freshLoader) throws Exception{ | |
5395 boolean createdLoader = false; | |
5396 if(true)//!LOADER.isBound()) | |
5397 { | |
5398 Var.pushThreadBindings(RT.map(LOADER, RT.makeClassLoader())); | |
5399 createdLoader = true; | |
5400 } | |
5401 try | |
5402 { | |
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 | |
5408 { | |
5409 form = macroexpand(form); | |
5410 if(form instanceof IPersistentCollection && Util.equals(RT.first(form), DO)) | |
5411 { | |
5412 ISeq s = RT.next(form); | |
5413 for(; RT.next(s) != null; s = RT.next(s)) | |
5414 eval(RT.first(s),false); | |
5415 return eval(RT.first(s),false); | |
5416 } | |
5417 else if(form instanceof IPersistentCollection | |
5418 && !(RT.first(form) instanceof Symbol | |
5419 && ((Symbol) RT.first(form)).name.startsWith("def"))) | |
5420 { | |
5421 ObjExpr fexpr = (ObjExpr) analyze(C.EXPRESSION, RT.list(FN, PersistentVector.EMPTY, form), | |
5422 "eval" + RT.nextID()); | |
5423 IFn fn = (IFn) fexpr.eval(); | |
5424 return fn.invoke(); | |
5425 } | |
5426 else | |
5427 { | |
5428 Expr expr = analyze(C.EVAL, form); | |
5429 return expr.eval(); | |
5430 } | |
5431 } | |
5432 finally | |
5433 { | |
5434 Var.popThreadBindings(); | |
5435 } | |
5436 } | |
5437 catch(Throwable e) | |
5438 { | |
5439 if(!(e instanceof CompilerException)) | |
5440 throw new CompilerException((String) SOURCE.deref(), (Integer) LINE.deref(), e); | |
5441 else | |
5442 throw (CompilerException) e; | |
5443 } | |
5444 finally | |
5445 { | |
5446 if(createdLoader) | |
5447 Var.popThreadBindings(); | |
5448 } | |
5449 } | |
5450 | |
5451 private static int registerConstant(Object o){ | |
5452 if(!CONSTANTS.isBound()) | |
5453 return -1; | |
5454 PersistentVector v = (PersistentVector) CONSTANTS.deref(); | |
5455 IdentityHashMap<Object,Integer> ids = (IdentityHashMap<Object,Integer>) CONSTANT_IDS.deref(); | |
5456 Integer i = ids.get(o); | |
5457 if(i != null) | |
5458 return i; | |
5459 CONSTANTS.set(RT.conj(v, o)); | |
5460 ids.put(o, v.count()); | |
5461 return v.count(); | |
5462 } | |
5463 | |
5464 private static KeywordExpr registerKeyword(Keyword keyword){ | |
5465 if(!KEYWORDS.isBound()) | |
5466 return new KeywordExpr(keyword); | |
5467 | |
5468 IPersistentMap keywordsMap = (IPersistentMap) KEYWORDS.deref(); | |
5469 Object id = RT.get(keywordsMap, keyword); | |
5470 if(id == null) | |
5471 { | |
5472 KEYWORDS.set(RT.assoc(keywordsMap, keyword, registerConstant(keyword))); | |
5473 } | |
5474 return new KeywordExpr(keyword); | |
5475 // KeywordExpr ke = (KeywordExpr) RT.get(keywordsMap, keyword); | |
5476 // if(ke == null) | |
5477 // KEYWORDS.set(RT.assoc(keywordsMap, keyword, ke = new KeywordExpr(keyword))); | |
5478 // return ke; | |
5479 } | |
5480 | |
5481 private static int registerKeywordCallsite(Keyword keyword){ | |
5482 if(!KEYWORD_CALLSITES.isBound()) | |
5483 throw new IllegalAccessError("KEYWORD_CALLSITES is not bound"); | |
5484 | |
5485 IPersistentVector keywordCallsites = (IPersistentVector) KEYWORD_CALLSITES.deref(); | |
5486 | |
5487 keywordCallsites = keywordCallsites.cons(keyword); | |
5488 KEYWORD_CALLSITES.set(keywordCallsites); | |
5489 return keywordCallsites.count()-1; | |
5490 } | |
5491 | |
5492 private static int registerProtocolCallsite(Var v){ | |
5493 if(!PROTOCOL_CALLSITES.isBound()) | |
5494 throw new IllegalAccessError("PROTOCOL_CALLSITES is not bound"); | |
5495 | |
5496 IPersistentVector protocolCallsites = (IPersistentVector) PROTOCOL_CALLSITES.deref(); | |
5497 | |
5498 protocolCallsites = protocolCallsites.cons(v); | |
5499 PROTOCOL_CALLSITES.set(protocolCallsites); | |
5500 return protocolCallsites.count()-1; | |
5501 } | |
5502 | |
5503 private static int registerVarCallsite(Var v){ | |
5504 if(!VAR_CALLSITES.isBound()) | |
5505 throw new IllegalAccessError("VAR_CALLSITES is not bound"); | |
5506 | |
5507 IPersistentVector varCallsites = (IPersistentVector) VAR_CALLSITES.deref(); | |
5508 | |
5509 varCallsites = varCallsites.cons(v); | |
5510 VAR_CALLSITES.set(varCallsites); | |
5511 return varCallsites.count()-1; | |
5512 } | |
5513 | |
5514 static ISeq fwdPath(PathNode p1){ | |
5515 ISeq ret = null; | |
5516 for(;p1 != null;p1 = p1.parent) | |
5517 ret = RT.cons(p1,ret); | |
5518 return ret; | |
5519 } | |
5520 | |
5521 static PathNode commonPath(PathNode n1, PathNode n2){ | |
5522 ISeq xp = fwdPath(n1); | |
5523 ISeq yp = fwdPath(n2); | |
5524 if(RT.first(xp) != RT.first(yp)) | |
5525 return null; | |
5526 while(RT.second(xp) != null && RT.second(xp) == RT.second(yp)) | |
5527 { | |
5528 xp = xp.next(); | |
5529 yp = yp.next(); | |
5530 } | |
5531 return (PathNode) RT.first(xp); | |
5532 } | |
5533 | |
5534 static void addAnnotation(Object visitor, IPersistentMap meta){ | |
5535 try{ | |
5536 if(meta != null && ADD_ANNOTATIONS.isBound()) | |
5537 ADD_ANNOTATIONS.invoke(visitor, meta); | |
5538 } | |
5539 catch (Exception e) | |
5540 { | |
5541 throw new RuntimeException(e); | |
5542 } | |
5543 } | |
5544 | |
5545 static void addParameterAnnotation(Object visitor, IPersistentMap meta, int i){ | |
5546 try{ | |
5547 if(meta != null && ADD_ANNOTATIONS.isBound()) | |
5548 ADD_ANNOTATIONS.invoke(visitor, meta, i); | |
5549 } | |
5550 catch (Exception e) | |
5551 { | |
5552 throw new RuntimeException(e); | |
5553 } | |
5554 } | |
5555 | |
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 | |
5559 { | |
5560 LocalBinding b = referenceLocal(sym); | |
5561 if(b != null) | |
5562 { | |
5563 return new LocalBindingExpr(b, tag); | |
5564 } | |
5565 } | |
5566 else | |
5567 { | |
5568 if(namespaceFor(sym) == null) | |
5569 { | |
5570 Symbol nsSym = Symbol.create(sym.ns); | |
5571 Class c = HostExpr.maybeClass(nsSym, false); | |
5572 if(c != null) | |
5573 { | |
5574 if(Reflector.getField(c, sym.name, true) != null) | |
5575 return new StaticFieldExpr((Integer) LINE.deref(), c, sym.name, tag); | |
5576 throw new Exception("Unable to find static field: " + sym.name + " in " + c); | |
5577 } | |
5578 } | |
5579 } | |
5580 //Var v = lookupVar(sym, false); | |
5581 // Var v = lookupVar(sym, false); | |
5582 // if(v != null) | |
5583 // return new VarExpr(v, tag); | |
5584 Object o = resolve(sym); | |
5585 if(o instanceof Var) | |
5586 { | |
5587 Var v = (Var) o; | |
5588 if(isMacro(v) != null) | |
5589 throw new Exception("Can't take value of a macro: " + v); | |
5590 registerVar(v); | |
5591 return new VarExpr(v, tag); | |
5592 } | |
5593 else if(o instanceof Class) | |
5594 return new ConstantExpr(o); | |
5595 else if(o instanceof Symbol) | |
5596 return new UnresolvedVarExpr((Symbol) o); | |
5597 | |
5598 throw new Exception("Unable to resolve symbol: " + sym + " in this context"); | |
5599 | |
5600 } | |
5601 | |
5602 static String destubClassName(String className){ | |
5603 //skip over prefix + '.' or '/' | |
5604 if(className.startsWith(COMPILE_STUB_PREFIX)) | |
5605 return className.substring(COMPILE_STUB_PREFIX.length()+1); | |
5606 return className; | |
5607 } | |
5608 | |
5609 static Type getType(Class c){ | |
5610 String descriptor = Type.getType(c).getDescriptor(); | |
5611 if(descriptor.startsWith("L")) | |
5612 descriptor = "L" + destubClassName(descriptor.substring(1)); | |
5613 return Type.getType(descriptor); | |
5614 } | |
5615 | |
5616 static Object resolve(Symbol sym, boolean allowPrivate) throws Exception{ | |
5617 return resolveIn(currentNS(), sym, allowPrivate); | |
5618 } | |
5619 | |
5620 static Object resolve(Symbol sym) throws Exception{ | |
5621 return resolveIn(currentNS(), sym, false); | |
5622 } | |
5623 | |
5624 static Namespace namespaceFor(Symbol sym){ | |
5625 return namespaceFor(currentNS(), sym); | |
5626 } | |
5627 | |
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) | |
5634 { | |
5635 // ...otherwise check the Namespaces map. | |
5636 ns = Namespace.find(nsSym); | |
5637 } | |
5638 return ns; | |
5639 } | |
5640 | |
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) | |
5644 { | |
5645 Namespace ns = namespaceFor(n, sym); | |
5646 if(ns == null) | |
5647 throw new Exception("No such namespace: " + sym.ns); | |
5648 | |
5649 Var v = ns.findInternedVar(Symbol.create(sym.name)); | |
5650 if(v == null) | |
5651 throw new Exception("No such var: " + sym); | |
5652 else if(v.ns != currentNS() && !v.isPublic() && !allowPrivate) | |
5653 throw new IllegalStateException("var: " + sym + " is not public"); | |
5654 return v; | |
5655 } | |
5656 else if(sym.name.indexOf('.') > 0 || sym.name.charAt(0) == '[') | |
5657 { | |
5658 return RT.classForName(sym.name); | |
5659 } | |
5660 else if(sym.equals(NS)) | |
5661 return RT.NS_VAR; | |
5662 else if(sym.equals(IN_NS)) | |
5663 return RT.IN_NS_VAR; | |
5664 else | |
5665 { | |
5666 if(Util.equals(sym,COMPILE_STUB_SYM.get())) | |
5667 return COMPILE_STUB_CLASS.get(); | |
5668 Object o = n.getMapping(sym); | |
5669 if(o == null) | |
5670 { | |
5671 if(RT.booleanCast(RT.ALLOW_UNRESOLVED_VARS.deref())) | |
5672 { | |
5673 return sym; | |
5674 } | |
5675 else | |
5676 { | |
5677 throw new Exception("Unable to resolve symbol: " + sym + " in this context"); | |
5678 } | |
5679 } | |
5680 return o; | |
5681 } | |
5682 } | |
5683 | |
5684 | |
5685 static public Object maybeResolveIn(Namespace n, Symbol sym) throws Exception{ | |
5686 //note - ns-qualified vars must already exist | |
5687 if(sym.ns != null) | |
5688 { | |
5689 Namespace ns = namespaceFor(n, sym); | |
5690 if(ns == null) | |
5691 return null; | |
5692 Var v = ns.findInternedVar(Symbol.create(sym.name)); | |
5693 if(v == null) | |
5694 return null; | |
5695 return v; | |
5696 } | |
5697 else if(sym.name.indexOf('.') > 0 && !sym.name.endsWith(".") | |
5698 || sym.name.charAt(0) == '[') | |
5699 { | |
5700 return RT.classForName(sym.name); | |
5701 } | |
5702 else if(sym.equals(NS)) | |
5703 return RT.NS_VAR; | |
5704 else if(sym.equals(IN_NS)) | |
5705 return RT.IN_NS_VAR; | |
5706 else | |
5707 { | |
5708 Object o = n.getMapping(sym); | |
5709 return o; | |
5710 } | |
5711 } | |
5712 | |
5713 | |
5714 static Var lookupVar(Symbol sym, boolean internNew) throws Exception{ | |
5715 Var var = null; | |
5716 | |
5717 //note - ns-qualified vars in other namespaces must already exist | |
5718 if(sym.ns != null) | |
5719 { | |
5720 Namespace ns = namespaceFor(sym); | |
5721 if(ns == null) | |
5722 return null; | |
5723 //throw new Exception("No such namespace: " + sym.ns); | |
5724 Symbol name = Symbol.create(sym.name); | |
5725 if(internNew && ns == currentNS()) | |
5726 var = currentNS().intern(name); | |
5727 else | |
5728 var = ns.findInternedVar(name); | |
5729 } | |
5730 else if(sym.equals(NS)) | |
5731 var = RT.NS_VAR; | |
5732 else if(sym.equals(IN_NS)) | |
5733 var = RT.IN_NS_VAR; | |
5734 else | |
5735 { | |
5736 //is it mapped? | |
5737 Object o = currentNS().getMapping(sym); | |
5738 if(o == null) | |
5739 { | |
5740 //introduce a new var in the current ns | |
5741 if(internNew) | |
5742 var = currentNS().intern(Symbol.create(sym.name)); | |
5743 } | |
5744 else if(o instanceof Var) | |
5745 { | |
5746 var = (Var) o; | |
5747 } | |
5748 else | |
5749 { | |
5750 throw new Exception("Expecting var, but " + sym + " is mapped to " + o); | |
5751 } | |
5752 } | |
5753 if(var != null) | |
5754 registerVar(var); | |
5755 return var; | |
5756 } | |
5757 | |
5758 private static void registerVar(Var var) throws Exception{ | |
5759 if(!VARS.isBound()) | |
5760 return; | |
5761 IPersistentMap varsMap = (IPersistentMap) VARS.deref(); | |
5762 Object id = RT.get(varsMap, var); | |
5763 if(id == null) | |
5764 { | |
5765 VARS.set(RT.assoc(varsMap, var, registerConstant(var))); | |
5766 } | |
5767 // if(varsMap != null && RT.get(varsMap, var) == null) | |
5768 // VARS.set(RT.assoc(varsMap, var, var)); | |
5769 } | |
5770 | |
5771 static Namespace currentNS(){ | |
5772 return (Namespace) RT.CURRENT_NS.deref(); | |
5773 } | |
5774 | |
5775 static void closeOver(LocalBinding b, ObjMethod method){ | |
5776 if(b != null && method != null) | |
5777 { | |
5778 if(RT.get(method.locals, b) == null) | |
5779 { | |
5780 method.objx.closes = (IPersistentMap) RT.assoc(method.objx.closes, b, b); | |
5781 closeOver(b, method.parent); | |
5782 } | |
5783 else if(IN_CATCH_FINALLY.deref() != null) | |
5784 { | |
5785 method.localsUsedInCatchFinally = (PersistentHashSet) method.localsUsedInCatchFinally.cons(b.idx); | |
5786 } | |
5787 } | |
5788 } | |
5789 | |
5790 | |
5791 static LocalBinding referenceLocal(Symbol sym) throws Exception{ | |
5792 if(!LOCAL_ENV.isBound()) | |
5793 return null; | |
5794 LocalBinding b = (LocalBinding) RT.get(LOCAL_ENV.deref(), sym); | |
5795 if(b != null) | |
5796 { | |
5797 ObjMethod method = (ObjMethod) METHOD.deref(); | |
5798 closeOver(b, method); | |
5799 } | |
5800 return b; | |
5801 } | |
5802 | |
5803 private static Symbol tagOf(Object o){ | |
5804 Object tag = RT.get(RT.meta(o), RT.TAG_KEY); | |
5805 if(tag instanceof Symbol) | |
5806 return (Symbol) tag; | |
5807 else if(tag instanceof String) | |
5808 return Symbol.intern(null, (String) tag); | |
5809 return null; | |
5810 } | |
5811 | |
5812 public static Object loadFile(String file) throws Exception{ | |
5813 // File fo = new File(file); | |
5814 // if(!fo.exists()) | |
5815 // return null; | |
5816 | |
5817 FileInputStream f = new FileInputStream(file); | |
5818 try | |
5819 { | |
5820 return load(new InputStreamReader(f, RT.UTF8), new File(file).getAbsolutePath(), (new File(file)).getName()); | |
5821 } | |
5822 finally | |
5823 { | |
5824 f.close(); | |
5825 } | |
5826 } | |
5827 | |
5828 public static Object load(Reader rdr) throws Exception{ | |
5829 return load(rdr, null, "NO_SOURCE_FILE"); | |
5830 } | |
5831 | |
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 )); | |
5850 | |
5851 try | |
5852 { | |
5853 for(Object r = LispReader.read(pushbackReader, false, EOF, false); r != EOF; | |
5854 r = LispReader.read(pushbackReader, false, EOF, false)) | |
5855 { | |
5856 LINE_AFTER.set(pushbackReader.getLineNumber()); | |
5857 ret = eval(r,false); | |
5858 LINE_BEFORE.set(pushbackReader.getLineNumber()); | |
5859 } | |
5860 } | |
5861 catch(LispReader.ReaderException e) | |
5862 { | |
5863 throw new CompilerException(sourceName, e.line, e.getCause()); | |
5864 } | |
5865 finally | |
5866 { | |
5867 Var.popThreadBindings(); | |
5868 } | |
5869 return ret; | |
5870 } | |
5871 | |
5872 static public void writeClassFile(String internalName, byte[] bytecode) throws Exception{ | |
5873 String genPath = (String) COMPILE_PATH.deref(); | |
5874 if(genPath == null) | |
5875 throw new Exception("*compile-path* not set"); | |
5876 String[] dirs = internalName.split("/"); | |
5877 String p = genPath; | |
5878 for(int i = 0; i < dirs.length - 1; i++) | |
5879 { | |
5880 p += File.separator + dirs[i]; | |
5881 (new File(p)).mkdir(); | |
5882 } | |
5883 String path = genPath + File.separator + internalName + ".class"; | |
5884 File cf = new File(path); | |
5885 cf.createNewFile(); | |
5886 FileOutputStream cfs = new FileOutputStream(cf); | |
5887 try | |
5888 { | |
5889 cfs.write(bytecode); | |
5890 cfs.flush(); | |
5891 cfs.getFD().sync(); | |
5892 } | |
5893 finally | |
5894 { | |
5895 cfs.close(); | |
5896 } | |
5897 } | |
5898 | |
5899 public static void pushNS(){ | |
5900 Var.pushThreadBindings(PersistentHashMap.create(Var.intern(Symbol.create("clojure.core"), | |
5901 Symbol.create("*ns*")), null)); | |
5902 } | |
5903 | |
5904 public static ILookupThunk getLookupThunk(Object target, Keyword k){ | |
5905 return null; //To change body of created methods use File | Settings | File Templates. | |
5906 } | |
5907 | |
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 | |
5917 { | |
5918 form = macroexpand(form); | |
5919 if(form instanceof IPersistentCollection && Util.equals(RT.first(form), DO)) | |
5920 { | |
5921 for(ISeq s = RT.next(form); s != null; s = RT.next(s)) | |
5922 { | |
5923 compile1(gen, objx, RT.first(s)); | |
5924 } | |
5925 } | |
5926 else | |
5927 { | |
5928 Expr expr = analyze(C.EVAL, form); | |
5929 objx.keywords = (IPersistentMap) KEYWORDS.deref(); | |
5930 objx.vars = (IPersistentMap) VARS.deref(); | |
5931 objx.constants = (PersistentVector) CONSTANTS.deref(); | |
5932 expr.emit(C.EXPRESSION, objx, gen); | |
5933 expr.eval(); | |
5934 } | |
5935 } | |
5936 finally | |
5937 { | |
5938 Var.popThreadBindings(); | |
5939 } | |
5940 } | |
5941 | |
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"); | |
5945 | |
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 )); | |
5967 | |
5968 try | |
5969 { | |
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; | |
5974 | |
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); | |
5979 | |
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(); | |
5987 | |
5988 for(Object r = LispReader.read(pushbackReader, false, EOF, false); r != EOF; | |
5989 r = LispReader.read(pushbackReader, false, EOF, false)) | |
5990 { | |
5991 LINE_AFTER.set(pushbackReader.getLineNumber()); | |
5992 compile1(gen, objx, r); | |
5993 LINE_BEFORE.set(pushbackReader.getLineNumber()); | |
5994 } | |
5995 //end of load | |
5996 gen.returnValue(); | |
5997 gen.endMethod(); | |
5998 | |
5999 //static fields for constants | |
6000 for(int i = 0; i < objx.constants.count(); i++) | |
6001 { | |
6002 cv.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, objx.constantName(i), objx.constantType(i).getDescriptor(), | |
6003 null, null); | |
6004 } | |
6005 | |
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(); | |
6017 | |
6018 if(objx.constants.count() > 0) | |
6019 { | |
6020 objx.emitConstants(clinitgen); | |
6021 } | |
6022 clinitgen.invokeStatic(Type.getType(Compiler.class), Method.getMethod("void pushNS()")); | |
6023 clinitgen.mark(startTry); | |
6024 clinitgen.invokeStatic(objx.objtype, Method.getMethod("void load()")); | |
6025 clinitgen.mark(endTry); | |
6026 clinitgen.invokeStatic(VAR_TYPE, Method.getMethod("void popThreadBindings()")); | |
6027 clinitgen.goTo(end); | |
6028 | |
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); | |
6035 | |
6036 //end of static init | |
6037 clinitgen.returnValue(); | |
6038 clinitgen.endMethod(); | |
6039 | |
6040 //end of class | |
6041 cv.visitEnd(); | |
6042 | |
6043 writeClassFile(objx.internalName, cw.toByteArray()); | |
6044 } | |
6045 catch(LispReader.ReaderException e) | |
6046 { | |
6047 throw new CompilerException(sourceName, e.line, e.getCause()); | |
6048 } | |
6049 finally | |
6050 { | |
6051 Var.popThreadBindings(); | |
6052 } | |
6053 return ret; | |
6054 } | |
6055 | |
6056 | |
6057 static public class NewInstanceExpr extends ObjExpr{ | |
6058 //IPersistentMap optionsMap = PersistentArrayMap.EMPTY; | |
6059 IPersistentCollection methods; | |
6060 | |
6061 Map<IPersistentVector,java.lang.reflect.Method> mmap; | |
6062 Map<IPersistentVector,Set<Class>> covariants; | |
6063 | |
6064 public NewInstanceExpr(Object tag){ | |
6065 super(tag); | |
6066 } | |
6067 | |
6068 static class DeftypeParser implements IParser{ | |
6069 public Expr parse(C context, final Object frm) throws Exception{ | |
6070 ISeq rform = (ISeq) frm; | |
6071 //(deftype* tagname classname [fields] :implements [interfaces] :tag tagname methods*) | |
6072 rform = RT.next(rform); | |
6073 String tagname = ((Symbol) rform.first()).toString(); | |
6074 rform = rform.next(); | |
6075 Symbol classname = (Symbol) rform.first(); | |
6076 rform = rform.next(); | |
6077 IPersistentVector fields = (IPersistentVector) rform.first(); | |
6078 rform = rform.next(); | |
6079 IPersistentMap opts = PersistentHashMap.EMPTY; | |
6080 while(rform != null && rform.first() instanceof Keyword) | |
6081 { | |
6082 opts = opts.assoc(rform.first(), RT.second(rform)); | |
6083 rform = rform.next().next(); | |
6084 } | |
6085 | |
6086 ObjExpr ret = build((IPersistentVector)RT.get(opts,implementsKey,PersistentVector.EMPTY),fields,null,tagname, classname, | |
6087 (Symbol) RT.get(opts,RT.TAG_KEY),rform, frm); | |
6088 return ret; | |
6089 } | |
6090 } | |
6091 | |
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; | |
6102 | |
6103 ISeq rform = RT.next(form); | |
6104 | |
6105 IPersistentVector interfaces = ((IPersistentVector) RT.first(rform)).cons(Symbol.intern("clojure.lang.IObj")); | |
6106 | |
6107 | |
6108 rform = RT.next(rform); | |
6109 | |
6110 | |
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; | |
6117 } | |
6118 } | |
6119 | |
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); | |
6124 | |
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); | |
6130 | |
6131 if(thisSym != null) | |
6132 ret.thisName = thisSym.name; | |
6133 | |
6134 if(fieldSyms != null) | |
6135 { | |
6136 IPersistentMap fmap = PersistentHashMap.EMPTY; | |
6137 Object[] closesvec = new Object[2 * fieldSyms.count()]; | |
6138 for(int i=0;i<fieldSyms.count();i++) | |
6139 { | |
6140 Symbol sym = (Symbol) fieldSyms.nth(i); | |
6141 LocalBinding lb = new LocalBinding(-1, sym, null, | |
6142 new MethodParamExpr(tagClass(tagOf(sym))),false,null); | |
6143 fmap = fmap.assoc(sym, lb); | |
6144 closesvec[i*2] = lb; | |
6145 closesvec[i*2 + 1] = lb; | |
6146 } | |
6147 | |
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++; | |
6154 } | |
6155 //todo - set up volatiles | |
6156 // ret.volatiles = PersistentHashSet.create(RT.seq(RT.get(ret.optionsMap, volatileKey))); | |
6157 | |
6158 PersistentVector interfaces = PersistentVector.EMPTY; | |
6159 for(ISeq s = RT.seq(interfaceSyms);s!=null;s = s.next()) | |
6160 { | |
6161 Class c = (Class) resolve((Symbol) s.first()); | |
6162 if(!c.isInterface()) | |
6163 throw new IllegalArgumentException("only interfaces are supported, had: " + c.getName()); | |
6164 interfaces = interfaces.cons(c); | |
6165 } | |
6166 Class superClass = Object.class; | |
6167 Map[] mc = gatherMethods(superClass,RT.seq(interfaces)); | |
6168 Map overrideables = mc[0]; | |
6169 Map covariants = mc[1]; | |
6170 ret.mmap = overrideables; | |
6171 ret.covariants = covariants; | |
6172 | |
6173 String[] inames = interfaceNames(interfaces); | |
6174 | |
6175 Class stub = compileStub(slashname(superClass),ret, inames, frm); | |
6176 Symbol thistag = Symbol.intern(null,stub.getName()); | |
6177 | |
6178 try | |
6179 { | |
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()) | |
6190 { | |
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)); | |
6195 } | |
6196 | |
6197 //now (methodname [args] body)* | |
6198 ret.line = (Integer) LINE.deref(); | |
6199 IPersistentCollection methods = null; | |
6200 for(ISeq s = methodForms; s != null; s = RT.next(s)) | |
6201 { | |
6202 NewInstanceMethod m = NewInstanceMethod.parse(ret, (ISeq) RT.first(s),thistag, overrideables); | |
6203 methods = RT.conj(methods, m); | |
6204 } | |
6205 | |
6206 | |
6207 ret.methods = methods; | |
6208 ret.keywords = (IPersistentMap) KEYWORDS.deref(); | |
6209 ret.vars = (IPersistentMap) VARS.deref(); | |
6210 ret.constants = (PersistentVector) CONSTANTS.deref(); | |
6211 ret.constantsID = RT.nextID(); | |
6212 ret.keywordCallsites = (IPersistentVector) KEYWORD_CALLSITES.deref(); | |
6213 ret.protocolCallsites = (IPersistentVector) PROTOCOL_CALLSITES.deref(); | |
6214 ret.varCallsites = (IPersistentVector) VAR_CALLSITES.deref(); | |
6215 } | |
6216 finally | |
6217 { | |
6218 if(ret.isDeftype()) | |
6219 Var.popThreadBindings(); | |
6220 Var.popThreadBindings(); | |
6221 } | |
6222 | |
6223 ret.compile(slashname(superClass),inames,false); | |
6224 ret.getCompiledClass(); | |
6225 return ret; | |
6226 } | |
6227 | |
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); | |
6240 | |
6241 //instance fields for closed-overs | |
6242 for(ISeq s = RT.keys(ret.closes); s != null; s = s.next()) | |
6243 { | |
6244 LocalBinding lb = (LocalBinding) s.first(); | |
6245 int access = ACC_PUBLIC + (ret.isVolatile(lb) ? ACC_VOLATILE : | |
6246 ret.isMutable(lb) ? 0 : | |
6247 ACC_FINAL); | |
6248 if(lb.getPrimitiveType() != null) | |
6249 cv.visitField(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); | |
6256 } | |
6257 | |
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(); | |
6270 | |
6271 if(ret.altCtorDrops > 0) | |
6272 { | |
6273 Type[] ctorTypes = ret.ctorTypes(); | |
6274 Type[] altCtorTypes = new Type[ctorTypes.length-ret.altCtorDrops]; | |
6275 for(int i=0;i<altCtorTypes.length;i++) | |
6276 altCtorTypes[i] = ctorTypes[i]; | |
6277 Method alt = new Method("<init>", Type.VOID_TYPE, altCtorTypes); | |
6278 ctorgen = new GeneratorAdapter(ACC_PUBLIC, | |
6279 alt, | |
6280 null, | |
6281 null, | |
6282 cv); | |
6283 ctorgen.visitCode(); | |
6284 ctorgen.loadThis(); | |
6285 ctorgen.loadArgs(); | |
6286 for(int i=0;i<ret.altCtorDrops;i++) | |
6287 ctorgen.visitInsn(Opcodes.ACONST_NULL); | |
6288 | |
6289 ctorgen.invokeConstructor(Type.getObjectType(COMPILE_STUB_PREFIX + "/" + ret.internalName), | |
6290 new Method("<init>", Type.VOID_TYPE, ctorTypes)); | |
6291 | |
6292 ctorgen.returnValue(); | |
6293 ctorgen.endMethod(); | |
6294 } | |
6295 //end of class | |
6296 cv.visitEnd(); | |
6297 | |
6298 byte[] bytecode = cw.toByteArray(); | |
6299 DynamicClassLoader loader = (DynamicClassLoader) LOADER.deref(); | |
6300 return loader.defineClass(COMPILE_STUB_PREFIX + "." + ret.name, bytecode, frm); | |
6301 } | |
6302 | |
6303 static String[] interfaceNames(IPersistentVector interfaces){ | |
6304 int icnt = interfaces.count(); | |
6305 String[] inames = icnt > 0 ? new String[icnt] : null; | |
6306 for(int i=0;i<icnt;i++) | |
6307 inames[i] = slashname((Class) interfaces.nth(i)); | |
6308 return inames; | |
6309 } | |
6310 | |
6311 | |
6312 static String slashname(Class c){ | |
6313 return c.getName().replace('.', '/'); | |
6314 } | |
6315 | |
6316 | |
6317 protected void emitMethods(ClassVisitor cv){ | |
6318 for(ISeq s = RT.seq(methods); s != null; s = s.next()) | |
6319 { | |
6320 ObjMethod method = (ObjMethod) s.first(); | |
6321 method.emit(this, cv); | |
6322 } | |
6323 //emit bridge methods | |
6324 for(Map.Entry<IPersistentVector,Set<Class>> e : covariants.entrySet()) | |
6325 { | |
6326 java.lang.reflect.Method m = mmap.get(e.getKey()); | |
6327 Class[] params = m.getParameterTypes(); | |
6328 Type[] argTypes = new Type[params.length]; | |
6329 | |
6330 for(int i = 0; i < params.length; i++) | |
6331 { | |
6332 argTypes[i] = Type.getType(params[i]); | |
6333 } | |
6334 | |
6335 Method target = new Method(m.getName(), Type.getType(m.getReturnType()), argTypes); | |
6336 | |
6337 for(Class retType : e.getValue()) | |
6338 { | |
6339 Method meth = new Method(m.getName(), Type.getType(retType), argTypes); | |
6340 | |
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(); | |
6353 } | |
6354 } | |
6355 } | |
6356 | |
6357 static public IPersistentVector msig(java.lang.reflect.Method m){ | |
6358 return RT.vector(m.getName(), RT.seq(m.getParameterTypes()),m.getReturnType()); | |
6359 } | |
6360 | |
6361 static void considerMethod(java.lang.reflect.Method m, Map mm){ | |
6362 IPersistentVector mk = msig(m); | |
6363 int mods = m.getModifiers(); | |
6364 | |
6365 if(!(mm.containsKey(mk) | |
6366 || !(Modifier.isPublic(mods) || Modifier.isProtected(mods)) | |
6367 || Modifier.isStatic(mods) | |
6368 || Modifier.isFinal(mods))) | |
6369 { | |
6370 mm.put(mk, m); | |
6371 } | |
6372 } | |
6373 | |
6374 static void gatherMethods(Class c, Map mm){ | |
6375 for(; c != null; c = c.getSuperclass()) | |
6376 { | |
6377 for(java.lang.reflect.Method m : c.getDeclaredMethods()) | |
6378 considerMethod(m, mm); | |
6379 for(java.lang.reflect.Method m : c.getMethods()) | |
6380 considerMethod(m, mm); | |
6381 } | |
6382 } | |
6383 | |
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); | |
6389 | |
6390 Map<IPersistentVector,java.lang.reflect.Method> mm = new HashMap<IPersistentVector,java.lang.reflect.Method>(); | |
6391 Map<IPersistentVector,Set<Class>> covariants = new HashMap<IPersistentVector,Set<Class>>(); | |
6392 for(Object o : allm.entrySet()) | |
6393 { | |
6394 Map.Entry e = (Map.Entry) o; | |
6395 IPersistentVector mk = (IPersistentVector) e.getKey(); | |
6396 mk = (IPersistentVector) mk.pop(); | |
6397 java.lang.reflect.Method m = (java.lang.reflect.Method) e.getValue(); | |
6398 if(mm.containsKey(mk)) //covariant return | |
6399 { | |
6400 Set<Class> cvs = covariants.get(mk); | |
6401 if(cvs == null) | |
6402 { | |
6403 cvs = new HashSet<Class>(); | |
6404 covariants.put(mk,cvs); | |
6405 } | |
6406 java.lang.reflect.Method om = mm.get(mk); | |
6407 if(om.getReturnType().isAssignableFrom(m.getReturnType())) | |
6408 { | |
6409 cvs.add(om.getReturnType()); | |
6410 mm.put(mk, m); | |
6411 } | |
6412 else | |
6413 cvs.add(m.getReturnType()); | |
6414 } | |
6415 else | |
6416 mm.put(mk, m); | |
6417 } | |
6418 return new Map[]{mm,covariants}; | |
6419 } | |
6420 } | |
6421 | |
6422 public static class NewInstanceMethod extends ObjMethod{ | |
6423 String name; | |
6424 Type[] argTypes; | |
6425 Type retType; | |
6426 Class retClass; | |
6427 Class[] exclasses; | |
6428 | |
6429 static Symbol dummyThis = Symbol.intern(null,"dummy_this_dlskjsdfower"); | |
6430 private IPersistentVector parms; | |
6431 | |
6432 public NewInstanceMethod(ObjExpr objx, ObjMethod parent){ | |
6433 super(objx, parent); | |
6434 } | |
6435 | |
6436 int numParams(){ | |
6437 return argLocals.count(); | |
6438 } | |
6439 | |
6440 String getMethodName(){ | |
6441 return name; | |
6442 } | |
6443 | |
6444 Type getReturnType(){ | |
6445 return retType; | |
6446 } | |
6447 | |
6448 Type[] getArgTypes(){ | |
6449 return argTypes; | |
6450 } | |
6451 | |
6452 | |
6453 | |
6454 static public IPersistentVector msig(String name,Class[] paramTypes){ | |
6455 return RT.vector(name,RT.seq(paramTypes)); | |
6456 } | |
6457 | |
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) | |
6467 { | |
6468 throw new IllegalArgumentException("Must supply at least one argument for 'this' in: " + dotname); | |
6469 } | |
6470 Symbol thisName = (Symbol) parms.nth(0); | |
6471 parms = RT.subvec(parms,1,parms.count()); | |
6472 ISeq body = RT.next(RT.next(form)); | |
6473 try | |
6474 { | |
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 )); | |
6488 | |
6489 //register 'this' as local 0 | |
6490 if(thisName != null) | |
6491 registerLocal((thisName == null) ? dummyThis:thisName,thistag, null,false); | |
6492 else | |
6493 getAndIncLocalNum(); | |
6494 | |
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()]; | |
6501 | |
6502 for(int i = 0; i < parms.count(); i++) | |
6503 { | |
6504 if(!(parms.nth(i) instanceof Symbol)) | |
6505 throw new IllegalArgumentException("params must be Symbols"); | |
6506 Symbol p = (Symbol) parms.nth(i); | |
6507 Object tag = tagOf(p); | |
6508 if(tag != null) | |
6509 hinted = true; | |
6510 if(p.getNamespace() != null) | |
6511 p = Symbol.create(p.name); | |
6512 Class pclass = tagClass(tag); | |
6513 pclasses[i] = pclass; | |
6514 psyms[i] = p; | |
6515 } | |
6516 Map matches = findMethodsWithNameAndArity(name.name, parms.count(), overrideables); | |
6517 Object mk = msig(name.name, pclasses); | |
6518 java.lang.reflect.Method m = null; | |
6519 if(matches.size() > 0) | |
6520 { | |
6521 //multiple methods | |
6522 if(matches.size() > 1) | |
6523 { | |
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()); | |
6533 } | |
6534 else //one match | |
6535 { | |
6536 //if hinted, validate match, | |
6537 if(hinted) | |
6538 { | |
6539 m = (java.lang.reflect.Method) matches.get(mk); | |
6540 if(m == null) | |
6541 throw new IllegalArgumentException("Can't find matching method: " + name.name + | |
6542 ", leave off hints for auto match."); | |
6543 if(m.getReturnType() != method.retClass) | |
6544 throw new IllegalArgumentException("Mismatched return type: " + name.name + | |
6545 ", expected: " + m.getReturnType().getName() + ", had: " + method.retClass.getName()); | |
6546 } | |
6547 else //adopt found method sig | |
6548 { | |
6549 m = (java.lang.reflect.Method) matches.values().iterator().next(); | |
6550 method.retClass = m.getReturnType(); | |
6551 pclasses = m.getParameterTypes(); | |
6552 } | |
6553 } | |
6554 } | |
6555 // else if(findMethodsWithName(name.name,allmethods).size()>0) | |
6556 // throw new IllegalArgumentException("Can't override/overload method: " + name.name); | |
6557 else | |
6558 throw new IllegalArgumentException("Can't define method not in interfaces: " + name.name); | |
6559 | |
6560 //else | |
6561 //validate unque name+arity among additional methods | |
6562 | |
6563 method.retType = Type.getType(method.retClass); | |
6564 method.exclasses = m.getExceptionTypes(); | |
6565 | |
6566 for(int i = 0; i < parms.count(); i++) | |
6567 { | |
6568 LocalBinding lb = registerLocal(psyms[i], null, new MethodParamExpr(pclasses[i]),true); | |
6569 argLocals = argLocals.assocN(i,lb); | |
6570 method.argTypes[i] = Type.getType(pclasses[i]); | |
6571 } | |
6572 for(int i = 0; i < parms.count(); i++) | |
6573 { | |
6574 if(pclasses[i] == long.class || pclasses[i] == double.class) | |
6575 getAndIncLocalNum(); | |
6576 } | |
6577 LOOP_LOCALS.set(argLocals); | |
6578 method.name = name.name; | |
6579 method.methodMeta = RT.meta(name); | |
6580 method.parms = parms; | |
6581 method.argLocals = argLocals; | |
6582 method.body = (new BodyExpr.Parser()).parse(C.RETURN, body); | |
6583 return method; | |
6584 } | |
6585 finally | |
6586 { | |
6587 Var.popThreadBindings(); | |
6588 } | |
6589 } | |
6590 | |
6591 private static Map findMethodsWithNameAndArity(String name, int arity, Map mm){ | |
6592 Map ret = new HashMap(); | |
6593 for(Object o : mm.entrySet()) | |
6594 { | |
6595 Map.Entry e = (Map.Entry) o; | |
6596 java.lang.reflect.Method m = (java.lang.reflect.Method) e.getValue(); | |
6597 if(name.equals(m.getName()) && m.getParameterTypes().length == arity) | |
6598 ret.put(e.getKey(), e.getValue()); | |
6599 } | |
6600 return ret; | |
6601 } | |
6602 | |
6603 private static Map findMethodsWithName(String name, Map mm){ | |
6604 Map ret = new HashMap(); | |
6605 for(Object o : mm.entrySet()) | |
6606 { | |
6607 Map.Entry e = (Map.Entry) o; | |
6608 java.lang.reflect.Method m = (java.lang.reflect.Method) e.getValue(); | |
6609 if(name.equals(m.getName())) | |
6610 ret.put(e.getKey(), e.getValue()); | |
6611 } | |
6612 return ret; | |
6613 } | |
6614 | |
6615 public void emit(ObjExpr obj, ClassVisitor cv){ | |
6616 Method m = new Method(getMethodName(), getReturnType(), getArgTypes()); | |
6617 | |
6618 Type[] extypes = null; | |
6619 if(exclasses.length > 0) | |
6620 { | |
6621 extypes = new Type[exclasses.length]; | |
6622 for(int i=0;i<exclasses.length;i++) | |
6623 extypes[i] = Type.getType(exclasses[i]); | |
6624 } | |
6625 GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC, | |
6626 m, | |
6627 null, | |
6628 extypes, | |
6629 cv); | |
6630 addAnnotation(gen,methodMeta); | |
6631 for(int i = 0; i < parms.count(); i++) | |
6632 { | |
6633 IPersistentMap meta = RT.meta(parms.nth(i)); | |
6634 addParameterAnnotation(gen, meta, i); | |
6635 } | |
6636 gen.visitCode(); | |
6637 Label loopLabel = gen.mark(); | |
6638 gen.visitLineNumber(line, loopLabel); | |
6639 try | |
6640 { | |
6641 Var.pushThreadBindings(RT.map(LOOP_LABEL, loopLabel, METHOD, this)); | |
6642 MaybePrimitiveExpr be = (MaybePrimitiveExpr) body; | |
6643 if(Util.isPrimitive(retClass) && be.canEmitPrimitive()) | |
6644 { | |
6645 if(be.getJavaClass() == retClass) | |
6646 be.emitUnboxed(C.RETURN,obj,gen); | |
6647 //todo - support the standard widening conversions | |
6648 else | |
6649 throw new IllegalArgumentException("Mismatched primitive return, expected: " | |
6650 + retClass + ", had: " + be.getJavaClass()); | |
6651 } | |
6652 else | |
6653 { | |
6654 body.emit(C.RETURN, obj, gen); | |
6655 if(retClass == void.class) | |
6656 { | |
6657 gen.pop(); | |
6658 } | |
6659 else | |
6660 gen.unbox(retType); | |
6661 } | |
6662 | |
6663 Label end = gen.mark(); | |
6664 gen.visitLocalVariable("this", obj.objtype.getDescriptor(), null, loopLabel, end, 0); | |
6665 for(ISeq lbs = argLocals.seq(); lbs != null; lbs = lbs.next()) | |
6666 { | |
6667 LocalBinding lb = (LocalBinding) lbs.first(); | |
6668 gen.visitLocalVariable(lb.name, argTypes[lb.idx-1].getDescriptor(), null, loopLabel, end, lb.idx); | |
6669 } | |
6670 } | |
6671 catch(Exception e) | |
6672 { | |
6673 throw new RuntimeException(e); | |
6674 } | |
6675 finally | |
6676 { | |
6677 Var.popThreadBindings(); | |
6678 } | |
6679 | |
6680 gen.returnValue(); | |
6681 //gen.visitMaxs(1, 1); | |
6682 gen.endMethod(); | |
6683 } | |
6684 } | |
6685 | |
6686 static Class primClass(Symbol sym){ | |
6687 if(sym == null) | |
6688 return null; | |
6689 Class c = null; | |
6690 if(sym.name.equals("int")) | |
6691 c = int.class; | |
6692 else if(sym.name.equals("long")) | |
6693 c = long.class; | |
6694 else if(sym.name.equals("float")) | |
6695 c = float.class; | |
6696 else if(sym.name.equals("double")) | |
6697 c = double.class; | |
6698 else if(sym.name.equals("char")) | |
6699 c = char.class; | |
6700 else if(sym.name.equals("short")) | |
6701 c = short.class; | |
6702 else if(sym.name.equals("byte")) | |
6703 c = byte.class; | |
6704 else if(sym.name.equals("boolean")) | |
6705 c = boolean.class; | |
6706 else if(sym.name.equals("void")) | |
6707 c = void.class; | |
6708 return c; | |
6709 } | |
6710 | |
6711 static Class tagClass(Object tag) throws Exception{ | |
6712 if(tag == null) | |
6713 return Object.class; | |
6714 Class c = null; | |
6715 if(tag instanceof Symbol) | |
6716 c = primClass((Symbol) tag); | |
6717 if(c == null) | |
6718 c = HostExpr.tagToClass(tag); | |
6719 return c; | |
6720 } | |
6721 | |
6722 static public class MethodParamExpr implements Expr, MaybePrimitiveExpr{ | |
6723 final Class c; | |
6724 | |
6725 public MethodParamExpr(Class c){ | |
6726 this.c = c; | |
6727 } | |
6728 | |
6729 public Object eval() throws Exception{ | |
6730 throw new Exception("Can't eval"); | |
6731 } | |
6732 | |
6733 public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ | |
6734 throw new RuntimeException("Can't emit"); | |
6735 } | |
6736 | |
6737 public boolean hasJavaClass() throws Exception{ | |
6738 return c != null; | |
6739 } | |
6740 | |
6741 public Class getJavaClass() throws Exception{ | |
6742 return c; | |
6743 } | |
6744 | |
6745 public boolean canEmitPrimitive(){ | |
6746 return Util.isPrimitive(c); | |
6747 } | |
6748 | |
6749 public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){ | |
6750 throw new RuntimeException("Can't emit"); | |
6751 } | |
6752 } | |
6753 | |
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; | |
6761 | |
6762 public final int line; | |
6763 | |
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)"); | |
6767 | |
6768 | |
6769 public CaseExpr(int line, LocalBindingExpr expr, int shift, int mask, int low, int high, Expr defaultExpr, | |
6770 HashMap<Integer,Expr> tests,HashMap<Integer,Expr> thens, boolean allKeywords){ | |
6771 this.expr = expr; | |
6772 this.shift = shift; | |
6773 this.mask = mask; | |
6774 this.low = low; | |
6775 this.high = high; | |
6776 this.defaultExpr = defaultExpr; | |
6777 this.tests = tests; | |
6778 this.thens = thens; | |
6779 this.line = line; | |
6780 this.allKeywords = allKeywords; | |
6781 } | |
6782 | |
6783 public Object eval() throws Exception{ | |
6784 throw new UnsupportedOperationException("Can't eval case"); | |
6785 } | |
6786 | |
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(); | |
6791 | |
6792 for(Integer i : tests.keySet()) | |
6793 { | |
6794 labels.put(i, gen.newLabel()); | |
6795 } | |
6796 | |
6797 Label[] la = new Label[(high-low)+1]; | |
6798 | |
6799 for(int i=low;i<=high;i++) | |
6800 { | |
6801 la[i-low] = labels.containsKey(i) ? labels.get(i) : defaultLabel; | |
6802 } | |
6803 | |
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); | |
6812 | |
6813 for(Integer i : labels.keySet()) | |
6814 { | |
6815 gen.mark(labels.get(i)); | |
6816 expr.emit(C.EXPRESSION, objx, gen); | |
6817 tests.get(i).emit(C.EXPRESSION, objx, gen); | |
6818 if(allKeywords) | |
6819 { | |
6820 gen.visitJumpInsn(IF_ACMPNE, defaultLabel); | |
6821 } | |
6822 else | |
6823 { | |
6824 gen.invokeStatic(UTIL_TYPE, equalsMethod); | |
6825 gen.ifZCmp(GeneratorAdapter.EQ, defaultLabel); | |
6826 } | |
6827 thens.get(i).emit(C.EXPRESSION,objx,gen); | |
6828 gen.goTo(endLabel); | |
6829 } | |
6830 | |
6831 gen.mark(defaultLabel); | |
6832 defaultExpr.emit(C.EXPRESSION, objx, gen); | |
6833 gen.mark(endLabel); | |
6834 if(context == C.STATEMENT) | |
6835 gen.pop(); | |
6836 } | |
6837 | |
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(); | |
6850 | |
6851 LocalBindingExpr testexpr = (LocalBindingExpr) analyze(C.EXPRESSION, args.nth(0)); | |
6852 testexpr.shouldClear = false; | |
6853 | |
6854 PathNode branch = new PathNode(PATHTYPE.BRANCH, (PathNode) CLEAR_PATH.get()); | |
6855 for(Object o : ((Map)args.nth(6)).entrySet()) | |
6856 { | |
6857 Map.Entry e = (Map.Entry) o; | |
6858 Integer minhash = (Integer) e.getKey(); | |
6859 MapEntry me = (MapEntry) e.getValue(); | |
6860 Expr testExpr = new ConstantExpr(me.getKey()); | |
6861 tests.put(minhash, testExpr); | |
6862 Expr thenExpr; | |
6863 try { | |
6864 Var.pushThreadBindings( | |
6865 RT.map(CLEAR_PATH, new PathNode(PATHTYPE.PATH,branch))); | |
6866 thenExpr = analyze(context, me.getValue()); | |
6867 } | |
6868 finally{ | |
6869 Var.popThreadBindings(); | |
6870 } | |
6871 thens.put(minhash, thenExpr); | |
6872 } | |
6873 | |
6874 Expr defaultExpr; | |
6875 try { | |
6876 Var.pushThreadBindings( | |
6877 RT.map(CLEAR_PATH, new PathNode(PATHTYPE.PATH,branch))); | |
6878 defaultExpr = analyze(context, args.nth(5)); | |
6879 } | |
6880 finally{ | |
6881 Var.popThreadBindings(); | |
6882 } | |
6883 | |
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); | |
6892 | |
6893 } | |
6894 } | |
6895 } | |
6896 | |
6897 } |