summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRich Hickey <richhickey@gmail.com>2009-11-03 11:16:25 -0500
committerRich Hickey <richhickey@gmail.com>2009-11-03 11:16:25 -0500
commita88ce1882218c83e410d430fab63fbc9331aaba0 (patch)
treef703170d256e6713518be992f84cd45d0380e522
parent679c79508713acddca1e18d1e28dafae885e5096 (diff)
first cut at keyword callsites
-rw-r--r--src/jvm/clojure/lang/Compiler.java263
-rw-r--r--src/jvm/clojure/lang/IKeywordLookup.java17
-rw-r--r--src/jvm/clojure/lang/ILookupHost.java19
-rw-r--r--src/jvm/clojure/lang/ILookupSite.java19
-rw-r--r--src/jvm/clojure/lang/ILookupThunk.java19
-rw-r--r--src/jvm/clojure/lang/KeywordLookupSite.java61
6 files changed, 396 insertions, 2 deletions
diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java
index 3c2e6d97..c734723e 100644
--- a/src/jvm/clojure/lang/Compiler.java
+++ b/src/jvm/clojure/lang/Compiler.java
@@ -177,6 +177,9 @@ static final public Var LOOP_LABEL = Var.create();
//vector<object>
static final public Var CONSTANTS = Var.create();
+//vector<keyword>
+static final public Var KEYWORD_CALLSITES = Var.create();
+
//keyword->constid
static final public Var KEYWORDS = Var.create();
@@ -225,6 +228,7 @@ 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
@@ -2484,6 +2488,7 @@ static class KeywordInvokeExpr implements Expr{
public final Object tag;
public final Expr target;
public final int line;
+ public final int siteIndex;
public final String source;
static Type ILOOKUP_TYPE = Type.getType(ILookup.class);
@@ -2493,6 +2498,7 @@ static class KeywordInvokeExpr implements Expr{
this.target = target;
this.line = line;
this.tag = tag;
+ this.siteIndex = registerKeywordCallsite(kw.k);
}
public Object eval() throws Exception{
@@ -2510,6 +2516,54 @@ static class KeywordInvokeExpr implements Expr{
}
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
+ Label endLabel = gen.newLabel();
+ Label faultLabel = gen.newLabel();
+
+ gen.visitLineNumber(line, gen.mark());
+ target.emit(C.EXPRESSION, objx, gen);
+ gen.dup();
+ gen.getStatic(objx.objtype, objx.thunkNameStatic(siteIndex),ObjExpr.ILOOKUP_THUNK_TYPE);
+ gen.swap();
+ gen.getStatic(objx.objtype, objx.siteNameStatic(siteIndex),ObjExpr.KEYWORD_LOOKUPSITE_TYPE);
+/// gen.loadThis();
+ gen.invokeInterface(ObjExpr.ILOOKUP_THUNK_TYPE,
+ Method.getMethod("Object get(Object,clojure.lang.ILookupSite)"));
+// gen.invokeInterface(ObjExpr.ILOOKUP_THUNK_TYPE,
+// Method.getMethod("Object get(Object,clojure.lang.ILookupSite,clojure.lang.ILookupHost)"));
+ gen.dup();
+ gen.getStatic(objx.objtype, objx.siteNameStatic(siteIndex),ObjExpr.KEYWORD_LOOKUPSITE_TYPE);
+ gen.visitJumpInsn(IF_ACMPEQ, faultLabel);
+ gen.swap();
+ gen.pop();
+ gen.goTo(endLabel);
+
+ gen.mark(faultLabel);
+ gen.swap();
+ gen.loadThis();
+ gen.invokeInterface(ObjExpr.ILOOKUP_SITE_TYPE,
+ Method.getMethod("Object fault(Object, clojure.lang.ILookupHost)"));
+
+ gen.mark(endLabel);
+ if(context == C.STATEMENT)
+ gen.pop();
+ }
+
+ public void emitInstance(C context, ObjExpr objx, GeneratorAdapter gen){
+ gen.visitLineNumber(line, gen.mark());
+ gen.loadThis();
+ gen.getField(objx.objtype, objx.thunkName(siteIndex),ObjExpr.ILOOKUP_THUNK_TYPE);
+ target.emit(C.EXPRESSION, objx, gen);
+ gen.loadThis();
+ gen.getField(objx.objtype, objx.siteName(siteIndex),ObjExpr.ILOOKUP_SITE_TYPE);
+ gen.loadThis();
+ gen.checkCast(Type.getType(ILookupHost.class));
+ gen.invokeInterface(ObjExpr.ILOOKUP_THUNK_TYPE,
+ Method.getMethod("Object get(Object,clojure.lang.ILookupSite,clojure.lang.ILookupHost)"));
+ if(context == C.STATEMENT)
+ gen.pop();
+ }
+
+ public void emitNormal(C context, ObjExpr objx, GeneratorAdapter gen){
Label slowLabel = gen.newLabel();
Label endLabel = gen.newLabel();
@@ -2761,7 +2815,8 @@ static public class FnExpr extends ObjExpr{
Var.pushThreadBindings(
RT.map(CONSTANTS, PersistentVector.EMPTY,
KEYWORDS, PersistentHashMap.EMPTY,
- VARS, PersistentHashMap.EMPTY));
+ VARS, PersistentHashMap.EMPTY,
+ KEYWORD_CALLSITES, PersistentVector.EMPTY));
//arglist might be preceded by symbol naming this fn
if(RT.second(form) instanceof Symbol)
@@ -2812,6 +2867,8 @@ static public class FnExpr extends ObjExpr{
fn.keywords = (IPersistentMap) KEYWORDS.deref();
fn.vars = (IPersistentMap) VARS.deref();
fn.constants = (PersistentVector) CONSTANTS.deref();
+ fn.keywordCallsites = (IPersistentVector) KEYWORD_CALLSITES.deref();
+
fn.constantsID = RT.nextID();
// DynamicClassLoader loader = (DynamicClassLoader) LOADER.get();
// loader.registerConstants(fn.constantsID, fn.constants.toArray());
@@ -2864,6 +2921,7 @@ static public class ObjExpr implements Expr{
PersistentVector constants;
int constantsID;
+ IPersistentVector keywordCallsites;
final static Method voidctor = Method.getMethod("void <init>()");
@@ -2925,6 +2983,11 @@ static public class ObjExpr implements Expr{
final static Method getClassLoaderMethod = Method.getMethod("ClassLoader getClassLoader()");
final static Method getConstantsMethod = Method.getMethod("Object[] getConstants(int)");
final static Method readStringMethod = Method.getMethod("Object readString(String)");
+
+ final static Type ILOOKUP_SITE_TYPE = Type.getType(ILookupSite.class);
+ final static Type ILOOKUP_THUNK_TYPE = Type.getType(ILookupThunk.class);
+ final static Type KEYWORD_LOOKUPSITE_TYPE = Type.getType(KeywordLookupSite.class);
+
private DynamicClassLoader loader;
private byte[] bytecode;
@@ -2962,6 +3025,18 @@ static public class ObjExpr implements Expr{
//with name current_ns.defname[$letname]+
//anonymous fns get names fn__id
//derived from AFn/RestFn
+ if(keywordCallsites.count() > 0)
+ {
+ if(interfaceNames == null)
+ interfaceNames = new String[]{"clojure/lang/ILookupHost"};
+ else
+ {
+ String[] inames = new String[interfaceNames.length + 1];
+ System.arraycopy(interfaceNames,0,inames,0,interfaceNames.length);
+ inames[interfaceNames.length] = "clojure/lang/ILookupHost";
+ interfaceNames = inames;
+ }
+ }
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
// ClassWriter cw = new ClassWriter(0);
ClassVisitor cv = cw;
@@ -3002,6 +3077,16 @@ static public class ObjExpr implements Expr{
null, null);
}
+ //static fields for lookup sites
+ for(int i = 0; i < keywordCallsites.count(); i++)
+ {
+ cv.visitField(ACC_FINAL
+ + ACC_STATIC, siteNameStatic(i), KEYWORD_LOOKUPSITE_TYPE.getDescriptor(),
+ null, null);
+ cv.visitField(ACC_STATIC, thunkNameStatic(i), ILOOKUP_THUNK_TYPE.getDescriptor(),
+ null, null);
+ }
+
//static init for constants, keywords and vars
GeneratorAdapter clinitgen = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC,
Method.getMethod("void <clinit> ()"),
@@ -3016,6 +3101,9 @@ static public class ObjExpr implements Expr{
emitConstants(clinitgen);
}
+ if(keywordCallsites.count() > 0)
+ emitKeywordCallsites(clinitgen);
+
clinitgen.returnValue();
clinitgen.endMethod();
@@ -3047,6 +3135,14 @@ static public class ObjExpr implements Expr{
, lb.name, OBJECT_TYPE.getDescriptor(), null, null);
}
}
+
+ //instance fields for callsites and thunks
+ for(int i=0;i<keywordCallsites.count();i++)
+ {
+// cv.visitField(ACC_FINAL, siteName(i), ILOOKUP_SITE_TYPE.getDescriptor(), null, null);
+// cv.visitField(ACC_PUBLIC, thunkName(i), ILOOKUP_THUNK_TYPE.getDescriptor(), null, null);
+ }
+
//ctor that takes closed-overs and inits base + fields
Method m = new Method("<init>", Type.VOID_TYPE, ctorTypes());
GeneratorAdapter ctorgen = new GeneratorAdapter(ACC_PUBLIC,
@@ -3088,13 +3184,61 @@ static public class ObjExpr implements Expr{
ctorgen.putField(objtype, lb.name, OBJECT_TYPE);
}
}
+
+ //copy static sites into instance site and thunk
+ for(int i=0;i<keywordCallsites.count();i++)
+ {
+// ctorgen.loadThis();
+// ctorgen.getStatic(objtype,siteNameStatic(i),KEYWORD_LOOKUPSITE_TYPE);
+// ctorgen.putField(objtype, siteName(i),ILOOKUP_SITE_TYPE);
+// ctorgen.loadThis();
+// ctorgen.getStatic(objtype,siteNameStatic(i),KEYWORD_LOOKUPSITE_TYPE);
+// ctorgen.putField(objtype, thunkName(i),ILOOKUP_THUNK_TYPE);
+ }
+
ctorgen.visitLabel(end);
ctorgen.returnValue();
+
ctorgen.endMethod();
emitMethods(cv);
+ if(keywordCallsites.count() > 0)
+ {
+ Method meth = Method.getMethod("void swapThunk(int,clojure.lang.ILookupThunk)");
+
+ GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC,
+ meth,
+ null,
+ null,
+ cv);
+ gen.visitCode();
+ Label endLabel = gen.newLabel();
+
+ Label[] labels = new Label[keywordCallsites.count()];
+ for(int i = 0; i < keywordCallsites.count();i++)
+ {
+ labels[i] = gen.newLabel();
+ }
+ gen.loadArg(0);
+ gen.visitTableSwitchInsn(0,keywordCallsites.count()-1,endLabel,labels);
+
+ for(int i = 0; i < keywordCallsites.count();i++)
+ {
+ gen.mark(labels[i]);
+// gen.loadThis();
+ gen.loadArg(1);
+ gen.putStatic(objtype, thunkNameStatic(i),ILOOKUP_THUNK_TYPE);
+ gen.goTo(endLabel);
+ }
+
+ gen.mark(endLabel);
+
+ gen.returnValue();
+ gen.endMethod();
+ }
+
//end of class
cv.visitEnd();
@@ -3105,6 +3249,22 @@ static public class ObjExpr implements Expr{
// getCompiledClass();
}
+ private void emitKeywordCallsites(GeneratorAdapter clinitgen){
+ for(int i=0;i<keywordCallsites.count();i++)
+ {
+ Keyword k = (Keyword) keywordCallsites.nth(i);
+ clinitgen.newInstance(KEYWORD_LOOKUPSITE_TYPE);
+ clinitgen.dup();
+ clinitgen.push(i);
+ emitValue(k,clinitgen);
+ clinitgen.invokeConstructor(KEYWORD_LOOKUPSITE_TYPE,
+ Method.getMethod("void <init>(int,clojure.lang.Keyword)"));
+ clinitgen.dup();
+ clinitgen.putStatic(objtype, siteNameStatic(i), KEYWORD_LOOKUPSITE_TYPE);
+ clinitgen.putStatic(objtype, thunkNameStatic(i), ILOOKUP_THUNK_TYPE);
+ }
+ }
+
protected void emitMethods(ClassVisitor gen){
}
@@ -3478,6 +3638,22 @@ static public class ObjExpr implements Expr{
return CONST_PREFIX + id;
}
+ String siteName(int n){
+ return "__site__" + n;
+ }
+
+ String siteNameStatic(int n){
+ return siteName(n) + "__";
+ }
+
+ String thunkName(int n){
+ return "__thunk__" + n;
+ }
+
+ String thunkNameStatic(int n){
+ return thunkName(n) + "__";
+ }
+
Type constantType(int id){
Object o = constants.nth(id);
Class c = o.getClass();
@@ -4635,6 +4811,17 @@ private static KeywordExpr registerKeyword(Keyword keyword){
// return ke;
}
+private static int registerKeywordCallsite(Keyword keyword){
+ if(!KEYWORD_CALLSITES.isBound())
+ throw new IllegalAccessError();
+
+ IPersistentVector keywordCallsites = (IPersistentVector) KEYWORD_CALLSITES.deref();
+
+ keywordCallsites = keywordCallsites.cons(keyword);
+ KEYWORD_CALLSITES.set(keywordCallsites);
+ return keywordCallsites.count()-1;
+}
+
private static Expr analyzeSymbol(Symbol sym) throws Exception{
Symbol tag = tagOf(sym);
if(sym.ns == null) //ns-qualified syms are always Vars
@@ -4976,6 +5163,10 @@ public static void pushNS(){
Symbol.create("*ns*")), null));
}
+public static ILookupThunk getLookupThunk(Object target, Keyword k){
+ return null; //To change body of created methods use File | Settings | File Templates.
+}
+
static void compile1(GeneratorAdapter gen, ObjExpr objx, Object form) throws Exception{
Integer line = (Integer) LINE.deref();
if(RT.meta(form) != null && RT.meta(form).containsKey(RT.LINE_KEY))
@@ -5207,6 +5398,8 @@ static public class NewInstanceExpr extends ObjExpr{
fmap = fmap.assoc(sym, lb);
closesvec[i*2] = lb;
closesvec[i*2 + 1] = lb;
+ if(!sym.name.startsWith("__"))
+ compileLookupThunk(ret, sym);
}
//todo - inject __meta et al into closes - when?
@@ -5242,7 +5435,8 @@ static public class NewInstanceExpr extends ObjExpr{
Var.pushThreadBindings(
RT.map(CONSTANTS, PersistentVector.EMPTY,
KEYWORDS, PersistentHashMap.EMPTY,
- VARS, PersistentHashMap.EMPTY
+ VARS, PersistentHashMap.EMPTY,
+ KEYWORD_CALLSITES, PersistentVector.EMPTY
));
if(ret.isDefclass())
{
@@ -5267,6 +5461,7 @@ static public class NewInstanceExpr extends ObjExpr{
ret.vars = (IPersistentMap) VARS.deref();
ret.constants = (PersistentVector) CONSTANTS.deref();
ret.constantsID = RT.nextID();
+ ret.keywordCallsites = (IPersistentVector) KEYWORD_CALLSITES.deref();
}
finally
{
@@ -5329,6 +5524,70 @@ static public class NewInstanceExpr extends ObjExpr{
return loader.defineClass(COMPILE_STUB_PREFIX + "." + ret.name, bytecode);
}
+ static Class compileLookupThunk(NewInstanceExpr ret, Symbol fld) throws Exception{
+ //String superName, NewInstanceExpr ret, String[] interfaceNames){
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+ ClassVisitor cv = cw;
+ String iname = ret.internalName + "$__lookup__" + fld.name;
+ String cname = ret.name + "$__lookup__" + fld.name;
+ Class fclass = tagClass(tagOf(fld));
+ Type ftype = Type.getType(fclass);
+ cv.visit(V1_5, ACC_PUBLIC + ACC_SUPER + ACC_FINAL, iname,
+ null,"java/lang/Object",new String[]{"clojure/lang/ILookupThunk"});
+
+ GeneratorAdapter ctorgen = new GeneratorAdapter(ACC_PUBLIC,
+ voidctor,
+ null,
+ null,
+ cv);
+ ctorgen.visitCode();
+ ctorgen.loadThis();
+ ctorgen.invokeConstructor(OBJECT_TYPE, voidctor);
+ ctorgen.returnValue();
+ ctorgen.endMethod();
+
+ Method meth = Method.getMethod("Object get(Object, clojure.lang.ILookupSite)");
+// Method meth = Method.getMethod("Object get(Object, clojure.lang.ILookupSite, clojure.lang.ILookupHost)");
+
+ GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC,
+ meth,
+ null,
+ null,
+ cv);
+ gen.visitCode();
+ Label faultLabel = gen.newLabel();
+ Label endLabel = gen.newLabel();
+
+ gen.loadArg(0);
+ gen.dup();
+ gen.instanceOf(ret.objtype);
+ gen.ifZCmp(GeneratorAdapter.EQ, faultLabel);
+ gen.checkCast(ret.objtype);
+ gen.getField(ret.objtype, fld.name, ftype);
+// gen.getField(ret.objtype, fld.name, ftype);
+ HostExpr.emitBoxReturn(ret,gen,fclass);
+ gen.goTo(endLabel);
+
+ gen.mark(faultLabel);
+ gen.pop();
+ gen.loadArg(1);
+// gen.swap();
+// gen.loadArg(2);
+// gen.invokeInterface(ObjExpr.ILOOKUP_SITE_TYPE,
+// Method.getMethod("Object fault(Object, clojure.lang.ILookupHost)"));
+
+ gen.mark(endLabel);
+ gen.returnValue();
+ gen.endMethod();
+
+ //end of class
+ cv.visitEnd();
+
+ byte[] bytecode = cw.toByteArray();
+ DynamicClassLoader loader = (DynamicClassLoader) LOADER.deref();
+ return loader.defineClass(cname, bytecode);
+ }
+
static String[] interfaceNames(IPersistentVector interfaces){
int icnt = interfaces.count();
String[] inames = icnt > 0 ? new String[icnt] : null;
diff --git a/src/jvm/clojure/lang/IKeywordLookup.java b/src/jvm/clojure/lang/IKeywordLookup.java
new file mode 100644
index 00000000..979d550a
--- /dev/null
+++ b/src/jvm/clojure/lang/IKeywordLookup.java
@@ -0,0 +1,17 @@
+/**
+ * Copyright (c) Rich Hickey. All rights reserved.
+ * The use and distribution terms for this software are covered by the
+ * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+ * which can be found in the file epl-v10.html at the root of this distribution.
+ * By using this software in any fashion, you are agreeing to be bound by
+ * the terms of this license.
+ * You must not remove this notice, or any other, from this software.
+ **/
+
+/* rich Oct 31, 2009 */
+
+package clojure.lang;
+
+public interface IKeywordLookup{
+ILookupThunk getLookupThunk(Keyword k);
+}
diff --git a/src/jvm/clojure/lang/ILookupHost.java b/src/jvm/clojure/lang/ILookupHost.java
new file mode 100644
index 00000000..ede82876
--- /dev/null
+++ b/src/jvm/clojure/lang/ILookupHost.java
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) Rich Hickey. All rights reserved.
+ * The use and distribution terms for this software are covered by the
+ * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+ * which can be found in the file epl-v10.html at the root of this distribution.
+ * By using this software in any fashion, you are agreeing to be bound by
+ * the terms of this license.
+ * You must not remove this notice, or any other, from this software.
+ **/
+
+/* rich Nov 2, 2009 */
+
+package clojure.lang;
+
+public interface ILookupHost{
+
+void swapThunk(int n, ILookupThunk thunk);
+
+}
diff --git a/src/jvm/clojure/lang/ILookupSite.java b/src/jvm/clojure/lang/ILookupSite.java
new file mode 100644
index 00000000..0609974e
--- /dev/null
+++ b/src/jvm/clojure/lang/ILookupSite.java
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) Rich Hickey. All rights reserved.
+ * The use and distribution terms for this software are covered by the
+ * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+ * which can be found in the file epl-v10.html at the root of this distribution.
+ * By using this software in any fashion, you are agreeing to be bound by
+ * the terms of this license.
+ * You must not remove this notice, or any other, from this software.
+ **/
+
+/* rich Nov 2, 2009 */
+
+package clojure.lang;
+
+public interface ILookupSite{
+
+Object fault(Object target, ILookupHost host);
+
+}
diff --git a/src/jvm/clojure/lang/ILookupThunk.java b/src/jvm/clojure/lang/ILookupThunk.java
new file mode 100644
index 00000000..49f68aa7
--- /dev/null
+++ b/src/jvm/clojure/lang/ILookupThunk.java
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) Rich Hickey. All rights reserved.
+ * The use and distribution terms for this software are covered by the
+ * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+ * which can be found in the file epl-v10.html at the root of this distribution.
+ * By using this software in any fashion, you are agreeing to be bound by
+ * the terms of this license.
+ * You must not remove this notice, or any other, from this software.
+ **/
+
+/* rich Nov 2, 2009 */
+
+package clojure.lang;
+
+public interface ILookupThunk{
+
+Object get(Object target, ILookupSite site);
+
+}
diff --git a/src/jvm/clojure/lang/KeywordLookupSite.java b/src/jvm/clojure/lang/KeywordLookupSite.java
new file mode 100644
index 00000000..de762a46
--- /dev/null
+++ b/src/jvm/clojure/lang/KeywordLookupSite.java
@@ -0,0 +1,61 @@
+/**
+ * Copyright (c) Rich Hickey. All rights reserved.
+ * The use and distribution terms for this software are covered by the
+ * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+ * which can be found in the file epl-v10.html at the root of this distribution.
+ * By using this software in any fashion, you are agreeing to be bound by
+ * the terms of this license.
+ * You must not remove this notice, or any other, from this software.
+ **/
+
+/* rich Nov 2, 2009 */
+
+package clojure.lang;
+
+public final class KeywordLookupSite implements ILookupSite, ILookupThunk{
+
+final int n;
+final Keyword k;
+
+public KeywordLookupSite(int n, Keyword k){
+ this.n = n;
+ this.k = k;
+}
+
+public Object fault(Object target, ILookupHost host){
+ if(target instanceof IKeywordLookup)
+ {
+ return install(target, host);
+ }
+ else if(target instanceof ILookup)
+ {
+// final Class c = target.getClass();
+ host.swapThunk(n,new ILookupThunk(){
+
+ public Object get(Object target, ILookupSite site){
+ if(target instanceof ILookup)
+ return ((ILookup)target).valAt(k);
+ return RT.get(target, k);
+// if(target != null && target.getClass() == c)
+// return ((ILookup) target).valAt(k);
+// return site;
+ }
+ });
+ return ((ILookup) target).valAt(k);
+ }
+ host.swapThunk(n,this);
+ return RT.get(target, k);
+}
+
+public Object get(Object target, ILookupSite site){
+ if(target instanceof IKeywordLookup || target instanceof ILookup)
+ return site;
+ return RT.get(target,k);
+}
+
+private Object install(Object target, ILookupHost host){
+ ILookupThunk t = ((IKeywordLookup)target).getLookupThunk(k);
+ host.swapThunk(n,t);
+ return t.get(target, this);
+}
+}