diff --git a/src/org/rascalmpl/exceptions/RuntimeExceptionFactory.java b/src/org/rascalmpl/exceptions/RuntimeExceptionFactory.java index 335e46da2e..fbb88d3653 100644 --- a/src/org/rascalmpl/exceptions/RuntimeExceptionFactory.java +++ b/src/org/rascalmpl/exceptions/RuntimeExceptionFactory.java @@ -99,6 +99,9 @@ public class RuntimeExceptionFactory { // NotImplemented public static final Type ParseError = TF.constructor(TS, Exception, "ParseError", TF.sourceLocationType(), "location"); + // this comes from lang::json::IO + public static final Type NoOffsetParseError = TF.constructor(TS, Exception, "NoOffsetParseError", TF.sourceLocationType(), "location", TF.integerType(), "line", TF.integerType(), "column"); + public static final Type PathNotFound = TF.constructor(TS,Exception,"PathNotFound",TF.sourceLocationType(), "location"); public static final Type PermissionDenied = TF.constructor(TS,Exception,"PermissionDenied",TF.stringType(), "message"); @@ -684,7 +687,12 @@ public static Throw jsonParseError(ISourceLocation loc, String cause, String pat .asWithKeywordParameters().setParameter("path", VF.string(path))); } - + public static Throw jsonParseError(ISourceLocation file, int line, int col, String cause, String path) { + return new Throw(VF.constructor(NoOffsetParseError, file, VF.integer(line), VF.integer(col)) + .asWithKeywordParameters().setParameter("reason", VF.string(cause)) + .asWithKeywordParameters().setParameter("path", VF.string(path))); + } + public static Throw parseError(ISourceLocation loc, AbstractAST ast, StackTrace trace) { return new Throw(VF.constructor(ParseError, loc), ast != null ? ast.getLocation() : null, trace); } @@ -793,4 +801,6 @@ public static Throw parseErrorRecovery(IValue trigger, ISourceLocation loc) { public static Throw parseErrorRecoveryNoSuchField(String name, ISourceLocation loc) { return new Throw(VF.constructor(ParseErrorRecovery, VF.constructor(NoSuchField, VF.string(name)), loc)); } + + } diff --git a/src/org/rascalmpl/library/lang/json/IO.java b/src/org/rascalmpl/library/lang/json/IO.java index f359b3a92d..e5b91d89fd 100644 --- a/src/org/rascalmpl/library/lang/json/IO.java +++ b/src/org/rascalmpl/library/lang/json/IO.java @@ -5,13 +5,16 @@ * * Contributors: * - * * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI * Mark Hills - Mark.Hills@cwi.nl (CWI) * Arnold - * Lankamp - Arnold.Lankamp@cwi.nl * Bert Lisser - Bert.Lisser@cwi.nl + * * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI + * * Mark Hills - Mark.Hills@cwi.nl (CWI) + * * Arnold - Lankamp - Arnold.Lankamp@cwi.nl + * * Bert Lisser - Bert.Lisser@cwi.nl *******************************************************************************/ package org.rascalmpl.library.lang.json; import java.io.IOException; import java.io.OutputStreamWriter; +import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.nio.charset.Charset; @@ -38,7 +41,6 @@ import io.usethesource.vallang.type.Type; import io.usethesource.vallang.type.TypeStore; -import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; public class IO { @@ -51,63 +53,55 @@ public IO(IRascalValueFactory values, IRascalMonitor monitor) { this.monitor = monitor; } - public IValue readJSON(IValue type, ISourceLocation loc, IString dateTimeFormat, IBool lenient, IBool trackOrigins, - IFunction parsers, IMap nulls, IBool explicitConstructorNames, IBool explicitDataTypes) { + private IValue doReadJSON(Reader in, + IValue type, ISourceLocation loc, IString dateTimeFormat, IBool lenient, IBool trackOrigins, + IFunction parsers, IMap nulls, IBool explicitConstructorNames, IBool explicitDataTypes) throws IOException { + TypeStore store = new TypeStore(); Type start = new TypeReifier(values).valueToType((IConstructor) type, store); - + if (parsers.getType() instanceof ReifiedType && parsers.getType().getTypeParameters().getFieldType(0).isTop()) { // ignore the default parser parsers = null; } - try (JsonReader in = new JsonReader(URIResolverRegistry.getInstance().getCharacterReader(loc))) { - in.setLenient(lenient.getValue()); + try { return new JsonValueReader(values, store, monitor, loc) - .setCalendarFormat(dateTimeFormat.getValue()) - .setParsers(parsers) - .setNulls(unreify(nulls)) - .setExplicitConstructorNames(explicitConstructorNames.getValue()) - .setExplicitDataTypes(explicitDataTypes.getValue()) - .setTrackOrigins(trackOrigins.getValue()) - .read(in, start); - } - catch (IOException e) { - throw RuntimeExceptionFactory.io(e); + .setCalendarFormat(dateTimeFormat.getValue()) + .setLenient(lenient.getValue()) + .setParsers(parsers) + .setNulls(unreify(nulls)) + .setExplicitConstructorNames(explicitConstructorNames.getValue()) + .setExplicitDataTypes(explicitDataTypes.getValue()) + .setTrackOrigins(trackOrigins.getValue()) + .read(in, start); } catch (NullPointerException e) { throw RuntimeExceptionFactory.io("NPE in error handling code"); } } + + public IValue readJSON( + IValue type, ISourceLocation loc, IString dateTimeFormat, IBool lenient, IBool trackOrigins, + IFunction parsers, IMap nulls, IBool explicitConstructorNames, IBool explicitDataTypes) { - private Map unreify(IMap nulls) { - var tr = new TypeReifier(values); - return nulls.stream().map(t -> (ITuple) t) - .collect(Collectors.toMap(t -> tr.valueToType((IConstructor) t.get(0)), t -> t.get(1))); + try (Reader in = URIResolverRegistry.getInstance().getCharacterReader(loc)) { + return doReadJSON(in, type, loc, dateTimeFormat, lenient, trackOrigins, parsers, nulls, explicitConstructorNames, explicitDataTypes); + } + catch (IOException e) { + throw RuntimeExceptionFactory.io(e); + } } public IValue parseJSON(IValue type, IString src, IString dateTimeFormat, IBool lenient, IBool trackOrigins, IFunction parsers, IMap nulls, IBool explicitConstructorNames, IBool explicitDataTypes) { - TypeStore store = new TypeStore(); - Type start = new TypeReifier(values).valueToType((IConstructor) type, store); - - try (JsonReader in = new JsonReader(new StringReader(src.getValue()))) { - in.setLenient(lenient.getValue()); - return new JsonValueReader(values, store, monitor,null) - .setCalendarFormat(dateTimeFormat.getValue()) - .setParsers(parsers) - .setNulls(unreify(nulls)) - .setTrackOrigins(trackOrigins.getValue()) - .setExplicitConstructorNames(explicitConstructorNames.getValue()) - .setExplicitDataTypes(explicitDataTypes.getValue()) - .read(in, start); + + try (Reader in = new StringReader(src.getValue())) { + return doReadJSON(in, type, null, dateTimeFormat, lenient, trackOrigins, parsers, nulls, explicitConstructorNames, explicitDataTypes); } catch (IOException e) { throw RuntimeExceptionFactory.io(e); } - catch (NullPointerException e) { - throw RuntimeExceptionFactory.io("NPE"); - } } public void writeJSON(ISourceLocation loc, IValue value, IBool unpackedLocations, IString dateTimeFormat, @@ -162,4 +156,10 @@ public IString asJSON(IValue value, IBool unpackedLocations, IString dateTimeFor throw RuntimeExceptionFactory.io(e); } } + + private Map unreify(IMap nulls) { + var tr = new TypeReifier(values); + return nulls.stream().map(t -> (ITuple) t) + .collect(Collectors.toMap(t -> tr.valueToType((IConstructor) t.get(0)), t -> t.get(1))); + } } diff --git a/src/org/rascalmpl/library/lang/json/IO.rsc b/src/org/rascalmpl/library/lang/json/IO.rsc index 8c71c94ed7..2198c8e28d 100644 --- a/src/org/rascalmpl/library/lang/json/IO.rsc +++ b/src/org/rascalmpl/library/lang/json/IO.rsc @@ -74,7 +74,14 @@ import Exception; * `cause` is a factual diagnosis of what was expected at that position, versus what was found. * `path` is a path query string into the JSON value from the root down to the leaf where the error was detected. } -data RuntimeException(str cause="", str path=""); +@benefits{ +* ((NoOffsetParseError)) is for when accurate offset tracking is turned off. Typically this is _on_ +even if `trackOrigins=false`, when we call the json parsers from Rascal. +} +data RuntimeException(str cause="", str path="") + = ParseError(loc location) + | NoOffsetParseError(loc location, int line, int column) + ; private str DEFAULT_DATETIME_FORMAT = "yyyy-MM-dd\'T\'HH:mm:ssZ"; diff --git a/src/org/rascalmpl/library/lang/json/internal/JsonValueReader.java b/src/org/rascalmpl/library/lang/json/internal/JsonValueReader.java index 5af18d864d..19397d1d28 100644 --- a/src/org/rascalmpl/library/lang/json/internal/JsonValueReader.java +++ b/src/org/rascalmpl/library/lang/json/internal/JsonValueReader.java @@ -23,7 +23,9 @@ package org.rascalmpl.library.lang.json.internal; import java.io.EOFException; +import java.io.FilterReader; import java.io.IOException; +import java.io.Reader; import java.io.StringReader; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; @@ -63,6 +65,7 @@ import io.usethesource.vallang.type.TypeStore; import com.google.gson.JsonParseException; +import com.google.gson.Strictness; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.MalformedJsonException; @@ -75,30 +78,50 @@ public class JsonValueReader { private static final TypeFactory TF = TypeFactory.getInstance(); private final TypeStore store; - private final IValueFactory vf; - private ThreadLocal format; + private final IValueFactory vf; private final IRascalMonitor monitor; - private ISourceLocation src; - private boolean trackOrigins = false; - private boolean disableTracking = false; + private final ISourceLocation src; private VarHandle posHandler; private VarHandle lineHandler; private VarHandle lineStartHandler; + + /* options */ + private ThreadLocal format; + private boolean trackOrigins = false; + private boolean stopTracking = false; private boolean explicitConstructorNames; private boolean explicitDataTypes; + private boolean lenient; private IFunction parsers; private Map nulls = Collections.emptyMap(); + private final class ExpectedTypeDispatcher implements ITypeVisitor { private final JsonReader in; + private final OriginTrackingReader tracker; + private int offset = 0; + private int readCount = 0; private int lastPos = 0; - private int lastLimit = 0; - private boolean stopTracking = true; /* origin tracking is turned off while we work on debugging the offsets on another PR */ + + /** + * In this mode we read directly from a given JsonReader, under which we can not + * encapsulate its Reader for counting offsets. This is used by the JSON-RPC bridge. + * @param in + */ + private ExpectedTypeDispatcher(JsonReader in) { + this.in = in; + this.tracker = null; + } - private ExpectedTypeDispatcher(JsonReader in, boolean noTracking) { + /** + * In this mode we have created an OriginTrackingReader which feeds the JsonReader from below. + * Accurate offsets can be tracked like this, which enables accurate error locations. When + * trackOrigins=true we get accurate origin src fields for objects. + */ + public ExpectedTypeDispatcher(JsonReader in, OriginTrackingReader tracker) { this.in = in; - this.stopTracking = true; // noTracking; NB origin tracking is turned off while we work on debugging the offsets on another PR + this.tracker = tracker; } @Override @@ -459,17 +482,21 @@ public IValue visitBool(Type type) throws IOException { * `internalPos < lastPos` will not have had the opportunity to evaluate to `true`. */ private int getPos() { + assert !(!stopTracking && posHandler == null) : "if we don't have the posHandler stopTracking should be true"; + if (stopTracking) { return 0; } var internalPos = (int) posHandler.get(in); - var internalLimit = getLimit(); + var trackerCount = tracker.getReadCount(); - if (internalPos < lastPos) { - // so we detected we are in trouble, but we do not have enough information for a solution here. - // TODO: fix this code in another PR by wrapping the CharacterReader. - offset = offset + (lastLimit - lastPos) + internalPos /* gson copies the tail of the buffer to the front */; + if (readCount < trackerCount) { + // the tracker indicates that `read` has happened and so the buffer has been rewound. + readCount = trackerCount; + // we learn from the tracker how far the offset is until the new first character in the buffer + // and we add the current offset since that reset to found our new current offset. + offset = tracker.getLimitOffset() + internalPos; } else { // the offset advances by the number of parsed characters @@ -478,9 +505,9 @@ private int getPos() { // save the previous state lastPos = internalPos; - lastLimit = internalLimit; try { + // never go below 0. might happen with a parse error at the first character. return Math.max(0, offset - 1); } catch (IllegalArgumentException | SecurityException e) { @@ -491,21 +518,6 @@ private int getPos() { } } - private int getLimit() { - if (stopTracking) { - return 0; - } - - try { - return (int) lineHandler.get(in) + 1; - } - catch (IllegalArgumentException | SecurityException e) { - // stop trying to recover the positions - stopTracking = true; - return 0; - } - } - private int getLine() { if (stopTracking) { return 0; @@ -549,8 +561,17 @@ protected Throw parseErrorHere(String cause) { int line = getLine(); int col = getCol(); - return RuntimeExceptionFactory - .jsonParseError(vf.sourceLocation(location, offset, 1, line, line, col, col + 1), cause, in.getPath()); + if (!stopTracking) { + return RuntimeExceptionFactory + .jsonParseError(vf.sourceLocation(location, offset, 1, line, line, col, col + 1), cause, in.getPath()); + } + else { + // if we didn't track the offset, we can at least produce line and column information, but not as a + // default Rascal ParseError with '0' or '-1' for offset, because that can trigger assertions and + // break other assumptions clients make about the source location values. + return RuntimeExceptionFactory + .jsonParseError(location, line, col, cause, in.getPath()); + } } /** @@ -735,12 +756,14 @@ else if (!explicitDataTypes && "_type".equals(label)) { } } - in.endObject(); int endPos = getPos(); - assert endPos >= startPos : "as assumpion on the internals of gson"; + assert endPos > startPos : "offset tracking messed up while stopTracking is " + stopTracking + " and trackOrigins is " + trackOrigins; + int endLine = getLine(); int endCol = getCol(); + in.endObject(); + for (int i = 0; i < args.length; i++) { if (args[i] == null) { throw parseErrorHere( @@ -829,11 +852,12 @@ public IValue visitNode(Type type) throws IOException { } } - in.endObject(); int endPos = getPos(); int endLine = getLine(); int endCol = getCol(); + in.endObject(); + if (trackOrigins && !stopTracking) { kws.put(kws.containsKey("src") ? "rascal-src" : "src", vf.sourceLocation(src, startPos, endPos - startPos + 1, startLine, endLine, startCol, endCol + 1)); @@ -898,6 +922,7 @@ public IValue visitList(Type type) throws IOException { } IListWriter w = vf.listWriter(); + getPos(); in.beginArray(); while (in.hasNext()) { // here we pass label from the higher context @@ -910,6 +935,7 @@ public IValue visitList(Type type) throws IOException { } in.endArray(); + getPos(); return w.done(); } @@ -959,24 +985,26 @@ public JsonValueReader(IValueFactory vf, TypeStore store, IRascalMonitor monitor this.store = store; this.monitor = monitor; this.src = src; - this.disableTracking = false; + this.stopTracking = false; setCalendarFormat("yyyy-MM-dd'T'HH:mm:ssZ"); - if (src != null) { - try { - var lookup = MethodHandles.lookup(); - var privateLookup = MethodHandles.privateLookupIn(JsonReader.class, lookup); - this.posHandler = privateLookup.findVarHandle(JsonReader.class, "pos", int.class); - this.lineHandler = privateLookup.findVarHandle(JsonReader.class, "lineNumber", int.class); - this.lineStartHandler = privateLookup.findVarHandle(JsonReader.class, "lineStart", int.class); - } - catch (NoSuchFieldException | SecurityException | IllegalAccessException e) { - // we disable the origin tracking if we can not get to the fields - disableTracking = true; - monitor.warning("Unable to retrieve origin information due to: " + e.getMessage(), src); + try { + var lookup = MethodHandles.lookup(); + var privateLookup = MethodHandles.privateLookupIn(JsonReader.class, lookup); + this.posHandler = privateLookup.findVarHandle(JsonReader.class, "pos", int.class); + this.lineHandler = privateLookup.findVarHandle(JsonReader.class, "lineNumber", int.class); + this.lineStartHandler = privateLookup.findVarHandle(JsonReader.class, "lineStart", int.class); + + if (posHandler == null) { + stopTracking = true; } } + catch (NoSuchFieldException | SecurityException | IllegalAccessException e) { + // we disable the origin tracking if we can not get to the fields + stopTracking = true; + monitor.warning("Unable to retrieve origin information due to: " + e.getMessage(), src); + } } public JsonValueReader(IValueFactory vf, IRascalMonitor monitor, ISourceLocation src) { @@ -1019,7 +1047,7 @@ public JsonValueReader(IRascalValueFactory vf, TypeStore store, IRascalMonitor m } catch (NoSuchFieldException | SecurityException | IllegalAccessException e) { // we disable the origin tracking if we can not get to the fields - disableTracking = true; + stopTracking = true; monitor.warning("Unable to retrieve origin information due to: " + e.getMessage(), src); } } @@ -1035,10 +1063,6 @@ public JsonValueReader setNulls(Map nulls) { public JsonValueReader setTrackOrigins(boolean trackOrigins) { this.trackOrigins = trackOrigins; - if (trackOrigins) { - monitor.warning("The origin tracking feature of the JSON parser is temporarily disabled.", src); - this.trackOrigins = false; - } return this; } @@ -1066,8 +1090,14 @@ public JsonValueReader setParsers(IFunction parsers) { return this; } + public JsonValueReader setLenient(boolean value) { + this.lenient = true; + return this; + } + /** - * Read and validate a Json stream as an IValue + * Read and validate a Json stream as an IValue. This version does not support accurate error messages + * or origin tracking. * * @param in json stream * @param expected type to validate against (recursively) @@ -1075,7 +1105,11 @@ public JsonValueReader setParsers(IFunction parsers) { * @throws IOException when either a parse error or a validation error occurs */ public IValue read(JsonReader in, Type expected) throws IOException { - var dispatch = new ExpectedTypeDispatcher(in, disableTracking); + in.setStrictness(lenient ? Strictness.LENIENT : Strictness.LEGACY_STRICT); + + // we can't track accurately because we don't have a handle to the raw buffer under `in` + this.stopTracking = true; + var dispatch = new ExpectedTypeDispatcher(in); try { var result = expected.accept(dispatch); @@ -1090,4 +1124,80 @@ public IValue read(JsonReader in, Type expected) throws IOException { throw dispatch.parseErrorHere(e.getMessage()); } } + + /** + * Read and validate a Json stream as an IValue. This version supports accurate error messages + * and origin tracking. + * + * @param in json stream + * @param expected type to validate against (recursively) + * @return an IValue of the expected type + * @throws IOException when either a parse error or a validation error occurs + */ + public IValue read(Reader in, Type expected) throws IOException { + try (OriginTrackingReader wrappedIn = new OriginTrackingReader(in); JsonReader jsonIn = new JsonReader(wrappedIn)) { + jsonIn.setStrictness(lenient ? Strictness.LENIENT : Strictness.LEGACY_STRICT); + + var dispatch = new ExpectedTypeDispatcher(jsonIn, wrappedIn); + + try { + var result = expected.accept(dispatch); + if (result == null) { + throw new JsonParseException( + "null occurred outside an optionality context and without a registered representation."); + } + return result; + } catch (EOFException | JsonParseException | NumberFormatException | MalformedJsonException | IllegalStateException | NullPointerException e) { + throw dispatch.parseErrorHere(e.getMessage()); + } + } + } + + /** + * This wraps a normal reader to make it possible for a client to detect accurate + * character offsets (> buffersize) in a large file, even if the underlying stream is buffered. + * + * This implementation is tightly coupled (semantically) with the internals of JsonReader. It provides + * just enough information, together with internal private fields of JsonReader, to compute Rascal-required + * offsets. We get only the character offset in the file, at the start of each streamed buffer contents. + * That should be just enough information to recompute the actual offset of every Json element, using the + * current position in the buffer. + */ + public static class OriginTrackingReader extends FilterReader { + // offset is always pointing at the point in the file where JsonReader.pos == 0 + private int offset = 0; + // limit is always pointing to the amount of no-junk characters in the underlying buffer below buffer.length + private int limit = 0; + // readCount increases by 1 with every call to `read` + private int readCount = 0; + + protected OriginTrackingReader(Reader in) { + super(in); + } + + @Override + public int read(char[] cbuf, int off, int len) throws IOException { + // we've read until here before we were reset to starting point `off`. + // the offset of the new current 0-based buffer starts here: + offset += limit - off; + + var charsRead = in.read(cbuf, off, len); + + // get the new limit (to where we've filled the buffer) + limit = charsRead + off; + + readCount++; + + // and return the number of characters read. + return charsRead; + } + + public int getLimitOffset() { + return offset; + } + + public int getReadCount() { + return readCount; + } + } } diff --git a/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/JSONIOTests.rsc b/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/JSONIOTests.rsc index 4ea773c85a..ecf73e70eb 100644 --- a/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/JSONIOTests.rsc +++ b/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/JSONIOTests.rsc @@ -106,10 +106,10 @@ bool originTest(loc example) { return true; } -@ignore{awaiting fix of 2633} test bool originTracking() { - return originTest(|std:///lang/rascal/tests/library/lang/json/glossary.json|) - && originTest(|std:///lang/rascal/tests/library/lang/json/testing.json|); + files = [ l | loc l <- |std:///lang/rascal/tests/library/lang/json|.ls, l.extension == "json"]; + + return (true | it && originTest(example) | example <- files, bprintln("testing origins of ")); } value numNormalizer(int i) = i % maxLong when abs(i) > maxLong; @@ -181,7 +181,6 @@ value toDefaultValue(real r) = r - round(r) == 0 : fitDouble(r); default value toDefaultValue(value x) = x; -@ignore{awaiting fix of 2633} test bool accurateParseErrors() { ex = readFile(|std:///lang/rascal/tests/library/lang/json/glossary.json|); broken = ex[..size(ex)/2] + ex[size(ex)/2+10..]; @@ -312,5 +311,37 @@ test bool explicitDataTypes() { // here we can't be sure to get z() back, but we will get some Enum assert data4(e=Enum _) := parseJSON(#DATA4, json, explicitDataTypes=false); + return true; +} + +data X(loc src=|unkown:///|) = v1(int x=0, str s = ""); + +test bool jsonVerifyOriginCorrect() { + ref = v1(x=123456789); + refExpected = asJSON(ref); + t1 = [v1(s="hoi"), ref]; + writeJSON(|memory:///test.json|, t1); + v = readJSON(#list[X],|memory:///test.json|, trackOrigins=true); + return refExpected == readFile(v[1].src); +} + +test bool jsonVerifyOriginCorrectAcrossBufferBoundaries() { + ref = v1(x=123456789); + refExpected = asJSON(ref); + for (sSize <- [900..1024]) { + println(sSize); + t1 = [v1(s="a<}>"), ref]; + writeJSON(|memory:///test.json|, t1); + + // this throws exceptions and asserts if there are bugs with the + // origin tracker. In particular it triggers #2633 + v = readJSON(#list[X],|memory:///test.json|, trackOrigins=true); + + // checking the last element + if (refExpected != readFile(v[1].src)) { + println("Failed for : "); + return false; + } + } return true; } \ No newline at end of file diff --git a/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/longcomment.json b/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/longcomment.json new file mode 100644 index 0000000000..001e102dca --- /dev/null +++ b/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/longcomment.json @@ -0,0 +1,8 @@ +// 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 +{ + "name": "this object has a very long comment of >1024 characters", + + "nested" : { + "type": "" + } +} \ No newline at end of file diff --git a/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/longstring.json b/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/longstring.json new file mode 100644 index 0000000000..a7830c82ab --- /dev/null +++ b/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/longstring.json @@ -0,0 +1,6 @@ +{ + "name": "this object has a very long string of 1024 characters", + "nested" : { + "type": "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" + } +} \ No newline at end of file diff --git a/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/testing.json b/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/testing.json index 4211534ee3..bb778c26d1 100644 --- a/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/testing.json +++ b/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/testing.json @@ -1,41 +1,42 @@ -{ - "name": "TESTING Adding one more character to this data causes the test to fail", - "type": "type_abcd", - "nested_abc": [ - { - "type": "line", - "property__1": { "y": { "value": 2, "absolute": false } }, - "property__2": {} - }, - { - "type": "line", - "property__1": { "y": { "value": 2, "absolute": false } }, - "property__2": {} - }, - { - "type": "line", - "property__1": { "y": { "value": 2, "absolute": false } }, - "property__2": {} - }, - { - "type": "line", - "property__1": { "y": { "value": 2, "absolute": false } }, - "property__2": {} - }, - { - "type": "line", - "property__1": { "y": { "value": 2, "absolute": false } }, - "property__2": {} - }, - { - "type": "line", - "property__1": { "y": { "value": 2, "absolute": false } }, - "property__2": {} - }, - { - "type": "line", - "property__1": { "y": { "value": 2, "absolute": false } }, - "property__2": {} - } - ] -} +// This comment pushes the length of the file beyong the 1024 limit. +{ + "name": "TESTING Adding one more character to this data caused the test to fail", + "type": "type_abcd", + "nested_abc": [ + { + "type": "line", + "property__1": { "y": { "value": 2, "absolute": false } }, + "property__2": {} + }, + { + "type": "line", + "property__1": { "y": { "value": 2, "absolute": false } }, + "property__2": {} + }, + { + "type": "line", + "property__1": { "y": { "value": 2, "absolute": false } }, + "property__2": {} + }, + { + "type": "line", + "property__1": { "y": { "value": 2, "absolute": false } }, + "property__2": {} + }, + { + "type": "line", + "property__1": { "y": { "value": 2, "absolute": false } }, + "property__2": {} + }, + { + "type": "line", + "property__1": { "y": { "value": 2, "absolute": false } }, + "property__2": {} + }, + { + "type": "line", + "property__1": { "y": { "value": 2, "absolute": false } }, + "property__2": {} + } + ] +}