summaryrefslogtreecommitdiff
path: root/src/jvm/clojure
diff options
context:
space:
mode:
authorRich Hickey <richhickey@gmail.com>2009-01-01 20:52:00 +0000
committerRich Hickey <richhickey@gmail.com>2009-01-01 20:52:00 +0000
commit8a6c52556d92ab6ab369c7bf2a8c956add582de9 (patch)
tree3058f6da1fb15c973ba165593b3b7edb2aa4f61f /src/jvm/clojure
parent96757ae6fe6651f2753f7495a6ff476ed72c1798 (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.java60
-rw-r--r--src/jvm/clojure/lang/AReference.java40
-rw-r--r--src/jvm/clojure/lang/Agent.java35
-rw-r--r--src/jvm/clojure/lang/Atom.java54
-rw-r--r--src/jvm/clojure/lang/Box.java2
-rw-r--r--src/jvm/clojure/lang/Compiler.java21
-rw-r--r--src/jvm/clojure/lang/IMeta.java17
-rw-r--r--src/jvm/clojure/lang/IObj.java6
-rw-r--r--src/jvm/clojure/lang/IReference.java18
-rw-r--r--src/jvm/clojure/lang/LispReader.java10
-rw-r--r--src/jvm/clojure/lang/Namespace.java2
-rw-r--r--src/jvm/clojure/lang/RT.java9
-rw-r--r--src/jvm/clojure/lang/Ref.java63
-rw-r--r--src/jvm/clojure/lang/Settable.java18
-rw-r--r--src/jvm/clojure/lang/Var.java77
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);
+ }
+};
}