summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRich Hickey <richhickey@gmail.com>2010-10-15 17:49:55 -0400
committerRich Hickey <richhickey@gmail.com>2010-10-15 17:49:55 -0400
commitb9b1a094499b69a94bd47fc94c4f082d80239fa9 (patch)
treea33460655455fe172b049e6ced18ce6448faefd2
parent64e4a00b25baed8315e17752241a3a3d28b5280c (diff)
require dynamically rebindable vars be explicitly declared dynamic, via ^:dynamic metadata support in def, or setDynamic builder method in Var. Also auto-enable :dynamic for *var*s as a bridge - prints warning, and will be removed before release.
-rw-r--r--src/clj/clojure/core.clj18
-rw-r--r--src/clj/clojure/core_print.clj4
-rw-r--r--src/clj/clojure/java/browse.clj2
-rw-r--r--src/clj/clojure/java/javadoc.clj10
-rw-r--r--src/clj/clojure/java/shell.clj4
-rw-r--r--src/clj/clojure/pprint/cl_format.clj2
-rw-r--r--src/clj/clojure/pprint/column_writer.clj2
-rw-r--r--src/clj/clojure/pprint/dispatch.clj4
-rw-r--r--src/clj/clojure/pprint/pprint_base.clj24
-rw-r--r--src/clj/clojure/test.clj14
-rw-r--r--src/clj/clojure/test/junit.clj4
-rw-r--r--src/clj/clojure/xml.clj8
-rw-r--r--src/jvm/clojure/lang/Compiler.java80
-rw-r--r--src/jvm/clojure/lang/LispReader.java4
-rw-r--r--src/jvm/clojure/lang/RT.java34
-rw-r--r--src/jvm/clojure/lang/Var.java26
-rw-r--r--test/clojure/test_clojure/test.clj2
-rw-r--r--test/clojure/test_clojure/test_fixtures.clj4
-rw-r--r--test/clojure/test_clojure/vars.clj2
19 files changed, 144 insertions, 104 deletions
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index 2f5504cc..f2993af8 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -3140,7 +3140,7 @@
(number? x) (BigDecimal/valueOf (long x))
:else (BigDecimal. x)))
-(def ^{:private true} print-initialized false)
+(def ^:dynamic ^{:private true} print-initialized false)
(defmulti print-method (fn [x writer] (type x)))
(defmulti print-dup (fn [x writer] (class x)))
@@ -5095,17 +5095,17 @@
;;;;;;;;;;; require/use/load, contributed by Stephen C. Gilardi ;;;;;;;;;;;;;;;;;;
-(defonce
+(defonce ^:dynamic
^{:private true
:doc "A ref to a sorted set of symbols representing loaded libs"}
*loaded-libs* (ref (sorted-set)))
-(defonce
+(defonce ^:dynamic
^{:private true
:doc "the set of paths currently being loaded by this thread"}
*pending-paths* #{})
-(defonce
+(defonce ^:dynamic
^{:private true :doc
"True while a verbose load is pending"}
*loading-verbosely* false)
@@ -5465,22 +5465,22 @@
:static true}
[coll] (instance? clojure.lang.Reversible coll))
-(def
+(def ^:dynamic
^{:doc "bound in a repl thread to the most recent value printed"
:added "1.0"}
*1)
-(def
+(def ^:dynamic
^{:doc "bound in a repl thread to the second most recent value printed"
:added "1.0"}
*2)
-(def
+(def ^:dynamic
^{:doc "bound in a repl thread to the third most recent value printed"
:added "1.0"}
*3)
-(def
+(def ^:dynamic
^{:doc "bound in a repl thread to the most recent exception caught by the repl"
:added "1.0"}
*e)
@@ -5941,7 +5941,7 @@
:minor (Integer/valueOf ^String (prop "minor"))
:incremental (Integer/valueOf ^String (prop "incremental"))
:qualifier (prop "qualifier")}]
- (def *clojure-version*
+ (def ^:dynamic *clojure-version*
(if (not (= (prop "interim") "false"))
(clojure.lang.RT/assoc clojure-version :interim true)
clojure-version)))
diff --git a/src/clj/clojure/core_print.clj b/src/clj/clojure/core_print.clj
index a8a198fd..dad509a7 100644
--- a/src/clj/clojure/core_print.clj
+++ b/src/clj/clojure/core_print.clj
@@ -12,7 +12,7 @@
(import '(java.io Writer))
-(def
+(def ^:dynamic
^{:doc "*print-length* controls how many items of each collection the
printer will print. If it is bound to logical false, there is no
limit. Otherwise, it must be bound to an integer indicating the maximum
@@ -23,7 +23,7 @@
:added "1.0"}
*print-length* nil)
-(def
+(def ^:dynamic
^{:doc "*print-level* controls how many levels deep the printer will
print nested objects. If it is bound to logical false, there is no
limit. Otherwise, it must be bound to an integer indicating the maximum
diff --git a/src/clj/clojure/java/browse.clj b/src/clj/clojure/java/browse.clj
index bc71553a..1acda374 100644
--- a/src/clj/clojure/java/browse.clj
+++ b/src/clj/clojure/java/browse.clj
@@ -17,7 +17,7 @@
(-> "os.name" System/getProperty .toLowerCase
(.startsWith "mac os x")))
-(def *open-url-script* (when (macosx?) "/usr/bin/open"))
+(def ^:dynamic *open-url-script* (when (macosx?) "/usr/bin/open"))
(defn- open-url-in-browser
"Opens url (a string) in the default system web browser. May not
diff --git a/src/clj/clojure/java/javadoc.clj b/src/clj/clojure/java/javadoc.clj
index cefdff1f..7a68bc0a 100644
--- a/src/clj/clojure/java/javadoc.clj
+++ b/src/clj/clojure/java/javadoc.clj
@@ -13,17 +13,17 @@
(:import
(java.io File)))
-(def *feeling-lucky-url* "http://www.google.com/search?btnI=I%27m%20Feeling%20Lucky&q=allinurl:")
-(def *feeling-lucky* true)
+(def ^:dynamic *feeling-lucky-url* "http://www.google.com/search?btnI=I%27m%20Feeling%20Lucky&q=allinurl:")
+(def ^:dynamic *feeling-lucky* true)
-(def *local-javadocs* (ref (list)))
+(def ^:dynamic *local-javadocs* (ref (list)))
-(def *core-java-api*
+(def ^:dynamic *core-java-api*
(if (= "1.5" (System/getProperty "java.specification.version"))
"http://java.sun.com/j2se/1.5.0/docs/api/"
"http://java.sun.com/javase/6/docs/api/"))
-(def *remote-javadocs*
+(def ^:dynamic *remote-javadocs*
(ref (sorted-map
"java." *core-java-api*
"javax." *core-java-api*
diff --git a/src/clj/clojure/java/shell.clj b/src/clj/clojure/java/shell.clj
index 004629df..5388e7a2 100644
--- a/src/clj/clojure/java/shell.clj
+++ b/src/clj/clojure/java/shell.clj
@@ -15,8 +15,8 @@ collecting its stdout"}
(:import (java.io OutputStreamWriter ByteArrayOutputStream StringWriter)
(java.nio.charset Charset)))
-(def *sh-dir* nil)
-(def *sh-env* nil)
+(def ^:dynamic *sh-dir* nil)
+(def ^:dynamic *sh-env* nil)
(defmacro with-sh-dir
"Sets the directory for use with sh, see sh for details."
diff --git a/src/clj/clojure/pprint/cl_format.clj b/src/clj/clojure/pprint/cl_format.clj
index 5c934f31..42594f07 100644
--- a/src/clj/clojure/pprint/cl_format.clj
+++ b/src/clj/clojure/pprint/cl_format.clj
@@ -63,7 +63,7 @@ http://www.lispworks.com/documentation/HyperSpec/Body/22_c.htm
navigator (init-navigator args)]
(execute-format writer compiled-format navigator)))
-(def ^{:private true} *format-str* nil)
+(def ^:dynamic ^{:private true} *format-str* nil)
(defn- format-error [message offset]
(let [full-message (str message \newline *format-str* \newline
diff --git a/src/clj/clojure/pprint/column_writer.clj b/src/clj/clojure/pprint/column_writer.clj
index f02ac0dc..0449d439 100644
--- a/src/clj/clojure/pprint/column_writer.clj
+++ b/src/clj/clojure/pprint/column_writer.clj
@@ -20,7 +20,7 @@
(import [clojure.lang IDeref]
[java.io Writer])
-(def ^{:private true} *default-page-width* 72)
+(def ^:dynamic ^{:private true} *default-page-width* 72)
(defn- get-field [^Writer this sym]
(sym @@this))
diff --git a/src/clj/clojure/pprint/dispatch.clj b/src/clj/clojure/pprint/dispatch.clj
index 77e145fa..99a131c8 100644
--- a/src/clj/clojure/pprint/dispatch.clj
+++ b/src/clj/clojure/pprint/dispatch.clj
@@ -289,7 +289,7 @@
(pprint-simple-code-list alis)))
;;; The map of symbols that are defined in an enclosing #() anonymous function
-(def ^{:private true} *symbol-map* {})
+(def ^:dynamic ^{:private true} *symbol-map* {})
(defn- pprint-anon-func [alis]
(let [args (second alis)
@@ -342,7 +342,7 @@
%))
amap))))
-(def ^{:private true} *code-table*
+(def ^:dynamic ^{:private true} *code-table*
(two-forms
(add-core-ns
{'def pprint-hold-first, 'defonce pprint-hold-first,
diff --git a/src/clj/clojure/pprint/pprint_base.clj b/src/clj/clojure/pprint/pprint_base.clj
index 9ff74e85..9c82fa69 100644
--- a/src/clj/clojure/pprint/pprint_base.clj
+++ b/src/clj/clojure/pprint/pprint_base.clj
@@ -27,24 +27,24 @@
;;; constructs
-(def
+(def ^:dynamic
^{:doc "Bind to true if you want write to use pretty printing", :added "1.2"}
*print-pretty* true)
-(defonce ; If folks have added stuff here, don't overwrite
+(defonce ^:dynamic ; If folks have added stuff here, don't overwrite
^{:doc "The pretty print dispatch function. Use with-pprint-dispatch or set-pprint-dispatch
to modify.",
:added "1.2"}
*print-pprint-dispatch* nil)
-(def
+(def ^:dynamic
^{:doc "Pretty printing will try to avoid anything going beyond this column.
Set it to nil to have pprint let the line be arbitrarily long. This will ignore all
non-mandatory newlines.",
:added "1.2"}
*print-right-margin* 72)
-(def
+(def ^:dynamic
^{:doc "The column at which to enter miser style. Depending on the dispatch table,
miser style add newlines in more places to try to keep lines short allowing for further
levels of nesting.",
@@ -52,24 +52,24 @@ levels of nesting.",
*print-miser-width* 40)
;;; TODO implement output limiting
-(def
+(def ^:dynamic
^{:private true,
:doc "Maximum number of lines to print in a pretty print instance (N.B. This is not yet used)"}
*print-lines* nil)
;;; TODO: implement circle and shared
-(def
+(def ^:dynamic
^{:private true,
:doc "Mark circular structures (N.B. This is not yet used)"}
*print-circle* nil)
;;; TODO: should we just use *print-dup* here?
-(def
+(def ^:dynamic
^{:private true,
:doc "Mark repeated structures rather than repeat them (N.B. This is not yet used)"}
*print-shared* nil)
-(def
+(def ^:dynamic
^{:doc "Don't print namespaces with symbols. This is particularly useful when
pretty printing the results of macro expansions"
:added "1.2"}
@@ -77,14 +77,14 @@ pretty printing the results of macro expansions"
;;; TODO: support print-base and print-radix in cl-format
;;; TODO: support print-base and print-radix in rationals
-(def
+(def ^:dynamic
^{:doc "Print a radix specifier in front of integers and rationals. If *print-base* is 2, 8,
or 16, then the radix specifier used is #b, #o, or #x, respectively. Otherwise the
radix specifier is in the form #XXr where XX is the decimal value of *print-base* "
:added "1.2"}
*print-radix* nil)
-(def
+(def ^:dynamic
^{:doc "The base to use for printing integers and rationals."
:added "1.2"}
*print-base* 10)
@@ -96,9 +96,9 @@ radix specifier is in the form #XXr where XX is the decimal value of *print-base
;; structure
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(def ^{ :private true } *current-level* 0)
+(def ^:dynamic ^{ :private true } *current-level* 0)
-(def ^{ :private true } *current-length* nil)
+(def ^:dynamic ^{ :private true } *current-length* nil)
;; TODO: add variables for length, lines.
diff --git a/src/clj/clojure/test.clj b/src/clj/clojure/test.clj
index 62dba13b..755ec0b4 100644
--- a/src/clj/clojure/test.clj
+++ b/src/clj/clojure/test.clj
@@ -236,14 +236,14 @@
;;; USER-MODIFIABLE GLOBALS
-(defonce
+(defonce ^:dynamic
^{:doc "True by default. If set to false, no test functions will
be created by deftest, set-test, or with-test. Use this to omit
tests when compiling or loading production code."
:added "1.1"}
*load-tests* true)
-(def
+(def ^:dynamic
^{:doc "The maximum depth of stack traces to print when an Exception
is thrown during a test. Defaults to nil, which means print the
complete stack trace."
@@ -253,16 +253,16 @@
;;; GLOBALS USED BY THE REPORTING FUNCTIONS
-(def *report-counters* nil) ; bound to a ref of a map in test-ns
+(def ^:dynamic *report-counters* nil) ; bound to a ref of a map in test-ns
-(def *initial-report-counters* ; used to initialize *report-counters*
+(def ^:dynamic *initial-report-counters* ; used to initialize *report-counters*
{:test 0, :pass 0, :fail 0, :error 0})
-(def *testing-vars* (list)) ; bound to hierarchy of vars being tested
+(def ^:dynamic *testing-vars* (list)) ; bound to hierarchy of vars being tested
-(def *testing-contexts* (list)) ; bound to hierarchy of "testing" strings
+(def ^:dynamic *testing-contexts* (list)) ; bound to hierarchy of "testing" strings
-(def *test-out* *out*) ; PrintWriter for test reporting output
+(def ^:dynamic *test-out* *out*) ; PrintWriter for test reporting output
(defmacro with-test-out
"Runs body with *out* bound to the value of *test-out*."
diff --git a/src/clj/clojure/test/junit.clj b/src/clj/clojure/test/junit.clj
index 7e696e3b..026b209d 100644
--- a/src/clj/clojure/test/junit.clj
+++ b/src/clj/clojure/test/junit.clj
@@ -45,8 +45,8 @@
(defn- escape-xml [text]
(apply str (map #(escape-xml-map % %) text)))
-(def *var-context*)
-(def *depth*)
+(def ^:dynamic *var-context*)
+(def ^:dynamic *depth*)
(defn indent
[]
diff --git a/src/clj/clojure/xml.clj b/src/clj/clojure/xml.clj
index 78f053fb..4e4220f2 100644
--- a/src/clj/clojure/xml.clj
+++ b/src/clj/clojure/xml.clj
@@ -12,10 +12,10 @@
(:import (org.xml.sax ContentHandler Attributes SAXException)
(javax.xml.parsers SAXParser SAXParserFactory)))
-(def *stack*)
-(def *current*)
-(def *state*) ; :element :chars :between
-(def *sb*)
+(def ^:dynamic *stack*)
+(def ^:dynamic *current*)
+(def ^:dynamic *state*) ; :element :chars :between
+(def ^:dynamic *sb*)
(defstruct element :tag :attrs :content)
diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java
index b1b4d84e..5f8fdc7c 100644
--- a/src/jvm/clojure/lang/Compiler.java
+++ b/src/jvm/clojure/lang/Compiler.java
@@ -86,6 +86,7 @@ static final String COMPILE_STUB_PREFIX = "compile__stub";
static final Keyword protocolKey = Keyword.intern(null, "protocol");
static final Keyword onKey = Keyword.intern(null, "on");
+static Keyword dynamicKey = Keyword.intern("dynamic");
static final Symbol NS = Symbol.intern("ns");
static final Symbol IN_NS = Symbol.intern("in-ns");
@@ -174,58 +175,58 @@ static
//symbol->localbinding
-static final public Var LOCAL_ENV = Var.create(null);
+static final public Var LOCAL_ENV = Var.create(null).setDynamic();
//vector<localbinding>
-static final public Var LOOP_LOCALS = Var.create();
+static final public Var LOOP_LOCALS = Var.create().setDynamic();
//Label
-static final public Var LOOP_LABEL = Var.create();
+static final public Var LOOP_LABEL = Var.create().setDynamic();
//vector<object>
-static final public Var CONSTANTS = Var.create();
+static final public Var CONSTANTS = Var.create().setDynamic();
//IdentityHashMap
-static final public Var CONSTANT_IDS = Var.create();
+static final public Var CONSTANT_IDS = Var.create().setDynamic();
//vector<keyword>
-static final public Var KEYWORD_CALLSITES = Var.create();
+static final public Var KEYWORD_CALLSITES = Var.create().setDynamic();
//vector<var>
-static final public Var PROTOCOL_CALLSITES = Var.create();
+static final public Var PROTOCOL_CALLSITES = Var.create().setDynamic();
//vector<var>
-static final public Var VAR_CALLSITES = Var.create();
+static final public Var VAR_CALLSITES = Var.create().setDynamic();
//keyword->constid
-static final public Var KEYWORDS = Var.create();
+static final public Var KEYWORDS = Var.create().setDynamic();
//var->constid
-static final public Var VARS = Var.create();
+static final public Var VARS = Var.create().setDynamic();
//FnFrame
-static final public Var METHOD = Var.create(null);
+static final public Var METHOD = Var.create(null).setDynamic();
//null or not
-static final public Var IN_CATCH_FINALLY = Var.create(null);
+static final public Var IN_CATCH_FINALLY = Var.create(null).setDynamic();
//DynamicClassLoader
-static final public Var LOADER = Var.create();
+static final public Var LOADER = Var.create().setDynamic();
//String
static final public Var SOURCE = Var.intern(Namespace.findOrCreate(Symbol.intern("clojure.core")),
- Symbol.intern("*source-path*"), "NO_SOURCE_FILE");
+ Symbol.intern("*source-path*"), "NO_SOURCE_FILE").setDynamic();
//String
static final public Var SOURCE_PATH = Var.intern(Namespace.findOrCreate(Symbol.intern("clojure.core")),
- Symbol.intern("*file*"), "NO_SOURCE_PATH");
+ Symbol.intern("*file*"), "NO_SOURCE_PATH").setDynamic();
//String
static final public Var COMPILE_PATH = Var.intern(Namespace.findOrCreate(Symbol.intern("clojure.core")),
- Symbol.intern("*compile-path*"), null);
+ Symbol.intern("*compile-path*"), null).setDynamic();
//boolean
static final public Var COMPILE_FILES = Var.intern(Namespace.findOrCreate(Symbol.intern("clojure.core")),
- Symbol.intern("*compile-files*"), Boolean.FALSE);
+ Symbol.intern("*compile-files*"), Boolean.FALSE).setDynamic();
static final public Var INSTANCE = Var.intern(Namespace.findOrCreate(Symbol.intern("clojure.core")),
Symbol.intern("instance?"));
@@ -234,31 +235,31 @@ static final public Var ADD_ANNOTATIONS = Var.intern(Namespace.findOrCreate(Symb
Symbol.intern("add-annotations"));
//Integer
-static final public Var LINE = Var.create(0);
+static final public Var LINE = Var.create(0).setDynamic();
//Integer
-static final public Var LINE_BEFORE = Var.create(0);
-static final public Var LINE_AFTER = Var.create(0);
+static final public Var LINE_BEFORE = Var.create(0).setDynamic();
+static final public Var LINE_AFTER = Var.create(0).setDynamic();
//Integer
-static final public Var NEXT_LOCAL_NUM = Var.create(0);
+static final public Var NEXT_LOCAL_NUM = Var.create(0).setDynamic();
//Integer
-static final public Var RET_LOCAL_NUM = Var.create();
+static final public Var RET_LOCAL_NUM = Var.create().setDynamic();
-static final public Var COMPILE_STUB_SYM = Var.create(null);
-static final public Var COMPILE_STUB_CLASS = Var.create(null);
+static final public Var COMPILE_STUB_SYM = Var.create(null).setDynamic();
+static final public Var COMPILE_STUB_CLASS = Var.create(null).setDynamic();
//PathNode chain
-static final public Var CLEAR_PATH = Var.create(null);
+static final public Var CLEAR_PATH = Var.create(null).setDynamic();
//tail of PathNode chain
-static final public Var CLEAR_ROOT = Var.create(null);
+static final public Var CLEAR_ROOT = Var.create(null).setDynamic();
//LocalBinding -> Set<LocalBindingExpr>
-static final public Var CLEAR_SITES = Var.create(null);
+static final public Var CLEAR_SITES = Var.create(null).setDynamic();
public enum C{
STATEMENT, //value ignored
@@ -326,19 +327,22 @@ static class DefExpr implements Expr{
public final Expr init;
public final Expr meta;
public final boolean initProvided;
+ public final boolean isDynamic;
public final String source;
public final int line;
final static Method bindRootMethod = Method.getMethod("void bindRoot(Object)");
final static Method setTagMethod = Method.getMethod("void setTag(clojure.lang.Symbol)");
final static Method setMetaMethod = Method.getMethod("void setMeta(clojure.lang.IPersistentMap)");
+ final static Method setDynamicMethod = Method.getMethod("clojure.lang.Var setDynamic(boolean)");
final static Method symintern = Method.getMethod("clojure.lang.Symbol intern(String, String)");
- public DefExpr(String source, int line, Var var, Expr init, Expr meta, boolean initProvided){
+ public DefExpr(String source, int line, Var var, Expr init, Expr meta, boolean initProvided, boolean isDynamic){
this.source = source;
this.line = line;
this.var = var;
this.init = init;
this.meta = meta;
+ this.isDynamic = isDynamic;
this.initProvided = initProvided;
}
@@ -370,7 +374,7 @@ static class DefExpr implements Expr{
if (initProvided || true)//includesExplicitMetadata((MapExpr) meta))
var.setMeta((IPersistentMap) meta.eval());
}
- return var;
+ return var.setDynamic(isDynamic);
}
catch(Throwable e)
{
@@ -383,6 +387,11 @@ static class DefExpr implements Expr{
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
objx.emitVar(gen, var);
+ if(isDynamic)
+ {
+ gen.push(isDynamic);
+ gen.invokeVirtual(VAR_TYPE, setDynamicMethod);
+ }
if(meta != null)
{
if (initProvided || true)//includesExplicitMetadata((MapExpr) meta))
@@ -440,6 +449,14 @@ static class DefExpr implements Expr{
throw new Exception("Can't create defs outside of current ns");
}
IPersistentMap mm = sym.meta();
+ boolean isDynamic = RT.booleanCast(RT.get(mm,dynamicKey));
+ if(!isDynamic && sym.name.startsWith("*") && sym.name.endsWith("*") && sym.name.length() > 1)
+ {
+ RT.errPrintWriter().format("Var %s not marked :dynamic true, setting to :dynamic. You should fix this before next release!\n",
+ sym);
+ isDynamic = true;
+ mm = (IPersistentMap) RT.assoc(mm,dynamicKey, RT.T);
+ }
if(RT.booleanCast(RT.get(mm, staticKey)))
{
IPersistentMap vm = v.meta();
@@ -456,7 +473,7 @@ static class DefExpr implements Expr{
Expr meta = analyze(context == C.EVAL ? context : C.EXPRESSION, mm);
return new DefExpr((String) SOURCE.deref(), (Integer) LINE.deref(),
v, analyze(context == C.EVAL ? context : C.EXPRESSION, RT.third(form), v.sym.name),
- meta, RT.count(form) == 3);
+ meta, RT.count(form) == 3, isDynamic);
}
}
}
@@ -3117,7 +3134,6 @@ static class InvokeExpr implements Expr{
public java.lang.reflect.Method onMethod;
static Keyword onKey = Keyword.intern("on");
static Keyword methodMapKey = Keyword.intern("method-map");
- static Keyword dynamicKey = Keyword.intern("dynamic");
public InvokeExpr(String source, int line, Symbol tag, Expr fexpr, IPersistentVector args) throws Exception{
this.source = source;
@@ -6479,7 +6495,7 @@ static public void writeClassFile(String internalName, byte[] bytecode) throws E
public static void pushNS(){
Var.pushThreadBindings(PersistentHashMap.create(Var.intern(Symbol.intern("clojure.core"),
- Symbol.intern("*ns*")), null));
+ Symbol.intern("*ns*")).setDynamic(), null));
}
public static ILookupThunk getLookupThunk(Object target, Keyword k){
diff --git a/src/jvm/clojure/lang/LispReader.java b/src/jvm/clojure/lang/LispReader.java
index 35addd7c..e5740a5c 100644
--- a/src/jvm/clojure/lang/LispReader.java
+++ b/src/jvm/clojure/lang/LispReader.java
@@ -58,9 +58,9 @@ static final Symbol CLOJURE_SLASH = Symbol.intern("clojure.core","/");
//static Pattern classNamePat = Pattern.compile("([a-zA-Z_][\\w\\.]*)\\.");
//symbol->gensymbol
-static Var GENSYM_ENV = Var.create(null);
+static Var GENSYM_ENV = Var.create(null).setDynamic();
//sorted-map num->gensymbol
-static Var ARG_ENV = Var.create(null);
+static Var ARG_ENV = Var.create(null).setDynamic();
static
{
diff --git a/src/jvm/clojure/lang/RT.java b/src/jvm/clojure/lang/RT.java
index 97abd85a..2c720c5f 100644
--- a/src/jvm/clojure/lang/RT.java
+++ b/src/jvm/clojure/lang/RT.java
@@ -170,24 +170,24 @@ static public Charset UTF8 = Charset.forName("UTF-8");
static public final Namespace CLOJURE_NS = Namespace.findOrCreate(Symbol.intern("clojure.core"));
//static final Namespace USER_NS = Namespace.findOrCreate(Symbol.intern("user"));
final static public Var OUT =
- Var.intern(CLOJURE_NS, Symbol.intern("*out*"), new OutputStreamWriter(System.out));
+ Var.intern(CLOJURE_NS, Symbol.intern("*out*"), new OutputStreamWriter(System.out)).setDynamic();
final static public Var IN =
Var.intern(CLOJURE_NS, Symbol.intern("*in*"),
- new LineNumberingPushbackReader(new InputStreamReader(System.in)));
+ new LineNumberingPushbackReader(new InputStreamReader(System.in))).setDynamic();
final static public Var ERR =
Var.intern(CLOJURE_NS, Symbol.intern("*err*"),
- new PrintWriter(new OutputStreamWriter(System.err), true));
+ new PrintWriter(new OutputStreamWriter(System.err), true)).setDynamic();
final static Keyword TAG_KEY = Keyword.intern(null, "tag");
-final static public Var AGENT = Var.intern(CLOJURE_NS, Symbol.intern("*agent*"), null);
-final static public Var READEVAL = Var.intern(CLOJURE_NS, Symbol.intern("*read-eval*"), T);
-final static public Var ASSERT = Var.intern(CLOJURE_NS, Symbol.intern("*assert*"), T);
-final static public Var MATH_CONTEXT = Var.intern(CLOJURE_NS, Symbol.intern("*math-context*"), null);
+final static public Var AGENT = Var.intern(CLOJURE_NS, Symbol.intern("*agent*"), null).setDynamic();
+final static public Var READEVAL = Var.intern(CLOJURE_NS, Symbol.intern("*read-eval*"), T).setDynamic();
+final static public Var ASSERT = Var.intern(CLOJURE_NS, Symbol.intern("*assert*"), T).setDynamic();
+final static public Var MATH_CONTEXT = Var.intern(CLOJURE_NS, Symbol.intern("*math-context*"), null).setDynamic();
static Keyword LINE_KEY = Keyword.intern(null, "line");
static Keyword FILE_KEY = Keyword.intern(null, "file");
static Keyword DECLARED_KEY = Keyword.intern(null, "declared");
static Keyword DOC_KEY = Keyword.intern(null, "doc");
final static public Var USE_CONTEXT_CLASSLOADER =
- Var.intern(CLOJURE_NS, Symbol.intern("*use-context-classloader*"), T);
+ Var.intern(CLOJURE_NS, Symbol.intern("*use-context-classloader*"), T).setDynamic();
//final static public Var CURRENT_MODULE = Var.intern(Symbol.intern("clojure.core", "current-module"),
// Module.findOrCreateModule("clojure/user"));
@@ -195,17 +195,17 @@ final static Symbol LOAD_FILE = Symbol.intern("load-file");
final static Symbol IN_NAMESPACE = Symbol.intern("in-ns");
final static Symbol NAMESPACE = Symbol.intern("ns");
static final Symbol IDENTICAL = Symbol.intern("identical?");
-final static Var CMD_LINE_ARGS = Var.intern(CLOJURE_NS, Symbol.intern("*command-line-args*"), null);
+final static Var CMD_LINE_ARGS = Var.intern(CLOJURE_NS, Symbol.intern("*command-line-args*"), null).setDynamic();
//symbol
final public static Var CURRENT_NS = Var.intern(CLOJURE_NS, Symbol.intern("*ns*"),
- CLOJURE_NS);
-
-final static Var FLUSH_ON_NEWLINE = Var.intern(CLOJURE_NS, Symbol.intern("*flush-on-newline*"), T);
-final static Var PRINT_META = Var.intern(CLOJURE_NS, Symbol.intern("*print-meta*"), F);
-final static Var PRINT_READABLY = Var.intern(CLOJURE_NS, Symbol.intern("*print-readably*"), T);
-final static Var PRINT_DUP = Var.intern(CLOJURE_NS, Symbol.intern("*print-dup*"), F);
-final static Var WARN_ON_REFLECTION = Var.intern(CLOJURE_NS, Symbol.intern("*warn-on-reflection*"), F);
-final static Var ALLOW_UNRESOLVED_VARS = Var.intern(CLOJURE_NS, Symbol.intern("*allow-unresolved-vars*"), F);
+ CLOJURE_NS).setDynamic();
+
+final static Var FLUSH_ON_NEWLINE = Var.intern(CLOJURE_NS, Symbol.intern("*flush-on-newline*"), T).setDynamic();
+final static Var PRINT_META = Var.intern(CLOJURE_NS, Symbol.intern("*print-meta*"), F).setDynamic();
+final static Var PRINT_READABLY = Var.intern(CLOJURE_NS, Symbol.intern("*print-readably*"), T).setDynamic();
+final static Var PRINT_DUP = Var.intern(CLOJURE_NS, Symbol.intern("*print-dup*"), F).setDynamic();
+final static Var WARN_ON_REFLECTION = Var.intern(CLOJURE_NS, Symbol.intern("*warn-on-reflection*"), F).setDynamic();
+final static Var ALLOW_UNRESOLVED_VARS = Var.intern(CLOJURE_NS, Symbol.intern("*allow-unresolved-vars*"), F).setDynamic();
final static Var IN_NS_VAR = Var.intern(CLOJURE_NS, Symbol.intern("in-ns"), F);
final static Var NS_VAR = Var.intern(CLOJURE_NS, Symbol.intern("ns"), F);
diff --git a/src/jvm/clojure/lang/Var.java b/src/jvm/clojure/lang/Var.java
index ca3b9533..5572a267 100644
--- a/src/jvm/clojure/lang/Var.java
+++ b/src/jvm/clojure/lang/Var.java
@@ -47,13 +47,15 @@ static class Frame{
}
}
-static ThreadLocal<Frame> dvals = new ThreadLocal<Frame>(){
+static final ThreadLocal<Frame> dvals = new ThreadLocal<Frame>(){
protected Frame initialValue(){
return new Frame();
}
};
+static public volatile int rev = 0;
+
static Keyword privateKey = Keyword.intern(null, "private");
static IPersistentMap privateMeta = new PersistentArrayMap(new Object[]{privateKey, Boolean.TRUE});
static Keyword macroKey = Keyword.intern(null, "macro");
@@ -62,6 +64,7 @@ static Keyword nsKey = Keyword.intern(null, "ns");
//static Keyword tagKey = Keyword.intern(null, "tag");
volatile Object root;
+volatile boolean dynamic = false;
transient final AtomicBoolean threadBound;
public final Symbol sym;
public final Namespace ns;
@@ -79,6 +82,20 @@ public static void resetThreadBindingFrame(Object frame){
dvals.set((Frame) frame);
}
+public Var setDynamic(){
+ this.dynamic = true;
+ return this;
+}
+
+public Var setDynamic(boolean b){
+ this.dynamic = b;
+ return this;
+}
+
+public final boolean isDynamic(){
+ return dynamic;
+}
+
public static Var intern(Namespace ns, Symbol sym, Object root){
return intern(ns, sym, root, true);
}
@@ -257,6 +274,7 @@ synchronized public void bindRoot(Object root){
validate(getValidator(), root);
Object oldroot = hasRoot()?this.root:null;
this.root = root;
+ ++rev;
try
{
alterMeta(dissoc, RT.list(macroKey));
@@ -272,11 +290,13 @@ synchronized void swapRoot(Object root){
validate(getValidator(), root);
Object oldroot = hasRoot()?this.root:null;
this.root = root;
+ ++rev;
notifyWatches(oldroot,root);
}
synchronized public void unbindRoot(){
this.root = dvals;
+ ++rev;
}
synchronized public void commuteRoot(IFn fn) throws Exception{
@@ -284,6 +304,7 @@ synchronized public void commuteRoot(IFn fn) throws Exception{
validate(getValidator(), newRoot);
Object oldroot = getRoot();
this.root = newRoot;
+ ++rev;
notifyWatches(oldroot,newRoot);
}
@@ -292,6 +313,7 @@ synchronized public Object alterRoot(IFn fn, ISeq args) throws Exception{
validate(getValidator(), newRoot);
Object oldroot = getRoot();
this.root = newRoot;
+ ++rev;
notifyWatches(oldroot,newRoot);
return newRoot;
}
@@ -303,6 +325,8 @@ public static void pushThreadBindings(Associative bindings){
{
IMapEntry e = (IMapEntry) bs.first();
Var v = (Var) e.key();
+ if(!v.dynamic)
+ throw new IllegalStateException(String.format("Can't dynamically bind non-dynamic var: %s/%s", v.ns, v.sym));
v.validate(v.getValidator(), e.val());
v.threadBound.set(true);
bmap = bmap.assoc(v, new TBox(Thread.currentThread(), e.val()));
diff --git a/test/clojure/test_clojure/test.clj b/test/clojure/test_clojure/test.clj
index 8a7a1e1c..086223f8 100644
--- a/test/clojure/test_clojure/test.clj
+++ b/test/clojure/test_clojure/test.clj
@@ -89,7 +89,7 @@
;; compares the event with the message, then calls the original
;; 'report' with modified arguments.
-(declare original-report)
+(declare ^:dynamic original-report)
(defn custom-report [data]
(let [event (:type data)
diff --git a/test/clojure/test_clojure/test_fixtures.clj b/test/clojure/test_clojure/test_fixtures.clj
index 0a6ff51a..90785787 100644
--- a/test/clojure/test_clojure/test_fixtures.clj
+++ b/test/clojure/test_clojure/test_fixtures.clj
@@ -14,9 +14,9 @@
(ns clojure.test-clojure.test-fixtures
(:use clojure.test))
-(declare *a* *b* *c* *d*)
+(declare ^:dynamic *a* ^:dynamic *b* ^:dynamic *c* ^:dynamic *d*)
-(def *n* 0)
+(def ^:dynamic *n* 0)
(defn fixture-a [f]
(binding [*a* 3] (f)))
diff --git a/test/clojure/test_clojure/vars.clj b/test/clojure/test_clojure/vars.clj
index e83c88c2..5b25321c 100644
--- a/test/clojure/test_clojure/vars.clj
+++ b/test/clojure/test_clojure/vars.clj
@@ -19,7 +19,7 @@
; declare