Skip to content

Commit

Permalink
split off arbitrary eval parts of #= and flip default for *read-eval*…
Browse files Browse the repository at this point in the history
… to false. N.B. this does not disable #=, only arbitrary eval using #=.
  • Loading branch information
richhickey committed Feb 5, 2013
1 parent cdc037d commit 974a64c
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 46 deletions.
9 changes: 5 additions & 4 deletions src/clj/clojure/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -5871,11 +5871,12 @@
{:added "1.0"})

(add-doc-and-meta *read-eval*
"When set to logical false, the EvalReader (#=(...)) is disabled in the
read/load in the thread-local binding.
Example: (binding [*read-eval* false] (read-string \"#=(eval (def x 3))\"))
"When set to logical true in the thread-local binding, the eval
reader (#=(...)) for arbitrary expressions is enabled in read/load.
Example:
(binding [*read-eval* true] (read-string \"#=(* 2 21)\"))
Defaults to true"
Defaults to false"
{:added "1.0"})

(defn future?
Expand Down
37 changes: 15 additions & 22 deletions src/clj/clojure/core_print.clj
Original file line number Diff line number Diff line change
Expand Up @@ -309,31 +309,23 @@
(defmethod print-dup clojure.lang.LazilyPersistentVector [o w] (print-method o w))

(def primitives-classnames
{Float/TYPE "Float/TYPE"
Integer/TYPE "Integer/TYPE"
Long/TYPE "Long/TYPE"
Boolean/TYPE "Boolean/TYPE"
Character/TYPE "Character/TYPE"
Double/TYPE "Double/TYPE"
Byte/TYPE "Byte/TYPE"
Short/TYPE "Short/TYPE"})
{Float/TYPE "float"
Integer/TYPE "int"
Long/TYPE "long"
Boolean/TYPE "boolean"
Character/TYPE "char"
Double/TYPE "double"
Byte/TYPE "byte"
Short/TYPE "short"})

(defmethod print-method Class [^Class c, ^Writer w]
(.write w (.getName c)))

(defmethod print-dup Class [^Class c, ^Writer w]
(cond
(.isPrimitive c) (do
(.write w "#=(identity ")
(.write w ^String (primitives-classnames c))
(.write w ")"))
(.isArray c) (do
(.write w "#=(java.lang.Class/forName \"")
(.write w (.getName c))
(.write w "\")"))
:else (do
(.write w "#=")
(.write w (.getName c)))))
(let [^String cs (or (primitives-classnames c) (.getName c))]
(.write w "#=\"")
(.write w cs)
(.write w "\"")))

(defmethod print-method java.math.BigDecimal [b, ^Writer w]
(.write w (str b))
Expand Down Expand Up @@ -368,7 +360,7 @@
(defmethod print-dup java.util.regex.Pattern [p ^Writer w] (print-method p w))

(defmethod print-dup clojure.lang.Namespace [^clojure.lang.Namespace n ^Writer w]
(.write w "#=(find-ns ")
(.write w "#=(clojure.lang.Namespace/find ")
(print-dup (.name n) w)
(.write w ")"))

Expand All @@ -380,7 +372,8 @@
(agent-error o))
" FAILED"
""))
pr-on, "", ">", (list (if (and (instance? clojure.lang.IPending o) (not (.isRealized o)))
pr-on, "", ">", (list (if (and (instance? clojure.lang.IPending o)
(not (.isRealized ^clojure.lang.IPending o)))
:pending
@o)), w))

Expand Down
71 changes: 53 additions & 18 deletions src/jvm/clojure/lang/LispReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@
import java.lang.reflect.Constructor;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand Down Expand Up @@ -1023,18 +1021,40 @@ else if(s.ns != null) //static method
}
*/

static IPersistentMap primclasses = RT.map("int", int.class,
"long", long.class,
"float", float.class,
"double", double.class,
"char", char.class,
"boolean", boolean.class,
"short", short.class,
"byte", byte.class,
"void", void.class);

static boolean onWhiteList(Class c){
Collection<Class> whitelist = (Collection<Class>) RT.READWHITELIST.deref();

for(Class wc : whitelist)
{
if(wc.isAssignableFrom(c))
return true;
}
return false;
}

public static class EvalReader extends AFn{
public Object invoke(Object reader, Object eq) {
if (!RT.booleanCast(RT.READEVAL.deref()))
{
throw Util.runtimeException("EvalReader not allowed when *read-eval* is false.");
}

boolean readeval = RT.booleanCast(RT.READEVAL.deref());

PushbackReader r = (PushbackReader) reader;
Object o = read(r, true, null, true);
if(o instanceof Symbol)
if(o instanceof Symbol || o instanceof String)
{
return RT.classForName(o.toString());
String s = o.toString();
Class c = (Class) primclasses.valAt(s);
if(c != null)
return c;
return RT.classForName(s);
}
else if(o instanceof IPersistentList)
{
Expand All @@ -1046,20 +1066,35 @@ else if(o instanceof IPersistentList)
}
if(fs.name.endsWith("."))
{
Object[] args = RT.toArray(RT.next(o));
return Reflector.invokeConstructor(RT.classForName(fs.name.substring(0, fs.name.length() - 1)), args);
Class c = RT.classForName(fs.name.substring(0, fs.name.length() - 1));
if(readeval || onWhiteList(c))
{
Object[] args = RT.toArray(RT.next(o));
return Reflector.invokeConstructor(c, args);
}
throw Util.runtimeException("eval reading not allowed when *read-eval* is false.");
}
if(Compiler.namesStaticMember(fs))
{
Object[] args = RT.toArray(RT.next(o));
return Reflector.invokeStaticMethod(fs.ns, fs.name, args);
Class c = RT.classForName(fs.ns);

if(readeval || onWhiteList(c))
{
Object[] args = RT.toArray(RT.next(o));
return Reflector.invokeStaticMethod(c, fs.name, args);
}
throw Util.runtimeException("eval reading not allowed when *read-eval* is false.");
}
Object v = Compiler.maybeResolveIn(Compiler.currentNS(), fs);
if(v instanceof Var)
if(readeval)
{
return ((IFn) v).applyTo(RT.next(o));
Object v = Compiler.maybeResolveIn(Compiler.currentNS(), fs);
if(v instanceof Var)
{
return ((IFn) v).applyTo(RT.next(o));
}
throw Util.runtimeException("Can't resolve " + fs);
}
throw Util.runtimeException("Can't resolve " + fs);
throw Util.runtimeException("eval reading not allowed when *read-eval* is false.");
}
else
throw new IllegalArgumentException("Unsupported #= form");
Expand Down
11 changes: 9 additions & 2 deletions src/jvm/clojure/lang/RT.java
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,13 @@ public class RT{
final static Keyword TAG_KEY = Keyword.intern(null, "tag");
final static Keyword CONST_KEY = Keyword.intern(null, "const");
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 READEVAL = Var.intern(CLOJURE_NS, Symbol.intern("*read-eval*"), F).setDynamic();
final static public Var READWHITELIST = Var.intern(CLOJURE_NS, Symbol.intern("*read-whitelist*"),
RT.vector(java.lang.Number.class,
java.util.Collection.class,
java.util.Map.class,
clojure.lang.Namespace.class,
clojure.lang.Fn.class)).setDynamic();
final static public Var DATA_READERS = Var.intern(CLOJURE_NS, Symbol.intern("*data-readers*"), RT.map()).setDynamic();
final static public Var DEFAULT_DATA_READER_FN = Var.intern(CLOJURE_NS, Symbol.intern("*default-data-reader-fn*"), RT.map()).setDynamic();
final static public Var DEFAULT_DATA_READERS = Var.intern(CLOJURE_NS, Symbol.intern("default-data-readers"), RT.map());
Expand Down Expand Up @@ -1861,8 +1867,9 @@ else if(x instanceof Character) {
}
}
else if(x instanceof Class) {
w.write("#=");
w.write("#=\"");
w.write(((Class) x).getName());
w.write('"');
}
else if(x instanceof BigDecimal && readably) {
w.write(x.toString());
Expand Down

0 comments on commit 974a64c

Please sign in to comment.