Browse Source

Use streaming JSON parser API.

Dmitry Panov 9 years ago
parent
commit
1396f3a248
1 changed files with 105 additions and 45 deletions
  1. 105 45
      builtin_json.go

+ 105 - 45
builtin_json.go

@@ -3,78 +3,138 @@ package goja
 import (
 import (
 	"bytes"
 	"bytes"
 	"encoding/json"
 	"encoding/json"
+	"fmt"
+	"io"
 	"strings"
 	"strings"
 )
 )
 
 
 var hex = "0123456789abcdef"
 var hex = "0123456789abcdef"
 
 
 func (r *Runtime) builtinJSON_parse(call FunctionCall) Value {
 func (r *Runtime) builtinJSON_parse(call FunctionCall) Value {
-	var reviver func(FunctionCall) Value
-
-	if arg1 := call.Argument(1); arg1 != _undefined {
-		reviver, _ = arg1.ToObject(r).self.assertCallable()
-	}
-
-	var root interface{}
-	err := json.Unmarshal([]byte(call.Argument(0).String()), &root)
+	d := json.NewDecoder(bytes.NewBufferString(call.Argument(0).String()))
 
 
+	value, err := r.builtinJSON_decodeValue(d)
 	if err != nil {
 	if err != nil {
 		panic(r.newError(r.global.SyntaxError, err.Error()))
 		panic(r.newError(r.global.SyntaxError, err.Error()))
 	}
 	}
 
 
-	value, exists := r.builtinJSON_parseWalk(root)
-	if !exists {
-		value = _undefined
+	if tok, err := d.Token(); err != io.EOF {
+		panic(r.newError(r.global.SyntaxError, "Unexpected token at the end: %v", tok))
+	}
+
+	var reviver func(FunctionCall) Value
+
+	if arg1 := call.Argument(1); arg1 != _undefined {
+		reviver, _ = arg1.ToObject(r).self.assertCallable()
 	}
 	}
+
 	if reviver != nil {
 	if reviver != nil {
 		root := r.NewObject()
 		root := r.NewObject()
 		root.self.putStr("", value, false)
 		root.self.putStr("", value, false)
 		return r.builtinJSON_reviveWalk(reviver, root, stringEmpty)
 		return r.builtinJSON_reviveWalk(reviver, root, stringEmpty)
 	}
 	}
+
 	return value
 	return value
 }
 }
 
 
-func (r *Runtime) builtinJSON_parseWalk(rawValue interface{}) (Value, bool) {
-	switch value := rawValue.(type) {
+func (r *Runtime) builtinJSON_decodeToken(d *json.Decoder, tok json.Token) (Value, error) {
+	switch tok := tok.(type) {
+	case json.Delim:
+		switch tok {
+		case '{':
+			return r.builtinJSON_decodeObject(d)
+		case '[':
+			return r.builtinJSON_decodeArray(d)
+		}
 	case nil:
 	case nil:
-		return _null, true
+		return _null, nil
+	case string:
+		return newStringValue(tok), nil
+	case float64:
+		return floatToValue(tok), nil
 	case bool:
 	case bool:
-		if value {
-			return valueTrue, true
+		if tok {
+			return valueTrue, nil
+		}
+		return valueFalse, nil
+	}
+	return nil, fmt.Errorf("Unexpected token (%T): %v", tok, tok)
+}
+
+func (r *Runtime) builtinJSON_decodeValue(d *json.Decoder) (Value, error) {
+	tok, err := d.Token()
+	if err != nil {
+		return nil, err
+	}
+	return r.builtinJSON_decodeToken(d, tok)
+}
+
+func (r *Runtime) builtinJSON_decodeObject(d *json.Decoder) (*Object, error) {
+	object := r.NewObject()
+	for {
+		key, end, err := r.builtinJSON_decodeObjectKey(d)
+		if err != nil {
+			return nil, err
+		}
+		if end {
+			break
+		}
+		value, err := r.builtinJSON_decodeValue(d)
+		if err != nil {
+			return nil, err
+		}
+
+		if key == "__proto__" {
+			descr := r.NewObject().self
+			descr.putStr("value", value, false)
+			descr.putStr("writable", valueTrue, false)
+			descr.putStr("enumerable", valueTrue, false)
+			descr.putStr("configurable", valueTrue, false)
+			object.self.defineOwnProperty(string__proto__, descr, false)
 		} else {
 		} else {
-			return valueFalse, true
+			object.self.putStr(key, value, false)
+		}
+	}
+	return object, nil
+}
+
+func (r *Runtime) builtinJSON_decodeObjectKey(d *json.Decoder) (string, bool, error) {
+	tok, err := d.Token()
+	if err != nil {
+		return "", false, err
+	}
+	switch tok := tok.(type) {
+	case json.Delim:
+		if tok == '}' {
+			return "", true, nil
 		}
 		}
 	case string:
 	case string:
-		return newStringValue(value), true
-	case float64:
-		return floatToValue(value), true
-	case []interface{}:
-		arrayValue := make([]Value, len(value))
-		for index, rawValue := range value {
-			if value, exists := r.builtinJSON_parseWalk(rawValue); exists {
-				arrayValue[index] = value
-			}
+		return tok, false, nil
+	}
+
+	return "", false, fmt.Errorf("Unexpected token (%T): %v", tok, tok)
+}
+
+func (r *Runtime) builtinJSON_decodeArray(d *json.Decoder) (*Object, error) {
+	var arrayValue []Value
+	for {
+		tok, err := d.Token()
+		if err != nil {
+			return nil, err
 		}
 		}
-		return r.newArrayValues(arrayValue), true
-	case map[string]interface{}:
-		object := r.NewObject()
-		for name, rawValue := range value {
-			if value, exists := r.builtinJSON_parseWalk(rawValue); exists {
-				if name == "__proto__" {
-					descr := r.NewObject().self
-					descr.putStr("value", value, false)
-					descr.putStr("writable", valueTrue, false)
-					descr.putStr("enumerable", valueTrue, false)
-					descr.putStr("configurable", valueTrue, false)
-					object.self.defineOwnProperty(string__proto__, descr, false)
-				} else {
-					object.self.putStr(name, value, false)
-				}
+		if delim, ok := tok.(json.Delim); ok {
+			if delim == ']' {
+				break
 			}
 			}
+			return nil, fmt.Errorf("Unexpected delimiter: %v, expecting ']'", delim)
+		}
+		value, err := r.builtinJSON_decodeToken(d, tok)
+		if err != nil {
+			return nil, err
 		}
 		}
-		return object, true
+		arrayValue = append(arrayValue, value)
 	}
 	}
-	return _undefined, false
+	return r.newArrayValues(arrayValue), nil
 }
 }
 
 
 func isArray(object *Object) bool {
 func isArray(object *Object) bool {
@@ -427,8 +487,8 @@ func (ctx *_builtinJSON_stringifyContext) quote(str valueString) {
 		default:
 		default:
 			if r < 0x20 {
 			if r < 0x20 {
 				ctx.buf.WriteString(`\u00`)
 				ctx.buf.WriteString(`\u00`)
-				ctx.buf.WriteByte(hex[r >> 4])
-				ctx.buf.WriteByte(hex[r & 0xF])
+				ctx.buf.WriteByte(hex[r>>4])
+				ctx.buf.WriteByte(hex[r&0xF])
 			} else {
 			} else {
 				ctx.buf.WriteRune(r)
 				ctx.buf.WriteRune(r)
 			}
 			}