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 | |
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
-rw-r--r-- | src/clj/clojure/core.clj | 102 | ||||
-rw-r--r-- | src/clj/clojure/core_proxy.clj | 52 | ||||
-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 |
17 files changed, 398 insertions, 188 deletions
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj index c6e81bad..55407a39 100644 --- a/src/clj/clojure/core.clj +++ b/src/clj/clojure/core.clj @@ -150,8 +150,8 @@ #^{:arglists '([obj]) :doc "Returns the metadata of obj, returns nil if there is no metadata."} meta (fn meta [x] - (if (instance? clojure.lang.IObj x) - (. #^clojure.lang.IObj x (meta))))) + (if (instance? clojure.lang.IMeta x) + (. #^clojure.lang.IMeta x (meta))))) (def #^{:arglists '([#^clojure.lang.IObj obj m]) @@ -1047,14 +1047,31 @@ [sym] (. clojure.lang.Var (find sym))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Refs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defn #^{:private true} + setup-reference [r options] + (let [opts (apply hash-map options)] + (when (:meta opts) + (.resetMeta r (:meta opts))) + (when (:validator opts) + (.setValidator r (:validator opts))) + r)) + (defn agent - "Creates and returns an agent with an initial value of state and an - optional validate fn. validate-fn must be nil or a side-effect-free fn of - one argument, which will be passed the intended new state on any state + "Creates and returns an agent with an initial value of state and + zero or more options (in any order): + + :meta metadata-map + + :validator validate-fn + + If metadata-map is supplied, it will be come the metadata on the + agent. validate-fn must be nil or a side-effect-free fn of one + argument, which will be passed the intended new state on any state change. If the new state is unacceptable, the validate-fn should - throw an exception." + return false or throw an exception." ([state] (new clojure.lang.Agent state)) - ([state validate-fn] (new clojure.lang.Agent state validate-fn))) + ([state & options] + (setup-reference (agent state) options))) (defn send "Dispatch an action to an agent. Returns the agent immediately. @@ -1119,14 +1136,21 @@ [] (. clojure.lang.Agent shutdown)) (defn ref - "Creates and returns a Ref with an initial value of x and an optional validate fn. - validate-fn must be nil or a side-effect-free fn of one argument, which will - be passed the intended new state on any state change. If the new - state is unacceptable, the validate-fn should throw an - exception. validate-fn will be called on transaction commit, when - all refs have their final values." + "Creates and returns a Ref with an initial value of x and zero or + more options (in any order): + + :meta metadata-map + + :validator validate-fn + + If metadata-map is supplied, it will be come the metadata on the + ref. validate-fn must be nil or a side-effect-free fn of one + argument, which will be passed the intended new state on any state + change. If the new state is unacceptable, the validate-fn should + return false or throw an exception. validate-fn will be called on + transaction commit, when all refs have their final values." ([x] (new clojure.lang.Ref x)) - ([x validate-fn] (new clojure.lang.Ref x validate-fn))) + ([x & options] (setup-reference (ref x) options))) (defn deref "Also reader macro: @ref/@agent/@var/@atom Within a transaction, @@ -1135,11 +1159,45 @@ or atom, returns its current state." [#^clojure.lang.IRef ref] (. ref (get))) -(defn set-validator +(defn atom + "Creates and returns an Atom with an initial value of x and zero or + more options (in any order): + + :meta metadata-map + + :validator validate-fn + + If metadata-map is supplied, it will be come the metadata on the + atom. validate-fn must be nil or a side-effect-free fn of one + argument, which will be passed the intended new state on any state + change. If the new state is unacceptable, the validate-fn should + return false or throw an exception." + ([x] (new clojure.lang.Atom x)) + ([x & options] (setup-reference (atom x) options))) + +(defn swap! + "Atomically swaps the value of atom to be: + (apply f current-value-of-atom args). Note that f may be called + multiple times, and thus should be free of side effects. Returns + the value that was swapped in." + [#^clojure.lang.Atom atom f & args] (.swap atom f args)) + +(defn compare-and-set! + "Atomically sets the value of atom to newval if and only if the + current value of the atom is identical to oldval. Returns true if + set happened, else false" + [#^clojure.lang.Atom atom oldval newval] (.compareAndSet atom oldval newval)) + +(defn reset! + "Sets the value of atom to newval without regard for the + current value. Returns newval." + [#^clojure.lang.Atom atom newval] (.reset atom newval)) + +(defn set-validator! "Sets the validator-fn for a var/ref/agent/atom. validator-fn must be nil or a side-effect-free fn of one argument, which will be passed the intended new state on any state change. If the new state is unacceptable, the - validator-fn should throw an exception. If the current state (root + validator-fn should return false or throw an exception. If the current state (root value if var) is not acceptable to the new validator, an exception will be thrown and the validator will not be changed." [#^clojure.lang.IRef iref validator-fn] (. iref (setValidator validator-fn))) @@ -1148,6 +1206,18 @@ "Gets the validator-fn for a var/ref/agent/atom." [#^clojure.lang.IRef iref] (. iref (getValidator))) +(defn alter-meta! + "Atomically sets the metadata for a namespace/var/ref/agent/atom to be: + + (apply f its-current-meta args) + + f must be free of side-effects" + [#^clojure.lang.IReference iref f & args] (.alterMeta iref f args)) + +(defn reset-meta! + "Atomically resets the metadata for a namespace/var/ref/agent/atom" + [#^clojure.lang.IReference iref metadata-map] (.resetMeta iref metadata-map)) + (defn commute "Must be called in a transaction. Sets the in-transaction-value of ref to: diff --git a/src/clj/clojure/core_proxy.clj b/src/clj/clojure/core_proxy.clj index b1e4c4cb..bda9824a 100644 --- a/src/clj/clojure/core_proxy.clj +++ b/src/clj/clojure/core_proxy.clj @@ -339,57 +339,5 @@ (lazy-cons (new clojure.lang.MapEntry (first pseq) (v (first pseq))) (thisfn (rest pseq))))) (keys pmap)))))) -(import '(java.util.concurrent.atomic AtomicReference)) -(defn atom - "Creates and returns a new Atom with an initial value of x and an - optional validate fn. validate-fn must be nil or a side-effect-free - fn of one argument, which will be passed the intended new state on - any state change. If the new state is unacceptable, the validate-fn - should throw an exception." - ([x] (atom x nil)) - ([x validator-fn] - (let [validator (AtomicReference. nil) - atom (proxy [AtomicReference clojure.lang.IRef] [x] - (getValidator [] (.get validator)) - (setValidator [f] - (when f - (try - (f @this) - (catch Exception e - (throw (IllegalStateException. "Invalid atom state" e))))) - (.set validator f)))] - (set-validator atom validator-fn) - atom))) - -(defn swap! - "Atomically swaps the value of atom to be: - (apply f current-value-of-atom args). Note that f may be called - multiple times, and thus should be free of side effects. Returns - the value that was swapped in." - [#^AtomicReference atom f & args] - (let [validate (get-validator atom)] - (loop [oldv (.get atom)] - (let [newv (apply f oldv args)] - (when validate - (try - (validate newv) - (catch Exception e - (throw (IllegalStateException. "Invalid atom state" e))))) - (if (.compareAndSet atom oldv newv) - newv - (recur (.get atom))))))) - -(defn compare-and-set! - "Atomically sets the value of atom to newval if and only if the - current value of the atom is identical to oldval. Returns true if - set happened, else false" - [#^AtomicReference atom oldval newval] - (let [validate (get-validator atom)] - (when validate - (try - (validate newval) - (catch Exception e - (throw (IllegalStateException. "Invalid atom state" e))))) - (.compareAndSet atom oldval newval))) 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); + } +}; } |