summaryrefslogtreecommitdiff
path: root/src/jvm
diff options
context:
space:
mode:
authorRich Hickey <richhickey@gmail.com>2009-10-26 10:10:06 -0400
committerRich Hickey <richhickey@gmail.com>2009-10-26 10:10:06 -0400
commit6362e0f2522c4f7b2b2e626016b291d4832bb58a (patch)
tree590ead48e0bbed00fd7b3455b42da61ad560eb87 /src/jvm
parent2ebf995173c5c6ee89cc753f059d4ba044e6d073 (diff)
added compile stub class to support reflection based interop against class being defined
Diffstat (limited to 'src/jvm')
-rw-r--r--src/jvm/clojure/lang/Compiler.java117
1 files changed, 104 insertions, 13 deletions
diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java
index 26304b68..dde789eb 100644
--- a/src/jvm/clojure/lang/Compiler.java
+++ b/src/jvm/clojure/lang/Compiler.java
@@ -77,6 +77,7 @@ static final Keyword inlineAritiesKey = Keyword.intern(null, "inline-arities");
static final Keyword volatileKey = Keyword.intern(null, "volatile");
static final Keyword implementsKey = Keyword.intern(null, "implements");
+static final String COMPILE_STUB_PREFIX = "compile__stub";
static final Symbol NS = Symbol.create("ns");
static final Symbol IN_NS = Symbol.create("in-ns");
@@ -215,6 +216,10 @@ static final public Var NEXT_LOCAL_NUM = Var.create(0);
static final public Var RET_LOCAL_NUM = Var.create();
+static final public Var COMPILE_STUB_SYM = Var.create(null);
+static final public Var COMPILE_STUB_CLASS = Var.create(null);
+
+
public enum C{
STATEMENT, //value ignored
EXPRESSION, //value required
@@ -780,6 +785,8 @@ static public abstract class HostExpr implements Expr, MaybePrimitiveExpr{
Symbol sym = (Symbol) form;
if(sym.ns == null) //if ns-qualified can't be classname
{
+ if(Util.equals(sym,COMPILE_STUB_SYM.get()))
+ return (Class) COMPILE_STUB_CLASS.get();
if(sym.name.indexOf('.') > 0 || sym.name.charAt(0) == '[')
c = RT.classForName(sym.name);
else
@@ -891,8 +898,8 @@ static class InstanceFieldExpr extends FieldExpr implements AssignableExpr{
if(targetClass != null && field != null)
{
target.emit(C.EXPRESSION, objx, gen);
- gen.checkCast(Type.getType(targetClass));
- gen.getField(Type.getType(targetClass), fieldName, Type.getType(field.getType()));
+ gen.checkCast(getType(targetClass));
+ gen.getField(getType(targetClass), fieldName, Type.getType(field.getType()));
}
else
throw new UnsupportedOperationException("Unboxed emit of unknown member");
@@ -903,8 +910,8 @@ static class InstanceFieldExpr extends FieldExpr implements AssignableExpr{
if(targetClass != null && field != null)
{
target.emit(C.EXPRESSION, objx, gen);
- gen.checkCast(Type.getType(targetClass));
- gen.getField(Type.getType(targetClass), fieldName, Type.getType(field.getType()));
+ gen.checkCast(getType(targetClass));
+ gen.getField(getType(targetClass), fieldName, Type.getType(field.getType()));
//if(context != C.STATEMENT)
HostExpr.emitBoxReturn(objx, gen, field.getType());
if(context == C.STATEMENT)
@@ -3033,7 +3040,7 @@ static public class ObjExpr implements Expr{
}
else
{
- gen.push(cc.getName());
+ gen.push(destubClassName(cc.getName()));
gen.invokeStatic(Type.getType(Class.class), Method.getMethod("Class forName(String)"));
}
}
@@ -4537,6 +4544,20 @@ private static Expr analyzeSymbol(Symbol sym) throws Exception{
}
+static String destubClassName(String className){
+ //skip over prefix + '.' or '/'
+ if(className.startsWith(COMPILE_STUB_PREFIX))
+ return className.substring(COMPILE_STUB_PREFIX.length()+1);
+ return className;
+}
+
+static Type getType(Class c){
+ String descriptor = Type.getType(c).getDescriptor();
+ if(descriptor.startsWith("L"))
+ descriptor = "L" + destubClassName(descriptor.substring(1));
+ return Type.getType(descriptor);
+}
+
static Object resolve(Symbol sym, boolean allowPrivate) throws Exception{
return resolveIn(currentNS(), sym, allowPrivate);
}
@@ -4587,6 +4608,8 @@ static public Object resolveIn(Namespace n, Symbol sym, boolean allowPrivate) th
return RT.IN_NS_VAR;
else
{
+ if(Util.equals(sym,COMPILE_STUB_SYM.get()))
+ return COMPILE_STUB_CLASS.get();
Object o = n.getMapping(sym);
if(o == null)
{
@@ -5061,7 +5084,15 @@ static public class NewInstanceExpr extends ObjExpr{
Map[] mc = gatherMethods(superClass,RT.seq(interfaces));
Map overrideables = mc[0];
Map allmethods = mc[1];
+ String[] inames = interfaceNames(interfaces);
+ Symbol thistag = null;
+ Class stub = null;
+ if(ret.isDefclass())
+ {
+ stub = compileStub(slashname(superClass),ret, inames);
+ thistag = Symbol.intern(null,stub.getName());
+ }
try
{
Var.pushThreadBindings(
@@ -5070,7 +5101,10 @@ static public class NewInstanceExpr extends ObjExpr{
VARS, PersistentHashMap.EMPTY));
if(ret.isDefclass())
{
- Var.pushThreadBindings(RT.map(METHOD,null,LOCAL_ENV,ret.fields));
+ Var.pushThreadBindings(RT.map(METHOD,null,
+ LOCAL_ENV,ret.fields
+ ,COMPILE_STUB_SYM, Symbol.intern(null,stub.getSimpleName())
+ ,COMPILE_STUB_CLASS, stub));
}
//now (methodname [args] body)*
@@ -5078,7 +5112,7 @@ static public class NewInstanceExpr extends ObjExpr{
IPersistentCollection methods = null;
for(ISeq s = methodForms; s != null; s = RT.next(s))
{
- NewInstanceMethod m = NewInstanceMethod.parse(ret, (ISeq) RT.first(s),overrideables, allmethods);
+ NewInstanceMethod m = NewInstanceMethod.parse(ret, (ISeq) RT.first(s),thistag, overrideables, allmethods);
methods = RT.conj(methods, m);
}
@@ -5096,15 +5130,68 @@ static public class NewInstanceExpr extends ObjExpr{
Var.popThreadBindings();
}
- int icnt = interfaces.count();
- String[] inames = icnt > 0 ? new String[icnt] : null;
- for(int i=0;i<icnt;i++)
- inames[i] = slashname((Class) interfaces.nth(i));
ret.compile(slashname(superClass),inames,false);
ret.getCompiledClass();
return ret;
}
+ /***
+ * Current host interop uses reflection, which requires pre-existing classes
+ * Work around this by:
+ * Generate a stub class that has the same interfaces and fields as the class we are generating.
+ * Use it as a type hint for this, and bind the simple name of the class to this stub (in resolve etc)
+ * Unmunge the name (using a magic prefix) on any code gen for classes
+ */
+ static Class compileStub(String superName, NewInstanceExpr ret, String[] interfaceNames){
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+ ClassVisitor cv = cw;
+ cv.visit(V1_5, ACC_PUBLIC + ACC_SUPER, COMPILE_STUB_PREFIX + "/" + ret.internalName,
+ null,superName,interfaceNames);
+
+ //instance fields for closed-overs
+ for(ISeq s = RT.keys(ret.closes); s != null; s = s.next())
+ {
+ LocalBinding lb = (LocalBinding) s.first();
+ int access = ACC_PUBLIC + (ret.isVolatile(lb) ? ACC_VOLATILE : ACC_FINAL);
+ if(lb.getPrimitiveType() != null)
+ cv.visitField(access
+ , lb.name, Type.getType(lb.getPrimitiveType()).getDescriptor(),
+ null, null);
+ else
+ //todo - when closed-overs are fields, use more specific types here and in ctor and emitLocal?
+ cv.visitField(access
+ , lb.name, OBJECT_TYPE.getDescriptor(), null, null);
+ }
+
+ //ctor that takes closed-overs and does nothing
+ Method m = new Method("<init>", Type.VOID_TYPE, ret.ctorTypes());
+ GeneratorAdapter ctorgen = new GeneratorAdapter(ACC_PUBLIC,
+ m,
+ null,
+ null,
+ cv);
+ ctorgen.visitCode();
+ ctorgen.loadThis();
+ ctorgen.invokeConstructor(Type.getObjectType(superName), voidctor);
+ ctorgen.returnValue();
+ ctorgen.endMethod();
+
+ //end of class
+ cv.visitEnd();
+
+ byte[] bytecode = cw.toByteArray();
+ DynamicClassLoader loader = (DynamicClassLoader) LOADER.deref();
+ return loader.defineClass(COMPILE_STUB_PREFIX + "." + ret.name, bytecode);
+ }
+
+ static String[] interfaceNames(IPersistentVector interfaces){
+ int icnt = interfaces.count();
+ String[] inames = icnt > 0 ? new String[icnt] : null;
+ for(int i=0;i<icnt;i++)
+ inames[i] = slashname((Class) interfaces.nth(i));
+ return inames;
+ }
+
static String slashname(Class c){
return c.getName().replace('.', '/');
@@ -5194,7 +5281,8 @@ public static class NewInstanceMethod extends ObjMethod{
return RT.vector(name,RT.seq(paramTypes));
}
- static NewInstanceMethod parse(ObjExpr objx, ISeq form, Map overrideables, Map allmethods) throws Exception{
+ static NewInstanceMethod parse(ObjExpr objx, ISeq form, Symbol thistag,
+ Map overrideables, Map allmethods) throws Exception{
//(methodname [args] body...)
NewInstanceMethod method = new NewInstanceMethod(objx, (ObjMethod) METHOD.deref());
Symbol name = (Symbol)RT.first(form);
@@ -5213,7 +5301,7 @@ public static class NewInstanceMethod extends ObjMethod{
//register 'this' as local 0
registerLocal(Symbol.intern(objx.thisName != null ? objx.thisName : "obj__" + RT.nextID()),
- null, null,false);
+ thistag, null,false);
PersistentVector argLocals = PersistentVector.EMPTY;
method.retClass = tagClass(tagOf(name));
@@ -5277,6 +5365,9 @@ public static class NewInstanceMethod extends ObjMethod{
else if(findMethodsWithName(name.name,allmethods).size()>0)
throw new IllegalArgumentException("Can't override/overload method: " + name.name);
//todo
+ else
+ throw new IllegalArgumentException("Can't define method not in interfaces: " + name.name);
+
//else
//validate unque name+arity among additional methods