Index: runtime/lib/convert_patch.dart |
diff --git a/runtime/lib/convert_patch.dart b/runtime/lib/convert_patch.dart |
index 7b143accb17ddee8097c2f7dbde0b119ab7d5446..6d839699f004004fe54fe5e588ddd31d6ea0d48a 100644 |
--- a/runtime/lib/convert_patch.dart |
+++ b/runtime/lib/convert_patch.dart |
@@ -31,7 +31,7 @@ patch class Utf8Decoder { |
return super.fuse(next); |
} |
- // Allow intercepting of UTF8 decoding when builtin lists are passed. |
+ // Allow intercepting of UTF-8 decoding when built-in lists are passed. |
/* patch */ |
static String _convertIntercepted( |
bool allowMalformed, List<int> codeUnits, int start, int end) { |
@@ -77,6 +77,13 @@ abstract class _JsonListener { |
void beginArray() {} |
void arrayElement() {} |
void endArray() {} |
+ |
+ /** |
+ * Read out the final result of parsing a JSON string. |
+ * |
+ * Must only be called when the entire input has been parsed. |
+ */ |
+ get result; |
} |
/** |
@@ -268,38 +275,41 @@ abstract class _ChunkedJsonParser { |
// gets to the next state (not empty, doesn't allow a value). |
// State building-block constants. |
- static const int INSIDE_ARRAY = 1; |
- static const int INSIDE_OBJECT = 2; |
- static const int AFTER_COLON = 3; // Always inside object. |
+ static const int TOP_LEVEL = 0; |
+ static const int INSIDE_ARRAY = 1; |
+ static const int INSIDE_OBJECT = 2; |
+ static const int AFTER_COLON = 3; // Always inside object. |
static const int ALLOW_STRING_MASK = 8; // Allowed if zero. |
- static const int ALLOW_VALUE_MASK = 4; // Allowed if zero. |
- static const int ALLOW_VALUE = 0; |
- static const int STRING_ONLY = 4; |
- static const int NO_VALUES = 12; |
+ static const int ALLOW_VALUE_MASK = 4; // Allowed if zero. |
+ static const int ALLOW_VALUE = 0; |
+ static const int STRING_ONLY = 4; |
+ static const int NO_VALUES = 12; |
// Objects and arrays are "empty" until their first property/element. |
// At this position, they may either have an entry or a close-bracket. |
- static const int EMPTY = 0; |
- static const int NON_EMPTY = 16; |
- static const int EMPTY_MASK = 16; // Empty if zero. |
+ static const int EMPTY = 0; |
+ static const int NON_EMPTY = 16; |
+ static const int EMPTY_MASK = 16; // Empty if zero. |
- static const int VALUE_READ_BITS = NO_VALUES | NON_EMPTY; |
+ // Actual states : Context | Is empty? | Next? |
+ static const int STATE_INITIAL = TOP_LEVEL | EMPTY | ALLOW_VALUE; |
+ static const int STATE_END = TOP_LEVEL | NON_EMPTY | NO_VALUES; |
- // Actual states. |
- static const int STATE_INITIAL = EMPTY | ALLOW_VALUE; |
- static const int STATE_END = NON_EMPTY | NO_VALUES; |
+ static const int STATE_ARRAY_EMPTY = INSIDE_ARRAY | EMPTY | ALLOW_VALUE; |
+ static const int STATE_ARRAY_VALUE = INSIDE_ARRAY | NON_EMPTY | NO_VALUES; |
+ static const int STATE_ARRAY_COMMA = INSIDE_ARRAY | NON_EMPTY | ALLOW_VALUE; |
- static const int STATE_ARRAY_EMPTY = INSIDE_ARRAY | EMPTY | ALLOW_VALUE; |
- static const int STATE_ARRAY_VALUE = INSIDE_ARRAY | NON_EMPTY | NO_VALUES; |
- static const int STATE_ARRAY_COMMA = INSIDE_ARRAY | NON_EMPTY | ALLOW_VALUE; |
- |
- static const int STATE_OBJECT_EMPTY = INSIDE_OBJECT | EMPTY | STRING_ONLY; |
+ static const int STATE_OBJECT_EMPTY = INSIDE_OBJECT | EMPTY | STRING_ONLY; |
static const int STATE_OBJECT_KEY = INSIDE_OBJECT | NON_EMPTY | NO_VALUES; |
- static const int STATE_OBJECT_COLON = AFTER_COLON | NON_EMPTY | ALLOW_VALUE; |
- static const int STATE_OBJECT_VALUE = AFTER_COLON | NON_EMPTY | NO_VALUES; |
+ static const int STATE_OBJECT_COLON = AFTER_COLON | NON_EMPTY | ALLOW_VALUE; |
+ static const int STATE_OBJECT_VALUE = AFTER_COLON | NON_EMPTY | NO_VALUES; |
static const int STATE_OBJECT_COMMA = INSIDE_OBJECT | NON_EMPTY | STRING_ONLY; |
+ // Bits set in state after successfully reading a value. |
+ // This transitions the state to expect the next punctuation. |
+ static const int VALUE_READ_BITS = NON_EMPTY | NO_VALUES; |
+ |
// Character code constants. |
static const int BACKSPACE = 0x08; |
static const int TAB = 0x09; |
@@ -334,40 +344,40 @@ abstract class _ChunkedJsonParser { |
static const int RBRACE = 0x7d; |
// State of partial value at chunk split. |
- static const int NO_PARTIAL = 0; |
- static const int PARTIAL_STRING = 1; |
- static const int PARTIAL_NUMERAL = 2; |
- static const int PARTIAL_KEYWORD = 3; |
- static const int MASK_PARTIAL = 3; |
+ static const int NO_PARTIAL = 0; |
+ static const int PARTIAL_STRING = 1; |
+ static const int PARTIAL_NUMERAL = 2; |
+ static const int PARTIAL_KEYWORD = 3; |
+ static const int MASK_PARTIAL = 3; |
// Partial states for numerals. Values can be |'ed with PARTIAL_NUMERAL. |
- static const int NUM_SIGN = 0; // After initial '-'. |
- static const int NUM_ZERO = 4; // After '0' as first digit. |
- static const int NUM_DIGIT = 8; // After digit, no '.' or 'e' seen. |
- static const int NUM_DOT = 12; // After '.'. |
- static const int NUM_DOT_DIGIT = 16; // After a decimal digit (after '.'). |
- static const int NUM_E = 20; // After 'e' or 'E'. |
- static const int NUM_E_SIGN = 24; // After '-' or '+' after 'e' or 'E'. |
- static const int NUM_E_DIGIT = 28; // After exponent digit. |
- static const int NUM_SUCCESS = 32; // Never stored as partial state. |
+ static const int NUM_SIGN = 0; // After initial '-'. |
+ static const int NUM_ZERO = 4; // After '0' as first digit. |
+ static const int NUM_DIGIT = 8; // After digit, no '.' or 'e' seen. |
+ static const int NUM_DOT = 12; // After '.'. |
+ static const int NUM_DOT_DIGIT = 16; // After a decimal digit (after '.'). |
+ static const int NUM_E = 20; // After 'e' or 'E'. |
+ static const int NUM_E_SIGN = 24; // After '-' or '+' after 'e' or 'E'. |
+ static const int NUM_E_DIGIT = 28; // After exponent digit. |
+ static const int NUM_SUCCESS = 32; // Never stored as partial state. |
// Partial states for strings. |
static const int STR_PLAIN = 0; // Inside string, but not escape. |
static const int STR_ESCAPE = 4; // After '\'. |
- static const int STR_U = 16; // After '\u' and 0-3 hex digits. |
+ static const int STR_U = 16; // After '\u' and 0-3 hex digits. |
static const int STR_U_COUNT_SHIFT = 2; // Hex digit count in bits 2-3. |
static const int STR_U_VALUE_SHIFT = 5; // Hex digit value in bits 5+. |
// Partial states for keywords. |
static const int KWD_TYPE_MASK = 12; |
- static const int KWD_TYPE_SHIFT = 2; |
- static const int KWD_NULL = 0; // Prefix of "null" seen. |
- static const int KWD_TRUE = 4; // Prefix of "true" seen. |
- static const int KWD_FALSE = 8; // Prefix of "false" seen. |
- static const int KWD_COUNT_SHIFT = 4; // Prefix length in bits 4+. |
+ static const int KWD_TYPE_SHIFT = 2; |
+ static const int KWD_NULL = 0; // Prefix of "null" seen. |
+ static const int KWD_TRUE = 4; // Prefix of "true" seen. |
+ static const int KWD_FALSE = 8; // Prefix of "false" seen. |
+ static const int KWD_COUNT_SHIFT = 4; // Prefix length in bits 4+. |
// Mask used to mask off two lower bits. |
- static const int TWO_BIT_MASK = 3; |
+ static const int TWO_BIT_MASK = 3; |
final _JsonListener listener; |
@@ -514,7 +524,7 @@ abstract class _ChunkedJsonParser { |
* |
* Used for number buffer (always copies ASCII, so encoding is not important). |
*/ |
- void copyCharsToList(int start, int end, List<int> target); |
+ void copyCharsToList(int start, int end, List<int> target, int offset); |
/** |
* Build a string using input code units. |
@@ -592,7 +602,7 @@ abstract class _ChunkedJsonParser { |
assert(start < chunkEnd); |
int length = chunkEnd - start; |
var buffer = new _NumberBuffer(length); |
- copyCharsToList(start, chunkEnd, buffer.list); |
+ copyCharsToList(start, chunkEnd, buffer.list, 0); |
buffer.length = length; |
return buffer; |
} |
@@ -800,9 +810,6 @@ abstract class _ChunkedJsonParser { |
int state = this.state; |
while (position < length) { |
int char = getChar(position); |
- if (char == null) { |
- print("[[[$chunk]]] - $position - ${chunk.runtimeType}"); |
- } |
switch (char) { |
case SPACE: |
case CARRIAGE_RETURN: |
@@ -901,7 +908,7 @@ abstract class _ChunkedJsonParser { |
/** |
* Parses a "true" literal starting at [position]. |
* |
- * [:source[position]:] must be "t". |
+ * The character `source[position]` must be "t". |
*/ |
int parseTrue(int position) { |
assert(getChar(position) == CHAR_t); |
@@ -920,7 +927,7 @@ abstract class _ChunkedJsonParser { |
/** |
* Parses a "false" literal starting at [position]. |
* |
- * [:source[position]:] must be "f". |
+ * The character `source[position]` must be "f". |
*/ |
int parseFalse(int position) { |
assert(getChar(position) == CHAR_f); |
@@ -940,7 +947,7 @@ abstract class _ChunkedJsonParser { |
/** |
* Parses a "null" literal starting at [position]. |
* |
- * [:source[position]:] must be "n". |
+ * The character `source[position]` must be "n". |
*/ |
int parseNull(int position) { |
assert(getChar(position) == CHAR_n); |
@@ -1000,7 +1007,7 @@ abstract class _ChunkedJsonParser { |
return position; |
} |
if (char < SPACE) { |
- fail(position - 1, "Control character in string"); |
+ return fail(position - 1, "Control character in string"); |
} |
} |
beginString(); |
@@ -1059,8 +1066,7 @@ abstract class _ChunkedJsonParser { |
int char = getChar(position++); |
if (char > BACKSLASH) continue; |
if (char < SPACE) { |
- fail(position - 1); // Control character in string. |
- return; |
+ return fail(position - 1); // Control character in string. |
} |
if (char == QUOTE) { |
int quotePosition = position - 1; |
@@ -1171,7 +1177,7 @@ abstract class _ChunkedJsonParser { |
int finishChunkNumber(int state, int start, int end, _NumberBuffer buffer) { |
if (state == NUM_ZERO) { |
listener.handleNumber(0); |
- return; |
+ return end; |
} |
if (end > start) { |
addNumberChunk(buffer, start, end, 0); |
@@ -1201,86 +1207,84 @@ abstract class _ChunkedJsonParser { |
// Break this block when the end of the number literal is reached. |
// At that time, position points to the next character, and isDouble |
// is set if the literal contains a decimal point or an exponential. |
- parsing: { |
- if (char == MINUS) { |
- sign = -1; |
+ if (char == MINUS) { |
+ sign = -1; |
+ position++; |
+ if (position == length) return beginChunkNumber(NUM_SIGN, start); |
+ char = getChar(position); |
+ } |
+ int digit = char ^ CHAR_0; |
+ if (digit > 9) { |
+ if (sign < 0) { |
+ fail(position, "Missing expected digit"); |
+ } else { |
+ // If it doesn't even start out as a numeral. |
+ fail(position, "Unexpected character"); |
+ } |
+ } |
+ if (digit == 0) { |
+ position++; |
+ if (position == length) return beginChunkNumber(NUM_ZERO, start); |
+ char = getChar(position); |
+ digit = char ^ CHAR_0; |
+ // If starting with zero, next character must not be digit. |
+ if (digit <= 9) fail(position); |
+ } else { |
+ do { |
+ intValue = 10 * intValue + digit; |
position++; |
- if (position == length) return beginChunkNumber(NUM_SIGN, start); |
+ if (position == length) return beginChunkNumber(NUM_DIGIT, start); |
char = getChar(position); |
- } |
- int digit = char ^ CHAR_0; |
- if (digit > 9) { |
- if (sign < 0) { |
- fail(position, "Missing expected digit"); |
- } else { |
- // If it doesn't even start out as a numeral. |
- fail(position, "Unexpected character"); |
- } |
- } |
- if (digit == 0) { |
+ digit = char ^ CHAR_0; |
+ } while (digit <= 9); |
+ } |
+ if (char == DECIMALPOINT) { |
+ isDouble = true; |
+ doubleValue = intValue.toDouble(); |
+ intValue = 0; |
+ position++; |
+ if (position == length) return beginChunkNumber(NUM_DOT, start); |
+ char = getChar(position); |
+ digit = char ^ CHAR_0; |
+ if (digit > 9) fail(position); |
+ do { |
+ doubleValue = 10.0 * doubleValue + digit; |
+ intValue -= 1; |
position++; |
- if (position == length) return beginChunkNumber(NUM_ZERO, start); |
+ if (position == length) return beginChunkNumber(NUM_DOT_DIGIT, start); |
char = getChar(position); |
digit = char ^ CHAR_0; |
- // If starting with zero, next character must not be digit. |
- if (digit <= 9) fail(position); |
- } else { |
- do { |
- intValue = 10 * intValue + digit; |
- position++; |
- if (position == length) return beginChunkNumber(NUM_DIGIT, start); |
- char = getChar(position); |
- digit = char ^ CHAR_0; |
- } while (digit <= 9); |
- } |
- if (char == DECIMALPOINT) { |
- isDouble = true; |
+ } while (digit <= 9); |
+ } |
+ if ((char | 0x20) == CHAR_e) { |
+ if (!isDouble) { |
doubleValue = intValue.toDouble(); |
intValue = 0; |
+ isDouble = true; |
+ } |
+ position++; |
+ if (position == length) return beginChunkNumber(NUM_E, start); |
+ char = getChar(position); |
+ int expSign = 1; |
+ int exponent = 0; |
+ if (char == PLUS || char == MINUS) { |
+ expSign = 0x2C - char; // -1 for MINUS, +1 for PLUS |
position++; |
- if (position == length) return beginChunkNumber(NUM_DOT, start); |
+ if (position == length) return beginChunkNumber(NUM_E_SIGN, start); |
char = getChar(position); |
- digit = char ^ CHAR_0; |
- if (digit > 9) fail(position); |
- do { |
- doubleValue = 10.0 * doubleValue + digit; |
- intValue -= 1; |
- position++; |
- if (position == length) return beginChunkNumber(NUM_DOT_DIGIT, start); |
- char = getChar(position); |
- digit = char ^ CHAR_0; |
- } while (digit <= 9); |
} |
- if ((char | 0x20) == CHAR_e) { |
- if (!isDouble) { |
- doubleValue = intValue.toDouble(); |
- intValue = 0; |
- isDouble = true; |
- } |
+ digit = char ^ CHAR_0; |
+ if (digit > 9) { |
+ fail(position, "Missing expected digit"); |
+ } |
+ do { |
+ exponent = 10 * exponent + digit; |
position++; |
- if (position == length) return beginChunkNumber(NUM_E, start); |
+ if (position == length) return beginChunkNumber(NUM_E_DIGIT, start); |
char = getChar(position); |
- int expSign = 1; |
- int exponent = 0; |
- if (char == PLUS || char == MINUS) { |
- expSign = 0x2C - char; // -1 for MINUS, +1 for PLUS |
- position++; |
- if (position == length) return beginChunkNumber(NUM_E_SIGN, start); |
- char = getChar(position); |
- } |
digit = char ^ CHAR_0; |
- if (digit > 9) { |
- fail(position, "Missing expected digit"); |
- } |
- do { |
- exponent = 10 * exponent + digit; |
- position++; |
- if (position == length) return beginChunkNumber(NUM_E_DIGIT, start); |
- char = getChar(position); |
- digit = char ^ CHAR_0; |
- } while (digit <= 9); |
- intValue += expSign * exponent; |
- } |
+ } while (digit <= 9); |
+ intValue += expSign * exponent; |
} |
if (!isDouble) { |
listener.handleNumber(sign * intValue); |
@@ -1314,7 +1318,7 @@ abstract class _ChunkedJsonParser { |
return position; |
} |
- int fail(int position, [String message]) { |
+ fail(int position, [String message]) { |
if (message == null) { |
message = "Unexpected character"; |
if (position == chunkEnd) message = "Unexpected end of input"; |