Permalink
Browse files

Marked some things as final, reduced number of JSONToken instances cr…

…eated, Replaced "even" test of % 2 with & 1, Added additional test cases for decoding with unexpected end of input and made test pass previously the decoder would throw a null runtime error when trying to access an invalid token).
1 parent c937270 commit 064139a0ce1e0422bb6e23a03614157bc8d00edb @darronschall darronschall committed Nov 7, 2010
View
12 src/com/adobe/serialization/json/JSON.as
@@ -32,7 +32,7 @@
package com.adobe.serialization.json
{
-
+
/**
* This class provides encoding and decoding of the JSON format.
*
@@ -45,7 +45,7 @@ package com.adobe.serialization.json
* var myObject:Object = JSON.decode( jsonString );
* </code>
*/
- public class JSON
+ public final class JSON
{
/**
* Encodes a object into a JSON string.
@@ -57,13 +57,13 @@ package com.adobe.serialization.json
* @tiptext
*/
public static function encode( o:Object ):String
- {
+ {
return new JSONEncoder( o ).getString();
}
/**
* Decodes a JSON string into a native object.
- *
+ *
* @param s The JSON string representing the object
* @param strict Flag indicating if the decoder should strictly adhere
* to the JSON standard or not. The default of <code>true</code>
@@ -77,8 +77,8 @@ package com.adobe.serialization.json
* @tiptext
*/
public static function decode( s:String, strict:Boolean = true ):*
- {
- return new JSONDecoder( s, strict ).getValue();
+ {
+ return new JSONDecoder( s, strict ).getValue();
}
}
View
99 src/com/adobe/serialization/json/JSONDecoder.as
@@ -34,9 +34,9 @@ package com.adobe.serialization.json
{
public class JSONDecoder
- {
-
- /**
+ {
+
+ /**
* Flag indicating if the parser should be strict about the format
* of the JSON string it is attempting to decode.
*/
@@ -52,7 +52,7 @@ package com.adobe.serialization.json
private var token:JSONToken;
/**
- * Constructs a new JSONDecoder to parse a JSON string
+ * Constructs a new JSONDecoder to parse a JSON string
* into a native object.
*
* @param s The JSON string to be converted
@@ -64,7 +64,7 @@ package com.adobe.serialization.json
* @tiptext
*/
public function JSONDecoder( s:String, strict:Boolean )
- {
+ {
this.strict = strict;
tokenizer = new JSONTokenizer( s, strict );
@@ -97,23 +97,47 @@ package com.adobe.serialization.json
* Returns the next token from the tokenzier reading
* the JSON string
*/
- private function nextToken():JSONToken
+ private final function nextToken():JSONToken
{
return token = tokenizer.getNextToken();
}
/**
+ * Returns the next token from the tokenizer reading
+ * the JSON string and verifies that the token is valid.
+ */
+ private final function nextValidToken():JSONToken
+ {
+ token = tokenizer.getNextToken();
+ checkValidToken();
+
+ return token;
+ }
+
+ /**
+ * Verifies that the token is valid.
+ */
+ private final function checkValidToken():void
+ {
+ // Catch errors when the input stream ends abruptly
+ if ( token == null )
+ {
+ tokenizer.parseError( "Unexpected end of input" );
+ }
+ }
+
+ /**
* Attempt to parse an array.
*/
- private function parseArray():Array
+ private final function parseArray():Array
{
// create an array internally that we're going to attempt
// to parse from the tokenizer
var a:Array = new Array();
// grab the next token from the tokenizer to move
// past the opening [
- nextToken();
+ nextValidToken();
// check to see if we have an empty array
if ( token.type == JSON_TOKEN::RIGHT_BRACKET )
@@ -126,12 +150,12 @@ package com.adobe.serialization.json
else if ( !strict && token.type == JSON_TOKEN::COMMA )
{
// move past the comma
- nextToken();
+ nextValidToken();
// check to see if we're reached the end of the array
if ( token.type == JSON_TOKEN::RIGHT_BRACKET )
{
- return a;
+ return a;
}
else
{
@@ -145,9 +169,9 @@ package com.adobe.serialization.json
{
// read in the value and add it to the array
a.push( parseValue() );
-
+
// after the value there should be a ] or a ,
- nextToken();
+ nextValidToken();
if ( token.type == JSON_TOKEN::RIGHT_BRACKET )
{
@@ -163,6 +187,8 @@ package com.adobe.serialization.json
// if the decoder is not in strict mode
if ( !strict )
{
+ checkValidToken();
+
// Reached ",]" as the end of the array, so return it
if ( token.type == JSON_TOKEN::RIGHT_BRACKET )
{
@@ -175,24 +201,25 @@ package com.adobe.serialization.json
tokenizer.parseError( "Expecting ] or , but found " + token.value );
}
}
- return null;
+
+ return null;
}
/**
* Attempt to parse an object.
*/
- private function parseObject():Object
+ private final function parseObject():Object
{
// create the object internally that we're going to
// attempt to parse from the tokenizer
var o:Object = new Object();
-
+
// store the string part of an object member so
// that we can assign it a value in the object
var key:String
// grab the next token from the tokenizer
- nextToken();
+ nextValidToken();
// check to see if we have an empty object
if ( token.type == JSON_TOKEN::RIGHT_BRACE )
@@ -205,7 +232,7 @@ package com.adobe.serialization.json
else if ( !strict && token.type == JSON_TOKEN::COMMA )
{
// move past the comma
- nextToken();
+ nextValidToken();
// check to see if we're reached the end of the object
if ( token.type == JSON_TOKEN::RIGHT_BRACE )
@@ -228,23 +255,23 @@ package com.adobe.serialization.json
key = String( token.value );
// move past the string to see what's next
- nextToken();
+ nextValidToken();
// after the string there should be a :
if ( token.type == JSON_TOKEN::COLON )
- {
+ {
// move past the : and read/assign a value for the key
nextToken();
- o[key] = parseValue();
+ o[ key ] = parseValue();
// move past the value to see what's next
- nextToken();
+ nextValidToken();
// after the value there's either a } or a ,
if ( token.type == JSON_TOKEN::RIGHT_BRACE )
{
// we're done reading the object, so return it
- return o;
+ return o;
}
else if ( token.type == JSON_TOKEN::COMMA )
{
@@ -255,6 +282,8 @@ package com.adobe.serialization.json
// if the decoder is not in strict mode
if ( !strict )
{
+ checkValidToken();
+
// Reached ",}" as the end of the object, so return it
if ( token.type == JSON_TOKEN::RIGHT_BRACE )
{
@@ -273,39 +302,35 @@ package com.adobe.serialization.json
}
}
else
- {
+ {
tokenizer.parseError( "Expecting string but found " + token.value );
}
}
- return null;
+ return null;
}
/**
* Attempt to parse a value
*/
- private function parseValue():Object
+ private final function parseValue():Object
{
- // Catch errors when the input stream ends abruptly
- if ( token == null )
- {
- tokenizer.parseError( "Unexpected end of input" );
- }
-
+ checkValidToken();
+
switch ( token.type )
{
case JSON_TOKEN::LEFT_BRACE:
return parseObject();
-
+
case JSON_TOKEN::LEFT_BRACKET:
return parseArray();
-
+
case JSON_TOKEN::STRING:
case JSON_TOKEN::NUMBER:
case JSON_TOKEN::TRUE:
case JSON_TOKEN::FALSE:
case JSON_TOKEN::NULL:
return token.value;
-
+
case JSON_TOKEN::NAN:
if ( !strict )
{
@@ -315,13 +340,13 @@ package com.adobe.serialization.json
{
tokenizer.parseError( "Unexpected " + token.value );
}
-
+
default:
tokenizer.parseError( "Unexpected " + token.value );
-
+
}
- return null;
+ return null;
}
}
}
View
157 src/com/adobe/serialization/json/JSONEncoder.as
@@ -8,11 +8,11 @@
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
-
+
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
-
+
* Neither the name of Adobe Systems Incorporated nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
@@ -30,13 +30,14 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package com.adobe.serialization.json
+package com.adobe.serialization.json
{
-
+
import flash.utils.describeType;
-
- public class JSONEncoder {
+ public class JSONEncoder
+ {
+
/** The string that is going to represent the object we're encoding */
private var jsonString:String;
@@ -48,9 +49,9 @@ package com.adobe.serialization.json
* @playerversion Flash 9.0
* @tiptext
*/
- public function JSONEncoder( value:* ) {
+ public function JSONEncoder( value:* )
+ {
jsonString = convertToString( value );
-
}
/**
@@ -62,45 +63,47 @@ package com.adobe.serialization.json
* @playerversion Flash 9.0
* @tiptext
*/
- public function getString():String {
+ public function getString():String
+ {
return jsonString;
}
/**
* Converts a value to it's JSON string equivalent.
*
- * @param value The value to convert. Could be any
+ * @param value The value to convert. Could be any
* type (object, number, array, etc)
*/
- private function convertToString( value:* ):String {
-
+ private function convertToString( value:* ):String
+ {
// determine what value is and convert it based on it's type
- if ( value is String ) {
-
+ if ( value is String )
+ {
// escape the string so it's formatted correctly
return escapeString( value as String );
-
- } else if ( value is Number ) {
-
+ }
+ else if ( value is Number )
+ {
// only encode numbers that finate
- return isFinite( value as Number) ? value.toString() : "null";
-
- } else if ( value is Boolean ) {
-
+ return isFinite( value as Number ) ? value.toString() : "null";
+ }
+ else if ( value is Boolean )
+ {
// convert boolean to string easily
return value ? "true" : "false";
-
- } else if ( value is Array ) {
-
+ }
+ else if ( value is Array )
+ {
// call the helper method to convert an array
return arrayToString( value as Array );
-
- } else if ( value is Object && value != null ) {
-
+ }
+ else if ( value is Object && value != null )
+ {
// call the helper method to convert an object
return objectToString( value );
}
- return "null";
+
+ return "null";
}
/**
@@ -110,7 +113,8 @@ package com.adobe.serialization.json
* @return The string with escaped special characters
* according to the JSON specification
*/
- private function escapeString( str:String ):String {
+ private function escapeString( str:String ):String
+ {
// create a string to store the string's jsonstring value
var s:String = "";
// current character in the string we're processing
@@ -119,48 +123,49 @@ package com.adobe.serialization.json
var len:Number = str.length;
// loop over all of the characters in the string
- for ( var i:int = 0; i < len; i++ ) {
-
+ for ( var i:int = 0; i < len; i++ )
+ {
// examine the character to determine if we have to escape it
ch = str.charAt( i );
- switch ( ch ) {
-
- case '"': // quotation mark
+ switch ( ch )
+ {
+ case '"': // quotation mark
s += "\\\"";
break;
-
+
//case '/': // solidus
// s += "\\/";
// break;
-
- case '\\': // reverse solidus
+
+ case '\\': // reverse solidus
s += "\\\\";
break;
-
- case '\b': // bell
+
+ case '\b': // bell
s += "\\b";
break;
-
- case '\f': // form feed
+
+ case '\f': // form feed
s += "\\f";
break;
-
- case '\n': // newline
+
+ case '\n': // newline
s += "\\n";
break;
-
- case '\r': // carriage return
+
+ case '\r': // carriage return
s += "\\r";
break;
-
- case '\t': // horizontal tab
+
+ case '\t': // horizontal tab
s += "\\t";
break;
-
- default: // everything else
+
+ default: // everything else
// check for a control character and escape as unicode
- if ( ch < ' ' ) {
+ if ( ch < ' ' )
+ {
// get the hex digit(s) of the character (either 1 or 2 digits)
var hexCode:String = ch.charCodeAt( 0 ).toString( 16 );
@@ -170,16 +175,18 @@ package com.adobe.serialization.json
// create the unicode escape sequence with 4 hex digits
s += "\\u" + zeroPad + hexCode;
- } else {
-
+ }
+ else
+ {
+
// no need to do any special encoding, just pass-through
s += ch;
}
- } // end switch
+ } // end switch
- } // end for loop
-
+ } // end for loop
+
return "\"" + s + "\"";
}
@@ -189,22 +196,26 @@ package com.adobe.serialization.json
* @param a The array to convert
* @return The JSON string representation of <code>a</code>
*/
- private function arrayToString( a:Array ):String {
+ private function arrayToString( a:Array ):String
+ {
// create a string to store the array's jsonstring value
var s:String = "";
// loop over the elements in the array and add their converted
// values to the string
- for ( var i:int = 0; i < a.length; i++ ) {
+ var length:int = a.length;
+ for ( var i:int = 0; i < length; i++ )
+ {
// when the length is 0 we're adding the first element so
// no comma is necessary
- if ( s.length > 0 ) {
+ if ( s.length > 0 )
+ {
// we've already added an element, so add the comma separator
s += ","
}
// convert the value to a string
- s += convertToString( a[i] );
+ s += convertToString( a[ i ] );
}
// KNOWN ISSUE: In ActionScript, Arrays can also be associative
@@ -222,7 +233,7 @@ package com.adobe.serialization.json
// A possible solution is to instead encode the Array as an Object
// but then it won't get decoded correctly (and won't be an
// Array instance)
-
+
// close the array and return it's string value
return "[" + s + "]";
}
@@ -252,7 +263,7 @@ package com.adobe.serialization.json
for ( var key:String in o )
{
// assign value to a variable for quick lookup
- value = o[key];
+ value = o[ key ];
// don't add function's to the JSON string
if ( value is Function )
@@ -263,7 +274,8 @@ package com.adobe.serialization.json
// when the length is 0 we're adding the first item so
// no comma is necessary
- if ( s.length > 0 ) {
+ if ( s.length > 0 )
+ {
// we've already added an item, so add the comma separator
s += ","
}
@@ -275,13 +287,13 @@ package com.adobe.serialization.json
{
// Loop over all of the variables and accessors in the class and
// serialize them along with their values.
- for each ( var v:XML in classInfo..*.(
+ for each ( var v:XML in classInfo..*.(
name() == "variable"
||
- (
+ (
name() == "accessor"
// Issue #116 - Make sure accessors are readable
- && attribute( "access" ).charAt( 0 ) == "r" )
+ && attribute( "access" ).charAt( 0 ) == "r" )
) )
{
// Issue #110 - If [Transient] metadata exists, then we should skip
@@ -292,21 +304,20 @@ package com.adobe.serialization.json
// When the length is 0 we're adding the first item so
// no comma is necessary
- if ( s.length > 0 ) {
+ if ( s.length > 0 )
+ {
// We've already added an item, so add the comma separator
s += ","
}
- s += escapeString( v.@name.toString() ) + ":"
- + convertToString( o[ v.@name ] );
+ s += escapeString( v.@name.toString() ) + ":"
+ + convertToString( o[ v.@name ] );
}
-
}
return "{" + s + "}";
}
-
-
- }
-}
+ }
+
+}
View
25 src/com/adobe/serialization/json/JSONParseError.as
@@ -30,20 +30,22 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package com.adobe.serialization.json {
-
+package com.adobe.serialization.json
+{
+
/**
*
*
*/
- public class JSONParseError extends Error {
-
+ public class JSONParseError extends Error
+ {
+
/** The location in the string where the error occurred */
private var _location:int;
/** The string in which the parse error occurred */
private var _text:String;
-
+
/**
* Constructs a new JSONParseError.
*
@@ -52,13 +54,14 @@ package com.adobe.serialization.json {
* @playerversion Flash 9.0
* @tiptext
*/
- public function JSONParseError( message:String = "", location:int = 0, text:String = "") {
+ public function JSONParseError( message:String = "", location:int = 0, text:String = "" )
+ {
super( message );
name = "JSONParseError";
_location = location;
_text = text;
}
-
+
/**
* Provides read-only access to the location variable.
*
@@ -67,7 +70,8 @@ package com.adobe.serialization.json {
* @playerversion Flash 9.0
* @tiptext
*/
- public function get location():int {
+ public function get location():int
+ {
return _location;
}
@@ -79,9 +83,10 @@ package com.adobe.serialization.json {
* @playerversion Flash 9.0
* @tiptext
*/
- public function get text():String {
+ public function get text():String
+ {
return _text;
}
}
-
+
}
View
68 src/com/adobe/serialization/json/JSONToken.as
@@ -30,75 +30,63 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package com.adobe.serialization.json {
-
- public class JSONToken {
+package com.adobe.serialization.json
+{
- private var _type:int;
- private var _value:Object;
+ public final class JSONToken
+ {
/**
- * Creates a new JSONToken with a specific token type and value.
+ * The type of the token.
*
- * @param type The JSONTokenType of the token
- * @param value The value of the token
* @langversion ActionScript 3.0
* @playerversion Flash 9.0
* @tiptext
*/
- public function JSONToken( type:int = JSON_TOKEN::UNKNOWN, value:Object = null ) {
- _type = type;
- _value = value;
- }
+ public var type:int;
/**
- * Returns the type of the token.
+ * The value of the token
*
- * @see com.adobe.serialization.json.JSONTokenType
* @langversion ActionScript 3.0
* @playerversion Flash 9.0
* @tiptext
*/
- public function get type():int {
- return _type;
- }
+ public var value:Object;
/**
- * Sets the type of the token.
+ * Creates a new JSONToken with a specific token type and value.
*
- * @see com.adobe.serialization.json.JSONTokenType
+ * @param type The JSONTokenType of the token
+ * @param value The value of the token
* @langversion ActionScript 3.0
* @playerversion Flash 9.0
* @tiptext
*/
- public function set type( value:int ):void {
- _type = value;
+ public function JSONToken( type:int = JSON_TOKEN::UNKNOWN, value:Object = null )
+ {
+ this.type = type;
+ this.value = value;
}
/**
- * Gets the value of the token
- *
- * @see com.adobe.serialization.json.JSONTokenType
- * @langversion ActionScript 3.0
- * @playerversion Flash 9.0
- * @tiptext
+ * Reusable token instance.
+ *
+ * @see #create()
*/
- public function get value():Object {
- return _value;
- }
+ internal static const token:JSONToken = new JSONToken();
/**
- * Sets the value of the token
- *
- * @see com.adobe.serialization.json.JSONTokenType
- * @langversion ActionScript 3.0
- * @playerversion Flash 9.0
- * @tiptext
+ * Factory method to create instances. Because we don't need more than one instance
+ * of a token at a time, we can always use the same instance to improve performance
+ * and reduce memory consumption during decoding.
*/
- public function set value ( v:Object ):void {
- _value = v;
+ internal static function create( type:int = JSON_TOKEN::UNKNOWN, value:Object = null ):JSONToken
+ {
+ token.type = type;
+ token.value = value;
+
+ return token;
}
-
}
-
}
View
196 src/com/adobe/serialization/json/JSONTokenizer.as
@@ -30,18 +30,20 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package com.adobe.serialization.json {
-
- public class JSONTokenizer {
+package com.adobe.serialization.json
+{
+
+ public class JSONTokenizer
+ {
- /**
+ /**
* Flag indicating if the tokenizer should only recognize
* standard JSON tokens. Setting to <code>false</code> allows
* tokens such as NaN and allows numbers to be formatted as
* hex, etc.
*/
private var strict:Boolean;
-
+
/** The object that will get parsed from the JSON string */
private var obj:Object;
@@ -54,14 +56,14 @@ package com.adobe.serialization.json {
/** The current character in the JSON string during parsing */
private var ch:String;
- /**
+ /**
* The regular expression used to make sure the string does not
* contain invalid control characters.
*/
- private var controlCharsRegExp:RegExp = /[\x00-\x1F]/;
+ private const controlCharsRegExp:RegExp = /[\x00-\x1F]/;
/**
- * Constructs a new JSONDecoder to parse a JSON string
+ * Constructs a new JSONDecoder to parse a JSON string
* into a native object.
*
* @param s The JSON string to be converted
@@ -79,62 +81,55 @@ package com.adobe.serialization.json {
/**
* Gets the next token in the input sting and advances
- * the character to the next character after the token
+ * the character to the next character after the token
*/
public function getNextToken():JSONToken
{
- var token:JSONToken = new JSONToken();
+ var token:JSONToken = null;
// skip any whitespace / comments since the last
// token was read
skipIgnored();
-
+
// examine the new character and see what we have...
switch ( ch )
- {
+ {
case '{':
- token.type = JSON_TOKEN::LEFT_BRACE;
- token.value = '{';
+ token = JSONToken.create( JSON_TOKEN::LEFT_BRACE, ch );
nextChar();
break
-
+
case '}':
- token.type = JSON_TOKEN::RIGHT_BRACE;
- token.value = '}';
+ token = JSONToken.create( JSON_TOKEN::RIGHT_BRACE, ch );
nextChar();
break
-
+
case '[':
- token.type = JSON_TOKEN::LEFT_BRACKET;
- token.value = '[';
+ token = JSONToken.create( JSON_TOKEN::LEFT_BRACKET, ch );
nextChar();
break
-
+
case ']':
- token.type = JSON_TOKEN::RIGHT_BRACKET;
- token.value = ']';
+ token = JSONToken.create( JSON_TOKEN::RIGHT_BRACKET, ch );
nextChar();
break
case ',':
- token.type = JSON_TOKEN::COMMA;
- token.value = ',';
+ token = JSONToken.create( JSON_TOKEN::COMMA, ch );
nextChar();
break
-
+
case ':':
- token.type = JSON_TOKEN::COLON;
- token.value = ':';
+ token = JSONToken.create( JSON_TOKEN::COLON, ch );
nextChar();
break;
-
+
case 't': // attempt to read true
var possibleTrue:String = "t" + nextChar() + nextChar() + nextChar();
if ( possibleTrue == "true" )
{
- token.type = JSON_TOKEN::TRUE;
- token.value = true;
+ token = JSONToken.create( JSON_TOKEN::TRUE, true );
nextChar();
}
else
@@ -143,14 +138,13 @@ package com.adobe.serialization.json {
}
break;
-
+
case 'f': // attempt to read false
var possibleFalse:String = "f" + nextChar() + nextChar() + nextChar() + nextChar();
if ( possibleFalse == "false" )
{
- token.type = JSON_TOKEN::FALSE;
- token.value = false;
+ token = JSONToken.create( JSON_TOKEN::FALSE, false );
nextChar();
}
else
@@ -159,14 +153,13 @@ package com.adobe.serialization.json {
}
break;
-
+
case 'n': // attempt to read null
var possibleNull:String = "n" + nextChar() + nextChar() + nextChar();
if ( possibleNull == "null" )
{
- token.type = JSON_TOKEN::NULL;
- token.value = null;
+ token = JSONToken.create( JSON_TOKEN::NULL, null );
nextChar();
}
else
@@ -175,14 +168,13 @@ package com.adobe.serialization.json {
}
break;
-
+
case 'N': // attempt to read NaN
var possibleNaN:String = "N" + nextChar() + nextChar();
if ( possibleNaN == "NaN" )
{
- token.type = JSON_TOKEN::NAN;
- token.value = NaN;
+ token = JSONToken.create( JSON_TOKEN::NAN, NaN );
nextChar();
}
else
@@ -191,12 +183,12 @@ package com.adobe.serialization.json {
}
break;
-
+
case '"': // the start of a string
token = readString();
break;
-
- default:
+
+ default:
// see if we can read a number
if ( isDigit( ch ) || ch == '-' )
{
@@ -205,10 +197,10 @@ package com.adobe.serialization.json {
else if ( ch == '' )
{
// check for reading past the end of the string
- return null;
+ token = null;
}
else
- {
+ {
// not sure what was in the input string - it's not
// anything we expected
parseError( "Unexpected " + ch + " encountered" );
@@ -226,7 +218,7 @@ package com.adobe.serialization.json {
* @return the JSONToken with the string value if a string could
* be read. Throws an error otherwise.
*/
- private function readString():JSONToken
+ private final function readString():JSONToken
{
// Rather than examine the string character-by-character, it's
// faster to use indexOf to try to and find the closing quote character
@@ -254,7 +246,7 @@ package com.adobe.serialization.json {
}
// If we have an even number of backslashes, that means this is the ending quote
- if ( backspaceCount % 2 == 0 )
+ if ( ( backspaceCount & 1 ) == 0 )
{
break;
}
@@ -271,10 +263,10 @@ package com.adobe.serialization.json {
// Unescape the string
// the token for the string we'll try to read
- var token:JSONToken = new JSONToken();
- token.type = JSON_TOKEN::STRING;
- // Attach resulting string to the token to return it
- token.value = unescapeString( jsonString.substr( loc, quoteIndex - loc ) );
+ var token:JSONToken = JSONToken.create(
+ JSON_TOKEN::STRING,
+ // Attach resulting string to the token to return it
+ unescapeString( jsonString.substr( loc, quoteIndex - loc ) ) );
// Move past the closing quote in the input string. This updates the next
// character in the input stream to be the character one after the closing quote
@@ -317,32 +309,43 @@ package com.adobe.serialization.json {
nextSubstringStartPosition = backslashIndex + 2;
// Check the next character so we know what to escape
- var afterBackslashIndex:int = backslashIndex + 1;
- var escapedChar:String = input.charAt( afterBackslashIndex );
+ var escapedChar:String = input.charAt( backslashIndex + 1 );
switch ( escapedChar )
- {
+ {
// Try to list the most common expected cases first to improve performance
- case '"': result += '"'; break; // quotation mark
- case '\\': result += '\\'; break; // reverse solidus
- case 'n': result += '\n'; break; // newline
- case 'r': result += '\r'; break; // carriage return
- case 't': result += '\t'; break; // horizontal tab
+ case '"':
+ result += escapedChar;
+ break; // quotation mark
+ case '\\':
+ result += escapedChar;
+ break; // reverse solidus
+ case 'n':
+ result += '\n';
+ break; // newline
+ case 'r':
+ result += '\r';
+ break; // carriage return
+ case 't':
+ result += '\t';
+ break; // horizontal tab
// Convert a unicode escape sequence to it's character value
case 'u':
// Save the characters as a string we'll convert to an int
var hexValue:String = "";
+ var unicodeEndPosition:int = nextSubstringStartPosition + 4;
+
// Make sure there are enough characters in the string leftover
- if ( nextSubstringStartPosition + 4 > len )
+ if ( unicodeEndPosition > len )
{
parseError( "Unexpected end of input. Expecting 4 hex digits after \\u." );
}
// Try to find 4 hex characters
- for ( var i:int = nextSubstringStartPosition; i < nextSubstringStartPosition + 4; i++ )
+ for ( var i:int = nextSubstringStartPosition; i < unicodeEndPosition; i++ )
{
// get the next character and determine
// if it's a valid hex digit or not
@@ -360,14 +363,22 @@ package com.adobe.serialization.json {
// integer value to create a character to add
// to our string.
result += String.fromCharCode( parseInt( hexValue, 16 ) );
+
// Move past the 4 hex digits that we just read
- nextSubstringStartPosition += 4;
+ nextSubstringStartPosition = unicodeEndPosition;
break;
- case 'f': result += '\f'; break; // form feed
- case '/': result += '/'; break; // solidus
- case 'b': result += '\b'; break; // bell
- default: result += '\\' + escapedChar; // Couldn't unescape the sequence, so just pass it through
+ case 'f':
+ result += '\f';
+ break; // form feed
+ case '/':
+ result += '/';
+ break; // solidus
+ case 'b':
+ result += '\b';
+ break; // bell
+ default:
+ result += '\\' + escapedChar; // Couldn't unescape the sequence, so just pass it through
}
}
else
@@ -386,11 +397,11 @@ package com.adobe.serialization.json {
* Attempts to read a number from the input string. Places
* the character location at the first character after the
* number.
- *
+ *
* @return The JSONToken with the number value if a number could
* be read. Throws an error otherwise.
*/
- private function readNumber():JSONToken
+ private final function readNumber():JSONToken
{
// the string to accumulate the number characters
// into that we'll convert to a number at the end
@@ -438,7 +449,7 @@ package com.adobe.serialization.json {
}
else
{
- parseError( "Number in hex format require at least one hex digit after \"0x\"" );
+ parseError( "Number in hex format require at least one hex digit after \"0x\"" );
}
// consume all of the hex values
@@ -497,7 +508,7 @@ package com.adobe.serialization.json {
{
parseError( "Scientific notation number needs exponent value" );
}
-
+
// read in the exponent
while ( isDigit( ch ) )
{
@@ -512,27 +523,24 @@ package com.adobe.serialization.json {
if ( isFinite( num ) && !isNaN( num ) )
{
// the token for the number that we've read
- var token:JSONToken = new JSONToken();
- token.type = JSON_TOKEN::NUMBER;
- token.value = num;
- return token;
+ return JSONToken.create( JSON_TOKEN::NUMBER, num );
}
else
{
parseError( "Number " + num + " is not valid!" );
}
- return null;
+ return null;
}
-
+
/**
* Reads the next character in the input
* string and advances the character location.
*
* @return The next character in the input string, or
* null if we've read past the end.
*/
- private function nextChar():String
+ private final function nextChar():String
{
return ch = jsonString.charAt( loc++ );
}
@@ -541,7 +549,7 @@ package com.adobe.serialization.json {
* Advances the character location past any
* sort of white space and comments
*/
- private function skipIgnored():void
+ private final function skipIgnored():void
{
var originalLoc:int;
@@ -552,8 +560,7 @@ package com.adobe.serialization.json {
originalLoc = loc;
skipWhite();
skipComments();
- }
- while ( originalLoc != loc );
+ } while ( originalLoc != loc );
}
/**
@@ -576,16 +583,15 @@ package com.adobe.serialization.json {
do
{
nextChar();
- }
- while ( ch != '\n' && ch != '' )
+ } while ( ch != '\n' && ch != '' )
// move past the \n
nextChar();
break;
case '*': // multi-line comment, read until closing */
-
+
// move past the opening *
nextChar();
@@ -596,7 +602,7 @@ package com.adobe.serialization.json {
{
// check to see if we have a closing /
nextChar();
- if ( ch == '/')
+ if ( ch == '/' )
{
// move past the end of the closing */
nextChar();
@@ -616,15 +622,15 @@ package com.adobe.serialization.json {
parseError( "Multi-line comment not closed" );
}
}
-
+
break;
// Can't match a comment after a /, so it's a parsing error
default:
parseError( "Unexpected " + ch + " encountered (expecting '/' or '*' )" );
}
}
-
+
}
@@ -633,16 +639,16 @@ package com.adobe.serialization.json {
* the character to the first character after any possible
* whitespace.
*/
- private function skipWhite():void
- {
+ private final function skipWhite():void
+ {
// As long as there are spaces in the input
// stream, advance the current location pointer
// past them
while ( isWhiteSpace( ch ) )
{
nextChar();
}
-
+
}
/**
@@ -651,7 +657,7 @@ package com.adobe.serialization.json {
* @return True if the character passed in is a whitespace
* character
*/
- private function isWhiteSpace( ch:String ):Boolean
+ private final function isWhiteSpace( ch:String ):Boolean
{
// Check for the whitespace defined in the spec
if ( ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' )
@@ -672,7 +678,7 @@ package com.adobe.serialization.json {
*
* @return True if the character passed in is a digit
*/
- private function isDigit( ch:String ):Boolean
+ private final function isDigit( ch:String ):Boolean
{
return ( ch >= '0' && ch <= '9' );
}
@@ -682,21 +688,21 @@ package com.adobe.serialization.json {
*
* @return True if the character passed in is a hex digit
*/
- private function isHexDigit( ch:String ):Boolean
+ private final function isHexDigit( ch:String ):Boolean
{
return ( isDigit( ch ) || ( ch >= 'A' && ch <= 'F' ) || ( ch >= 'a' && ch <= 'f' ) );
}
-
+
/**
* Raises a parsing error with a specified message, tacking
* on the error location and the original string.
*
* @param message The message indicating why the error occurred
*/
- public function parseError( message:String ):void
+ public final function parseError( message:String ):void
{
throw new JSONParseError( message, loc, jsonString );
}
}
-
+
}
View
18 tests/src/com/adobe/serialization/json/JSONTest.as
@@ -414,6 +414,24 @@ package com.adobe.serialization.json
assertTrue( b );
}
+ public function testDecodeWithUnexpectedEndOfInput():void
+ {
+ expectParseError( "[" );
+ expectParseError( "[ 12" );
+ expectParseError( "[ 12, " );
+
+ expectParseError( "{" );
+ expectParseError( "{ \"prop" );
+ expectParseError( "{ \"prop\"" );
+ expectParseError( "{ \"prop\":" );
+ expectParseError( "{ \"prop\": t" );
+ expectParseError( "{ \"prop\": true" );
+
+ expectParseError( "t" );
+ expectParseError( "tr" );
+ expectParseError( "tru" );
+ }
+
public function testEncodeTrue():void
{
var o:String = JSON.encode( true );

0 comments on commit 064139a

Please sign in to comment.