Skip to content

Commit 8c20834

Browse files
puredangerstuarthalloway
authored andcommitted
CLJ-1706 Make top level reader conditional splicing an error
Signed-off-by: Stuart Halloway <[email protected]>
1 parent 3aaa410 commit 8c20834

File tree

2 files changed

+36
-16
lines changed

2 files changed

+36
-16
lines changed

src/jvm/clojure/lang/LispReader.java

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -192,11 +192,19 @@ static public Object read(PushbackReader r, boolean eofIsError, Object eofValue,
192192

193193
static public Object read(PushbackReader r, boolean eofIsError, Object eofValue, boolean isRecursive, Object opts)
194194
{
195-
return read(r, eofIsError, eofValue, null, null, isRecursive, opts, new LinkedList());
195+
// start with pendingForms null as reader conditional splicing is not allowed at top level
196+
return read(r, eofIsError, eofValue, null, null, isRecursive, opts, null);
196197
}
197198

198199
static private Object read(PushbackReader r, boolean eofIsError, Object eofValue, boolean isRecursive, Object opts, Object pendingForms) {
199-
return read(r, eofIsError, eofValue, null, null, isRecursive, opts, pendingForms);
200+
return read(r, eofIsError, eofValue, null, null, isRecursive, opts, ensurePending(pendingForms));
201+
}
202+
203+
static private Object ensurePending(Object pendingForms) {
204+
if(pendingForms == null)
205+
return new LinkedList();
206+
else
207+
return pendingForms;
200208
}
201209

202210
static private Object installPlatformFeature(Object opts) {
@@ -584,7 +592,7 @@ public Object invoke(Object reader, Object semicolon, Object opts, Object pendin
584592
public static class DiscardReader extends AFn{
585593
public Object invoke(Object reader, Object underscore, Object opts, Object pendingForms) {
586594
PushbackReader r = (PushbackReader) reader;
587-
read(r, true, null, true, opts, pendingForms);
595+
read(r, true, null, true, opts, ensurePending(pendingForms));
588596
return r;
589597
}
590598
}
@@ -598,7 +606,7 @@ public WrappingReader(Symbol sym){
598606

599607
public Object invoke(Object reader, Object quote, Object opts, Object pendingForms) {
600608
PushbackReader r = (PushbackReader) reader;
601-
Object o = read(r, true, null, true, opts, pendingForms);
609+
Object o = read(r, true, null, true, opts, ensurePending(pendingForms));
602610
return RT.list(sym, o);
603611
}
604612

@@ -618,7 +626,7 @@ public Object invoke(Object reader, Object quote, Object opts, Object pendingFor
618626
" is deprecated; use " + sym.getName() +
619627
" instead");
620628
PushbackReader r = (PushbackReader) reader;
621-
Object o = read(r, true, null, true, opts, pendingForms);
629+
Object o = read(r, true, null, true, opts, ensurePending(pendingForms));
622630
return RT.list(sym, o);
623631
}
624632

@@ -627,7 +635,7 @@ public Object invoke(Object reader, Object quote, Object opts, Object pendingFor
627635
public static class VarReader extends AFn{
628636
public Object invoke(Object reader, Object quote, Object opts, Object pendingForms) {
629637
PushbackReader r = (PushbackReader) reader;
630-
Object o = read(r, true, null, true, opts, pendingForms);
638+
Object o = read(r, true, null, true, opts, ensurePending(pendingForms));
631639
// if(o instanceof Symbol)
632640
// {
633641
// Object v = Compiler.maybeResolveIn(Compiler.currentNS(), (Symbol) o);
@@ -672,6 +680,7 @@ public Object invoke(Object reader, Object hash, Object opts, Object pendingForm
672680
// Try the ctor reader first
673681
if(fn == null) {
674682
unread((PushbackReader) reader, ch);
683+
pendingForms = ensurePending(pendingForms);
675684
Object result = ctorReader.invoke(reader, ch, opts, pendingForms);
676685

677686
if(result != null)
@@ -697,7 +706,7 @@ public Object invoke(Object reader, Object lparen, Object opts, Object pendingFo
697706
Var.pushThreadBindings(
698707
RT.map(ARG_ENV, PersistentTreeMap.EMPTY));
699708
unread(r, '(');
700-
Object form = read(r, true, null, true, opts, pendingForms);
709+
Object form = read(r, true, null, true, opts, ensurePending(pendingForms));
701710

702711
PersistentVector args = PersistentVector.EMPTY;
703712
PersistentTreeMap argsyms = (PersistentTreeMap) ARG_ENV.deref();
@@ -760,7 +769,7 @@ public Object invoke(Object reader, Object pct, Object opts, Object pendingForms
760769
{
761770
return registerArg(1);
762771
}
763-
Object n = read(r, true, null, true, opts, pendingForms);
772+
Object n = read(r, true, null, true, opts, ensurePending(pendingForms));
764773
if(n.equals(Compiler._AMP_))
765774
return registerArg(-1);
766775
if(!(n instanceof Number))
@@ -779,6 +788,7 @@ public Object invoke(Object reader, Object caret, Object opts, Object pendingFor
779788
line = ((LineNumberingPushbackReader) r).getLineNumber();
780789
column = ((LineNumberingPushbackReader) r).getColumnNumber()-1;
781790
}
791+
pendingForms = ensurePending(pendingForms);
782792
Object meta = read(r, true, null, true, opts, pendingForms);
783793
if(meta instanceof Symbol || meta instanceof String)
784794
meta = RT.map(RT.TAG_KEY, meta);
@@ -820,7 +830,7 @@ public Object invoke(Object reader, Object backquote, Object opts, Object pendin
820830
Var.pushThreadBindings(
821831
RT.map(GENSYM_ENV, PersistentHashMap.EMPTY));
822832

823-
Object form = read(r, true, null, true, opts, pendingForms);
833+
Object form = read(r, true, null, true, opts, ensurePending(pendingForms));
824834
return syntaxQuote(form);
825835
}
826836
finally
@@ -967,6 +977,7 @@ public Object invoke(Object reader, Object comma, Object opts, Object pendingFor
967977
int ch = read1(r);
968978
if(ch == -1)
969979
throw Util.runtimeException("EOF while reading character");
980+
pendingForms = ensurePending(pendingForms);
970981
if(ch == '@')
971982
{
972983
Object o = read(r, true, null, true, opts, pendingForms);
@@ -1035,7 +1046,7 @@ public Object invoke(Object reader, Object leftparen, Object opts, Object pendin
10351046
line = ((LineNumberingPushbackReader) r).getLineNumber();
10361047
column = ((LineNumberingPushbackReader) r).getColumnNumber()-1;
10371048
}
1038-
List list = readDelimitedList(')', r, true, opts, pendingForms);
1049+
List list = readDelimitedList(')', r, true, opts, ensurePending(pendingForms));
10391050
if(list.isEmpty())
10401051
return PersistentList.EMPTY;
10411052
IObj s = (IObj) PersistentList.create(list);
@@ -1090,7 +1101,7 @@ public Object invoke(Object reader, Object eq, Object opts, Object pendingForms)
10901101
}
10911102

10921103
PushbackReader r = (PushbackReader) reader;
1093-
Object o = read(r, true, null, true, opts, pendingForms);
1104+
Object o = read(r, true, null, true, opts, ensurePending(pendingForms));
10941105
if(o instanceof Symbol)
10951106
{
10961107
return RT.classForName(o.toString());
@@ -1136,15 +1147,15 @@ else if(o instanceof IPersistentList)
11361147
public static class VectorReader extends AFn{
11371148
public Object invoke(Object reader, Object leftparen, Object opts, Object pendingForms) {
11381149
PushbackReader r = (PushbackReader) reader;
1139-
return LazilyPersistentVector.create(readDelimitedList(']', r, true, opts, pendingForms));
1150+
return LazilyPersistentVector.create(readDelimitedList(']', r, true, opts, ensurePending(pendingForms)));
11401151
}
11411152

11421153
}
11431154

11441155
public static class MapReader extends AFn{
11451156
public Object invoke(Object reader, Object leftparen, Object opts, Object pendingForms) {
11461157
PushbackReader r = (PushbackReader) reader;
1147-
Object[] a = readDelimitedList('}', r, true, opts, pendingForms).toArray();
1158+
Object[] a = readDelimitedList('}', r, true, opts, ensurePending(pendingForms)).toArray();
11481159
if((a.length & 1) == 1)
11491160
throw Util.runtimeException("Map literal must contain an even number of forms");
11501161
return RT.map(a);
@@ -1155,7 +1166,7 @@ public Object invoke(Object reader, Object leftparen, Object opts, Object pendin
11551166
public static class SetReader extends AFn{
11561167
public Object invoke(Object reader, Object leftbracket, Object opts, Object pendingForms) {
11571168
PushbackReader r = (PushbackReader) reader;
1158-
return PersistentHashSet.createWithCheck(readDelimitedList('}', r, true, opts, pendingForms));
1169+
return PersistentHashSet.createWithCheck(readDelimitedList('}', r, true, opts, ensurePending(pendingForms)));
11591170
}
11601171

11611172
}
@@ -1204,6 +1215,7 @@ public static List readDelimitedList(char delim, PushbackReader r, boolean isRec
12041215
public static class CtorReader extends AFn{
12051216
public Object invoke(Object reader, Object firstChar, Object opts, Object pendingForms){
12061217
PushbackReader r = (PushbackReader) reader;
1218+
pendingForms = ensurePending(pendingForms);
12071219
Object name = read(r, true, null, false, opts, pendingForms);
12081220
if (!(name instanceof Symbol))
12091221
throw new RuntimeException("Reader tag must be a symbol");
@@ -1320,6 +1332,8 @@ public static boolean hasFeature(Object feature, Object opts) {
13201332
public static Object readCondDelimited(PushbackReader r, boolean splicing, Object opts, Object pendingForms) {
13211333
Object result = null;
13221334
Object form; // The most recently ready form
1335+
boolean toplevel = (pendingForms == null);
1336+
pendingForms = ensurePending(pendingForms);
13231337

13241338
final int firstline =
13251339
(r instanceof LineNumberingPushbackReader) ?
@@ -1391,6 +1405,9 @@ public static Object readCondDelimited(PushbackReader r, boolean splicing, Objec
13911405
if(! (result instanceof List))
13921406
throw Util.runtimeException("Spliced form list in read-cond-splicing must implement java.util.List");
13931407

1408+
if(toplevel)
1409+
throw Util.runtimeException("Reader conditional splicing not allowed at the top level.");
1410+
13941411
((List)pendingForms).addAll(0, (List)result);
13951412

13961413
return r;
@@ -1435,7 +1452,7 @@ public Object invoke(Object reader, Object mode, Object opts, Object pendingForm
14351452

14361453
if (isPreserveReadCond(opts)) {
14371454
IFn listReader = getMacro(ch); // should always be a list
1438-
Object form = listReader.invoke(r, ch, opts, pendingForms);
1455+
Object form = listReader.invoke(r, ch, opts, ensurePending(pendingForms));
14391456

14401457
return ReaderConditional.create(form, splicing);
14411458
} else {

test/clojure/test_clojure/reader.cljc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -692,7 +692,10 @@
692692
(is (thrown-with-msg? RuntimeException #"is reserved" (read-string {:read-cond :allow} "#?@(:foo :a :else :b)")))
693693
(is (thrown-with-msg? RuntimeException #"must be a list" (read-string {:read-cond :allow} "#?[:foo :a :else :b]")))
694694
(is (thrown-with-msg? RuntimeException #"Conditional read not allowed" (read-string {:read-cond :BOGUS} "#?[:clj :a :default nil]")))
695-
(is (thrown-with-msg? RuntimeException #"Conditional read not allowed" (read-string "#?[:clj :a :default nil]"))))
695+
(is (thrown-with-msg? RuntimeException #"Conditional read not allowed" (read-string "#?[:clj :a :default nil]")))
696+
(is (thrown-with-msg? RuntimeException #"Reader conditional splicing not allowed at the top level" (read-string {:read-cond :allow} "#?@(:clj [1 2])")))
697+
(is (thrown-with-msg? RuntimeException #"Reader conditional splicing not allowed at the top level" (read-string {:read-cond :allow} "#?@(:clj [1])")))
698+
(is (thrown-with-msg? RuntimeException #"Reader conditional splicing not allowed at the top level" (read-string {:read-cond :allow} "#?@(:clj []) 1"))))
696699
(testing "clj-1698-regression"
697700
(let [opts {:features #{:clj} :read-cond :allow}]
698701
(is (= 1 (read-string opts "#?(:cljs {'a 1 'b 2} :clj 1)")))

0 commit comments

Comments
 (0)