diff options
author | Rich Hickey <richhickey@gmail.com> | 2007-09-03 17:20:12 +0000 |
---|---|---|
committer | Rich Hickey <richhickey@gmail.com> | 2007-09-03 17:20:12 +0000 |
commit | a7241b23866e9f628349232034090aada1ef4010 (patch) | |
tree | b127a238158e458164e0c73d0172074e541e12d2 /src | |
parent | 3a4ab3a82e9b1b750da2d3c536d4867f69654ce5 (diff) |
reflection based host access
Diffstat (limited to 'src')
-rw-r--r-- | src/jvm/clojure/lang/BytecodeCompiler.java | 186 | ||||
-rw-r--r-- | src/jvm/clojure/lang/RT.java | 2 |
2 files changed, 186 insertions, 2 deletions
diff --git a/src/jvm/clojure/lang/BytecodeCompiler.java b/src/jvm/clojure/lang/BytecodeCompiler.java index c109010c..c019d300 100644 --- a/src/jvm/clojure/lang/BytecodeCompiler.java +++ b/src/jvm/clojure/lang/BytecodeCompiler.java @@ -28,12 +28,14 @@ public class BytecodeCompiler implements Opcodes{ static final Symbol DEF = Symbol.create("def"); static final Symbol LOOP = Symbol.create("loop"); static final Symbol RECUR = Symbol.create("recur"); -static final Symbol DOT = Symbol.create("."); static final Symbol IF = Symbol.create("if"); static final Symbol LET = Symbol.create("let"); static final Symbol DO = Symbol.create("do"); static final Symbol FN = Symbol.create("fn"); static final Symbol QUOTE = Symbol.create("quote"); +static final Symbol DOT = Symbol.create("."); +static final Symbol ASSIGN = Symbol.create("="); + static final Symbol THISFN = Symbol.create("thisfn"); static final Symbol IFN = Symbol.create("clojure.lang", "IFn"); static final Symbol CLASS = Symbol.create("class"); @@ -50,6 +52,7 @@ private static final Type SYMBOL_TYPE = Type.getType(Symbol.class); private static final Type NUM_TYPE = Type.getType(Num.class); private static final Type IFN_TYPE = Type.getType(IFn.class); final static Type CLASS_TYPE = Type.getType(Class.class); +final static Type REFLECTOR_TYPE = Type.getType(Reflector.class); private static final Type[][] ARG_TYPES; private static final Type[] EXCEPTION_TYPES = {Type.getType(Exception.class)}; @@ -194,6 +197,185 @@ static abstract class LiteralExpr implements Expr{ } } +static abstract class HostExpr implements Expr{ + public static Expr parse(C context, ISeq form) throws Exception{ + //(. x fieldname-sym) or (. x (methodname-sym args...)) + if(RT.length(form) != 3) + throw new IllegalArgumentException("Malformed member expression, expecting (. target member)"); + //determine static or instance + //static target must be symbol, either fully.qualified.Classname or Classname that has been imported + String className = null; + if(RT.second(form) instanceof Symbol) + { + Symbol sym = (Symbol) RT.second(form); + if(sym.ns == null) //if ns-qualified can't be classname + { + if(sym.name.indexOf('.') != -1) + className = sym.name; + else + { + IPersistentMap imports = (IPersistentMap) RT.IMPORTS.get(); + className = (String) imports.valAt(sym); + } + } + } + //at this point className will be non-null if static + Expr instance = null; + if(className == null) + instance = analyze(C.EXPRESSION, RT.second(form)); + + if(RT.third(form) instanceof Symbol) //field + { + Symbol sym = (Symbol) RT.third(form); + if(className != null) + return new StaticFieldExpr(className, sym.name); + else + return new InstanceFieldExpr(instance, sym.name); + } + else if(RT.third(form) instanceof ISeq && RT.first(RT.third(form)) instanceof Symbol) + { + Symbol sym = (Symbol) RT.first(RT.third(form)); + PersistentVector args = PersistentVector.EMPTY; + for(ISeq s = RT.rest(RT.third(form)); s != null; s = s.rest()) + args = args.cons(analyze(C.EXPRESSION, s.first())); + if(className != null) + return new StaticMethodExpr(className, sym.name, args); + else + return new InstanceMethodExpr(instance, sym.name, args); + } + else + throw new IllegalArgumentException("Malformed member expression"); + } +} + +static abstract class FieldExpr extends HostExpr{ +} + +static class InstanceFieldExpr extends FieldExpr{ + final Expr target; + final String fieldName; + final static Method getInstanceFieldMethod = Method.getMethod("Object getInstanceField(Object,String)"); + + + public InstanceFieldExpr(Expr target, String fieldName){ + this.target = target; + this.fieldName = fieldName; + } + + public Object eval() throws Exception{ + return Reflector.getInstanceField(target.eval(), fieldName); + } + + public void emit(C context, FnExpr fn, GeneratorAdapter gen){ + if(context != C.STATEMENT) + { + target.emit(C.EXPRESSION, fn, gen); + gen.push(fieldName); + gen.invokeStatic(REFLECTOR_TYPE, getInstanceFieldMethod); + } + } +} + +static class StaticFieldExpr extends FieldExpr{ + final String className; + final String fieldName; + final static Method getStaticFieldMethod = Method.getMethod("Object getStaticField(String,String)"); + + + public StaticFieldExpr(String className, String fieldName){ + this.className = className; + this.fieldName = fieldName; + } + + public Object eval() throws Exception{ + return Reflector.getStaticField(className, fieldName); + } + + public void emit(C context, FnExpr fn, GeneratorAdapter gen){ + gen.push(className); + gen.push(fieldName); + gen.invokeStatic(REFLECTOR_TYPE, getStaticFieldMethod); + } +} + +static abstract class MethodExpr extends HostExpr{ + static void emitArgsAsArray(IPersistentArray args, FnExpr fn, GeneratorAdapter gen){ + gen.push(args.count()); + gen.newArray(OBJECT_TYPE); + for(int i = 0; i < args.count(); i++) + { + gen.dup(); + gen.push(i); + ((Expr) args.nth(i)).emit(C.EXPRESSION, fn, gen); + gen.arrayStore(OBJECT_TYPE); + } + } +} + +static class InstanceMethodExpr extends MethodExpr{ + final Expr target; + final String methodName; + final IPersistentArray args; + final static Method invokeInstanceMethodMethod = + Method.getMethod("Object invokeInstanceMethod(Object,String,Object[])"); + + + public InstanceMethodExpr(Expr target, String methodName, IPersistentArray args){ + this.args = args; + this.methodName = methodName; + this.target = target; + } + + public Object eval() throws Exception{ + Object targetval = target.eval(); + Object[] argvals = new Object[args.count()]; + for(int i = 0; i < args.count(); i++) + argvals[i] = ((Expr) args.nth(i)).eval(); + return Reflector.invokeInstanceMethod(targetval, methodName, argvals); + } + + public void emit(C context, FnExpr fn, GeneratorAdapter gen){ + target.emit(C.EXPRESSION, fn, gen); + gen.push(methodName); + emitArgsAsArray(args, fn, gen); + gen.invokeStatic(REFLECTOR_TYPE, invokeInstanceMethodMethod); + if(context == C.STATEMENT) + gen.pop(); + } +} + +static class StaticMethodExpr extends MethodExpr{ + final String className; + final String methodName; + final IPersistentArray args; + final static Method invokeStaticMethodMethod = + Method.getMethod("Object invokeStaticMethod(String,String,Object[])"); + + + public StaticMethodExpr(String className, String methodName, IPersistentArray args){ + this.className = className; + this.methodName = methodName; + this.args = args; + } + + public Object eval() throws Exception{ + Object[] argvals = new Object[args.count()]; + for(int i = 0; i < args.count(); i++) + argvals[i] = ((Expr) args.nth(i)).eval(); + return Reflector.invokeStaticMethod(className, methodName, argvals); + } + + public void emit(C context, FnExpr fn, GeneratorAdapter gen){ + gen.push(className); + gen.push(methodName); + emitArgsAsArray(args, fn, gen); + gen.invokeStatic(REFLECTOR_TYPE, invokeStaticMethodMethod); + if(context == C.STATEMENT) + gen.pop(); + } +} + + static class QuoteExpr extends LiteralExpr{ //stuff quoted vals in classloader at compile time, pull out at runtime //this won't work for static compilation... @@ -1108,6 +1290,8 @@ private static Expr analyzeSeq(C context, ISeq form, String name) throws Excepti return RecurExpr.parse(context, form); else if(op.equals(QUOTE)) return QuoteExpr.parse(context, form); + else if(op.equals(DOT)) + return HostExpr.parse(context, form); else return InvokeExpr.parse(context, form); } diff --git a/src/jvm/clojure/lang/RT.java b/src/jvm/clojure/lang/RT.java index 4a9648e5..7455c9ea 100644 --- a/src/jvm/clojure/lang/RT.java +++ b/src/jvm/clojure/lang/RT.java @@ -30,7 +30,7 @@ final static public Var CURRENT_MODULE = Var.intern(Symbol.create("clojure", "cu final static Var CURRENT_NS = Var.intern(Symbol.create("clojure", "current-ns"), "clojure-user"); //simple-symbol->var final static Var USES = Var.intern(Symbol.create("clojure", "uses"), PersistentHashMap.EMPTY); -//simple-symbol->fully-qualified-class-name-symbol +//simple-symbol->fully-qualified-class-name-string final static Var IMPORTS = Var.intern(Symbol.create("clojure", "imports"), PersistentHashMap.EMPTY); static public final Object[] EMPTY_ARRAY = new Object[]{}; |