diff options
author | Rich Hickey <richhickey@gmail.com> | 2009-01-01 20:52:00 +0000 |
---|---|---|
committer | Rich Hickey <richhickey@gmail.com> | 2009-01-01 20:52:00 +0000 |
commit | 8a6c52556d92ab6ab369c7bf2a8c956add582de9 (patch) | |
tree | 3058f6da1fb15c973ba165593b3b7edb2aa4f61f /src/jvm/clojure | |
parent | 96757ae6fe6651f2753f7495a6ff476ed72c1798 (diff) |
Added uniform metadata handling for atoms/refs/agents/vars/namespaces
Note - breaking change for agent/ref when supplying validator - validator must be passed using :validator option
Added :validator and :meta options to agent/ref/atom
Added alter-meta! and reset-meta! for reference types
renamed set-validator to set-validator!
Validators now can simply return false, or throw
Refactoring, added IMeta, IReference
Switched to longs for Ref ids
Diffstat (limited to 'src/jvm/clojure')
-rw-r--r-- | src/jvm/clojure/lang/ARef.java | 60 | ||||
-rw-r--r-- | src/jvm/clojure/lang/AReference.java | 40 | ||||
-rw-r--r-- | src/jvm/clojure/lang/Agent.java | 35 | ||||
-rw-r--r-- | src/jvm/clojure/lang/Atom.java | 54 | ||||
-rw-r--r-- | src/jvm/clojure/lang/Box.java | 2 | ||||
-rw-r--r-- | src/jvm/clojure/lang/Compiler.java | 21 | ||||
-rw-r--r-- | src/jvm/clojure/lang/IMeta.java | 17 | ||||
-rw-r--r-- | src/jvm/clojure/lang/IObj.java | 6 | ||||
-rw-r--r-- | src/jvm/clojure/lang/IReference.java | 18 | ||||
-rw-r--r-- | src/jvm/clojure/lang/LispReader.java | 10 | ||||
-rw-r--r-- | src/jvm/clojure/lang/Namespace.java | 2 | ||||
-rw-r--r-- | src/jvm/clojure/lang/RT.java | 9 | ||||
-rw-r--r-- | src/jvm/clojure/lang/Ref.java | 63 | ||||
-rw-r--r-- | src/jvm/clojure/lang/Settable.java | 18 | ||||
-rw-r--r-- | src/jvm/clojure/lang/Var.java | 77 |
15 files changed, 312 insertions, 120 deletions
diff --git a/src/jvm/clojure/lang/ARef.java b/src/jvm/clojure/lang/ARef.java new file mode 100644 index 00000000..ee04d2e4 --- /dev/null +++ b/src/jvm/clojure/lang/ARef.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) Rich Hickey. All rights reserved. + * The use and distribution terms for this software are covered by the + * Common Public License 1.0 (http://opensource.org/licenses/cpl.php) + * which can be found in the file CPL.TXT 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 Jan 1, 2009 */ + +package clojure.lang; + +public abstract class ARef extends AReference implements IRef { + private volatile IFn validator = null; + + public ARef() { + super(); + } + + public ARef(IPersistentMap meta) { + super(meta); + } + + void validate(IFn vf, Object val){ + try{ + if(vf != null && !RT.booleanCast(vf.invoke(val))) + throw new IllegalStateException("Invalid reference state"); + } + catch(RuntimeException re) + { + throw re; + } + catch(Exception e) + { + throw new IllegalStateException("Invalid reference state", e); + } + } + + void validate(Object val){ + validate(validator,val); + } + + public void setValidator(IFn vf){ + try + { + validate(vf,get()); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + validator = vf; + } + + public IFn getValidator(){ + return validator; + } +} diff --git a/src/jvm/clojure/lang/AReference.java b/src/jvm/clojure/lang/AReference.java new file mode 100644 index 00000000..ab7ae8ab --- /dev/null +++ b/src/jvm/clojure/lang/AReference.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) Rich Hickey. All rights reserved. + * The use and distribution terms for this software are covered by the + * Common Public License 1.0 (http://opensource.org/licenses/cpl.php) + * which can be found in the file CPL.TXT 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 Dec 31, 2008 */ + +package clojure.lang; + +public class AReference implements IReference { + private IPersistentMap _meta; + + public AReference() { + this(null); + } + + public AReference(IPersistentMap meta) { + _meta = meta; + } + + synchronized public IPersistentMap meta() { + return _meta; + } + + synchronized public IPersistentMap alterMeta(IFn alter, ISeq args) throws Exception { + _meta = (IPersistentMap) alter.applyTo(new Cons(_meta, args)); + return _meta; + } + + synchronized public IPersistentMap resetMeta(IPersistentMap m) { + _meta = m; + return m; + } + +} diff --git a/src/jvm/clojure/lang/Agent.java b/src/jvm/clojure/lang/Agent.java index e4500174..c2d8613d 100644 --- a/src/jvm/clojure/lang/Agent.java +++ b/src/jvm/clojure/lang/Agent.java @@ -16,10 +16,9 @@ import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicReference; import java.util.Map; -public class Agent implements IRef{ +public class Agent extends ARef { volatile Object state; -volatile IFn validator = null; -AtomicReference<IPersistentStack> q = new AtomicReference(PersistentQueue.EMPTY); + AtomicReference<IPersistentStack> q = new AtomicReference(PersistentQueue.EMPTY); AtomicReference<IPersistentMap> watchers = new AtomicReference(PersistentHashMap.EMPTY); volatile ISeq errors = null; @@ -116,29 +115,18 @@ public Agent(Object state) throws Exception{ this(state,null); } -public Agent(Object state, IFn validator) throws Exception{ - this.validator = validator; - setState(state); +public Agent(Object state, IPersistentMap meta) throws Exception { + super(meta); + setState(state); } boolean setState(Object newState) throws Exception{ - validate(getValidator(),newState); + validate(newState); boolean ret = state != newState; state = newState; return ret; } -void validate(IFn vf, Object val){ - try{ - if(vf != null) - vf.invoke(val); - } - catch(Exception e) - { - throw new IllegalStateException("Invalid agent state", e); - } -} - public Object get() throws Exception{ if(errors != null) { @@ -147,16 +135,7 @@ public Object get() throws Exception{ return state; } -public void setValidator(IFn vf){ - validate(vf,state); - validator = vf; -} - -public IFn getValidator(){ - return validator; -} - -public ISeq getErrors(){ + public ISeq getErrors(){ return errors; } diff --git a/src/jvm/clojure/lang/Atom.java b/src/jvm/clojure/lang/Atom.java new file mode 100644 index 00000000..7834047a --- /dev/null +++ b/src/jvm/clojure/lang/Atom.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) Rich Hickey. All rights reserved. + * The use and distribution terms for this software are covered by the + * Common Public License 1.0 (http://opensource.org/licenses/cpl.php) + * which can be found in the file CPL.TXT 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 Jan 1, 2009 */ + +package clojure.lang; + +import java.util.concurrent.atomic.AtomicReference; + +public class Atom extends ARef{ + final AtomicReference state; + + public Atom(Object state) { + this.state = new AtomicReference(state); + } + + public Atom(Object state, IPersistentMap meta) { + super(meta); + this.state = new AtomicReference(state); + } + + public Object get() { + return state.get(); + } + + public Object swap(IFn f, ISeq args) throws Exception { + for(;;) + { + Object v = get(); + Object newv = f.applyTo(new Cons(v, args)); + validate(newv); + if(state.compareAndSet(v,newv)) + return newv; + } + } + + public boolean compareAndSet(Object oldv, Object newv){ + validate(newv); + return state.compareAndSet(oldv, newv); + } + + public Object reset(Object newval){ + validate(newval); + state.set(newval); + return newval; + } +} diff --git a/src/jvm/clojure/lang/Box.java b/src/jvm/clojure/lang/Box.java index 61ec17ff..95161cce 100644 --- a/src/jvm/clojure/lang/Box.java +++ b/src/jvm/clojure/lang/Box.java @@ -14,7 +14,7 @@ package clojure.lang; public class Box{ -public volatile Object val; +public Object val; public Box(Object val){ this.val = val; diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index a5aba11e..c1802016 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -4047,7 +4047,7 @@ public static Object macroexpand1(Object x) throws Exception{ { try { - Var.pushThreadBindings(RT.map(RT.MACRO_META, ((IObj) form).meta())); + Var.pushThreadBindings(RT.map(RT.MACRO_META, RT.meta(form))); return v.applyTo(form.rest()); } finally @@ -4437,19 +4437,12 @@ static LocalBinding referenceLocal(Symbol sym) throws Exception{ } private static Symbol tagOf(Object o){ - if(o instanceof IObj) - { - IObj obj = (IObj) o; - if(obj.meta() != null) - { - Object tag = obj.meta().valAt(RT.TAG_KEY); - if(tag instanceof Symbol) - return (Symbol) tag; - else if(tag instanceof String) - return Symbol.intern(null, (String) tag); - } - } - return null; + Object tag = RT.get(RT.meta(o), RT.TAG_KEY); + if (tag instanceof Symbol) + return (Symbol) tag; + else if (tag instanceof String) + return Symbol.intern(null, (String) tag); + return null; } public static Object loadFile(String file) throws Exception{ diff --git a/src/jvm/clojure/lang/IMeta.java b/src/jvm/clojure/lang/IMeta.java new file mode 100644 index 00000000..8c4d231a --- /dev/null +++ b/src/jvm/clojure/lang/IMeta.java @@ -0,0 +1,17 @@ +/** + * Copyright (c) Rich Hickey. All rights reserved. + * The use and distribution terms for this software are covered by the + * Common Public License 1.0 (http://opensource.org/licenses/cpl.php) + * which can be found in the file CPL.TXT 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 Dec 31, 2008 */ + +package clojure.lang; + +public interface IMeta { + IPersistentMap meta(); +} diff --git a/src/jvm/clojure/lang/IObj.java b/src/jvm/clojure/lang/IObj.java index c3be04ca..35dbbd30 100644 --- a/src/jvm/clojure/lang/IObj.java +++ b/src/jvm/clojure/lang/IObj.java @@ -11,10 +11,8 @@ package clojure.lang;
-public interface IObj{
+public interface IObj extends IMeta {
-public IPersistentMap meta();
-
-public IObj withMeta(IPersistentMap meta);
+ public IObj withMeta(IPersistentMap meta);
}
diff --git a/src/jvm/clojure/lang/IReference.java b/src/jvm/clojure/lang/IReference.java new file mode 100644 index 00000000..fc57b1f7 --- /dev/null +++ b/src/jvm/clojure/lang/IReference.java @@ -0,0 +1,18 @@ +/** + * Copyright (c) Rich Hickey. All rights reserved. + * The use and distribution terms for this software are covered by the + * Common Public License 1.0 (http://opensource.org/licenses/cpl.php) + * which can be found in the file CPL.TXT 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 Dec 31, 2008 */ + +package clojure.lang; + +public interface IReference extends IMeta { + IPersistentMap alterMeta(IFn alter, ISeq args) throws Exception; + IPersistentMap resetMeta(IPersistentMap m); +} diff --git a/src/jvm/clojure/lang/LispReader.java b/src/jvm/clojure/lang/LispReader.java index d1e66000..d7677f7e 100644 --- a/src/jvm/clojure/lang/LispReader.java +++ b/src/jvm/clojure/lang/LispReader.java @@ -626,19 +626,19 @@ public static class MetaReader extends AFn{ throw new IllegalArgumentException("Metadata must be Symbol,Keyword,String or Map");
Object o = read(r, true, null, true);
- if(o instanceof IObj)
+ if(o instanceof IMeta)
{
if(line != -1 && o instanceof ISeq)
meta = ((IPersistentMap) meta).assoc(RT.LINE_KEY, line);
- if(o instanceof Var)
+ if(o instanceof IReference)
{
- ((Var)o).setMeta((IPersistentMap) meta);
+ ((IReference)o).resetMeta((IPersistentMap) meta);
return o;
}
return ((IObj) o).withMeta((IPersistentMap) meta);
}
else
- throw new IllegalArgumentException("Metadata can only be applied to IObjs");
+ throw new IllegalArgumentException("Metadata can only be applied to IMetas");
}
}
@@ -718,7 +718,7 @@ public static class SyntaxQuoteReader extends AFn{ else
ret = RT.list(Compiler.QUOTE, form);
- if(form instanceof IObj && !(form instanceof Var) && ((IObj) form).meta() != null)
+ if(form instanceof IObj && RT.meta(form) != null)
{
//filter line numbers
IPersistentMap newMeta = ((IObj) form).meta().without(RT.LINE_KEY);
diff --git a/src/jvm/clojure/lang/Namespace.java b/src/jvm/clojure/lang/Namespace.java index 9f272055..0af1bbda 100644 --- a/src/jvm/clojure/lang/Namespace.java +++ b/src/jvm/clojure/lang/Namespace.java @@ -15,7 +15,7 @@ package clojure.lang; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicReference; -public class Namespace{ +public class Namespace extends AReference{ final public Symbol name; final AtomicReference<IPersistentMap> mappings = new AtomicReference<IPersistentMap>(); final AtomicReference<IPersistentMap> aliases = new AtomicReference<IPersistentMap>(); diff --git a/src/jvm/clojure/lang/RT.java b/src/jvm/clojure/lang/RT.java index 2ad2ae49..57d0cf5c 100644 --- a/src/jvm/clojure/lang/RT.java +++ b/src/jvm/clojure/lang/RT.java @@ -502,8 +502,8 @@ static public ISeq vals(Object coll){ } static public IPersistentMap meta(Object x){ - if(x instanceof IObj) - return ((Obj) x).meta(); + if(x instanceof IMeta) + return ((IMeta) x).meta(); return null; } @@ -817,10 +817,7 @@ static public Object assocN(int n, Object val, Object coll){ } static boolean hasTag(Object o, Object tag){ - if(!(o instanceof IObj)) - return false; - IPersistentMap meta = ((IObj) o).meta(); - return Util.equal(tag, RT.get(meta, TAG_KEY)); + return Util.equal(tag, RT.get(RT.meta(o), TAG_KEY)); } /** diff --git a/src/jvm/clojure/lang/Ref.java b/src/jvm/clojure/lang/Ref.java index 2802a716..08700e5c 100644 --- a/src/jvm/clojure/lang/Ref.java +++ b/src/jvm/clojure/lang/Ref.java @@ -13,16 +13,21 @@ package clojure.lang; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.UUID; -public class Ref implements IFn, Comparable<Ref>, IRef{ - -public int compareTo(Ref o){ - return uuid.compareTo(o.uuid); -} - -public static class TVal{ +public class Ref extends AReference implements IFn, Comparable<Ref>, IRef{ + public int compareTo(Ref ref) { + if(this.id == ref.id) + return 0; + else if(this.id < ref.id) + return -1; + else + return 1; + } + + public static class TVal{ Object val; long point; long msecs; @@ -53,45 +58,23 @@ TVal tvals; final AtomicInteger faults; final ReentrantReadWriteLock lock; LockingTransaction.Info tinfo; -final UUID uuid; IFn validator; +final long id; -Ref(){ - this.tvals = null; - this.tinfo = null; - this.faults = new AtomicInteger(); - this.lock = new ReentrantReadWriteLock(); - this.uuid = UUID.randomUUID(); -} +static final AtomicLong ids = new AtomicLong(); public Ref(Object initVal) throws Exception{ this(initVal, null); } -public Ref(Object initVal,IFn validator) throws Exception{ - this(); - if(validator != null) - validate(validator,initVal); - this.validator = validator; +public Ref(Object initVal,IPersistentMap meta) throws Exception{ + super(meta); + this.id = ids.getAndIncrement(); + this.faults = new AtomicInteger(); + this.lock = new ReentrantReadWriteLock(); tvals = new TVal(initVal, 0, System.currentTimeMillis()); } -//note - makes no attempt to ensure there is no other Ref with same UUID - -//use only with a cache/registry -//public Ref(UUID uuid, Object initVal){ -// tvals = new TVal(initVal, 0, System.currentTimeMillis()); -// this.tinfo = null; -// this.faults = new AtomicInteger(); -// this.lock = new ReentrantReadWriteLock(); -// this.uuid = uuid; -//} - -public UUID getUUID(){ - return uuid; -} - - //the latest val // ok out of transaction @@ -120,9 +103,13 @@ public Object get(){ void validate(IFn vf, Object val){ try{ - if(vf != null) - vf.invoke(val); + if(vf != null && !RT.booleanCast(vf.invoke(val))) + throw new IllegalStateException("Invalid ref state"); } + catch(RuntimeException re) + { + throw re; + } catch(Exception e) { throw new IllegalStateException("Invalid ref state", e); diff --git a/src/jvm/clojure/lang/Settable.java b/src/jvm/clojure/lang/Settable.java new file mode 100644 index 00000000..384f7eef --- /dev/null +++ b/src/jvm/clojure/lang/Settable.java @@ -0,0 +1,18 @@ +/** + * Copyright (c) Rich Hickey. All rights reserved. + * The use and distribution terms for this software are covered by the + * Common Public License 1.0 (http://opensource.org/licenses/cpl.php) + * which can be found in the file CPL.TXT 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 Dec 31, 2008 */ + +package clojure.lang; + +public interface Settable { + Object doSet(Object val) throws Exception; + Object doReset(Object val) throws Exception; +} diff --git a/src/jvm/clojure/lang/Var.java b/src/jvm/clojure/lang/Var.java index 93d4d859..a8d29a6e 100644 --- a/src/jvm/clojure/lang/Var.java +++ b/src/jvm/clojure/lang/Var.java @@ -15,7 +15,7 @@ package clojure.lang; import java.util.concurrent.atomic.AtomicInteger; -public final class Var implements IFn, IRef, IObj{ +public final class Var extends AReference implements IFn, IRef, Settable{ static class Frame{ @@ -57,7 +57,7 @@ public final Symbol sym; public final Namespace ns; volatile IFn validator = null; -IPersistentMap _meta; +//IPersistentMap _meta; public static Var intern(Namespace ns, Symbol sym, Object root){ return intern(ns, sym, root, true); @@ -153,11 +153,14 @@ public Object alter(IFn fn, ISeq args) throws Exception{ } void validate(IFn vf, Object val){ - try - { - if(vf != null) - vf.invoke(val); + try{ + if(vf != null && !RT.booleanCast(vf.invoke(val))) + throw new IllegalStateException("Invalid var state"); } + catch(RuntimeException re) + { + throw re; + } catch(Exception e) { throw new IllegalStateException("Invalid var state", e); @@ -178,25 +181,33 @@ public Object set(Object val){ throw new IllegalStateException(String.format("Can't change/establish root binding of: %s with set", sym)); } -public void setMeta(IPersistentMap m){ - //ensure these basis keys - _meta = m.assoc(nameKey, sym).assoc(nsKey, ns); -} +public Object doSet(Object val) throws Exception { + return set(val); + } -public IPersistentMap meta(){ - return _meta; -} +public Object doReset(Object val) throws Exception { + bindRoot(val); + return val; + } -public IObj withMeta(IPersistentMap meta){ - throw new UnsupportedOperationException("Vars are not values"); +public void setMeta(IPersistentMap m) { + //ensure these basis keys + resetMeta(m.assoc(nameKey, sym).assoc(nsKey, ns)); } -public void setMacro(){ - _meta = _meta.assoc(macroKey, RT.T); +public void setMacro() { + try + { + alterMeta(assoc, RT.list(macroKey, RT.T)); + } + catch (Exception e) + { + throw new RuntimeException(e); + } } public boolean isMacro(){ - return RT.booleanCast(_meta.valAt(macroKey)); + return RT.booleanCast(meta().valAt(macroKey)); } //public void setExported(boolean state){ @@ -204,7 +215,7 @@ public boolean isMacro(){ //} public boolean isPublic(){ - return !RT.booleanCast(_meta.valAt(privateKey)); + return !RT.booleanCast(meta().valAt(privateKey)); } public Object getRoot(){ @@ -212,11 +223,18 @@ public Object getRoot(){ } public Object getTag(){ - return _meta.valAt(RT.TAG_KEY); + return meta().valAt(RT.TAG_KEY); } -public void setTag(Symbol tag){ - _meta = _meta.assoc(RT.TAG_KEY, tag); +public void setTag(Symbol tag) { + try + { + alterMeta(assoc, RT.list(RT.TAG_KEY, tag)); + } + catch (Exception e) + { + throw new RuntimeException(e); + } } final public boolean hasRoot(){ @@ -227,7 +245,14 @@ final public boolean hasRoot(){ synchronized public void bindRoot(Object root){ validate(getValidator(), root); this.root = root; - _meta = _meta.assoc(macroKey, RT.F); + try + { + alterMeta(assoc, RT.list(macroKey, RT.F)); + } + catch (Exception e) + { + throw new RuntimeException(e); + } } synchronized void swapRoot(Object root){ @@ -444,4 +469,10 @@ public Object applyTo(ISeq arglist) throws Exception{ return AFn.applyToHelper(this, arglist); } +static IFn assoc = new AFn(){ + @Override + public Object invoke(Object m, Object k, Object v) throws Exception { + return RT.assoc(m, k, v); + } +}; } |