1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
/**
* 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.
**/
using System;
using System.Threading;
namespace clojure.lang
{
public class Var : AFn
{
public readonly Symbol sym;
public Namespace ns;
public Binding binding;
public IFn fn; //todo, bind to throw stub?
public IFn setfn;
volatile IPersistentMap threadBindings = PersistentArrayIdentityMap.EMPTY;
internal Var(Symbol sym, Namespace ns)
{
if(sym.GetType() != typeof(Symbol))
throw new ArgumentException("Only simple symbols can be vars");
this.ns = ns;
this.sym = sym;
}
override public String ToString()
{
if(ns == null)
return "#:" + sym;
return ns.name + ":" + sym;
}
public Var bind(Object val)
{
if(binding == null)
binding = new Binding(val);
else
binding.val = val;
if (val is IFn)
this.fn = (IFn)val;
else
this.fn = null; //todo, bind to throw stub?
return this;
}
public Object getValue()
{
Binding binding = getBinding();
if(binding != null)
return binding.val;
throw new InvalidOperationException(this.ToString() + " is unbound.");
}
public Object setValue(Object val)
{
Binding b = getThreadBinding();
if(b != null)
return b.val = val;
if(binding == null)
throw new InvalidOperationException(this.ToString() + " is unbound.");
if(val is IFn)
this.fn = (IFn) val;
else
this.fn = null; //todo, bind to throw stub?
return binding.val = val;
}
public Binding pushThreadBinding(Object val) {
Binding ret = new Binding(val, getThreadBinding());
setThreadBinding(ret);
return ret;
}
public void popThreadBinding() {
setThreadBinding(getThreadBinding().rest);
}
Binding getBinding()
{
Binding b = getThreadBinding();
if(b != null)
return b;
return binding;
}
Binding getThreadBinding()
{
if (threadBindings.count() > 0)
return (Binding)threadBindings.get(Thread.CurrentThread);
return null;
}
void setThreadBinding(Binding b) {
Thread thread = Thread.CurrentThread;
IPersistentMap tb;
IPersistentMap newtb;
do
{
tb = threadBindings;
if (b == null)
newtb = tb.remove(thread);
else
newtb = tb.put(thread, b);
} while (tb != Interlocked.CompareExchange(ref threadBindings, newtb, tb));
}
override public Object invoke() /*throws Exception*/
{
return fn.invoke();
}
override public Object invoke( Object arg1) /*throws Exception*/
{
return fn.invoke(arg1);
}
override public Object invoke( Object arg1, Object arg2) /*throws Exception*/
{
return fn.invoke(arg1,arg2);
}
override public Object invoke( Object arg1, Object arg2, Object arg3) /*throws Exception*/
{
return fn.invoke(arg1,arg2,arg3);
}
override public Object invoke( Object arg1, Object arg2, Object arg3, Object arg4) /*throws Exception*/
{
return fn.invoke(arg1,arg2,arg3,arg4);
}
override public Object invoke( Object arg1, Object arg2, Object arg3, Object arg4, Object arg5)
/*throws Exception*/
{
return fn.invoke(arg1,arg2,arg3,arg4,arg5);
}
override public Object invoke( Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, ISeq args)
/*throws Exception*/
{
return fn.invoke(arg1,arg2,arg3,arg4,arg5,args);
}
}
}
|