aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/robertkrimen/otto/builtin_json.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/robertkrimen/otto/builtin_json.go')
-rw-r--r--vendor/github.com/robertkrimen/otto/builtin_json.go299
1 files changed, 299 insertions, 0 deletions
diff --git a/vendor/github.com/robertkrimen/otto/builtin_json.go b/vendor/github.com/robertkrimen/otto/builtin_json.go
new file mode 100644
index 000000000..aed54bf12
--- /dev/null
+++ b/vendor/github.com/robertkrimen/otto/builtin_json.go
@@ -0,0 +1,299 @@
+package otto
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "strings"
+)
+
+type _builtinJSON_parseContext struct {
+ call FunctionCall
+ reviver Value
+}
+
+func builtinJSON_parse(call FunctionCall) Value {
+ ctx := _builtinJSON_parseContext{
+ call: call,
+ }
+ revive := false
+ if reviver := call.Argument(1); reviver.isCallable() {
+ revive = true
+ ctx.reviver = reviver
+ }
+
+ var root interface{}
+ err := json.Unmarshal([]byte(call.Argument(0).string()), &root)
+ if err != nil {
+ panic(call.runtime.panicSyntaxError(err.Error()))
+ }
+ value, exists := builtinJSON_parseWalk(ctx, root)
+ if !exists {
+ value = Value{}
+ }
+ if revive {
+ root := ctx.call.runtime.newObject()
+ root.put("", value, false)
+ return builtinJSON_reviveWalk(ctx, root, "")
+ }
+ return value
+}
+
+func builtinJSON_reviveWalk(ctx _builtinJSON_parseContext, holder *_object, name string) Value {
+ value := holder.get(name)
+ if object := value._object(); object != nil {
+ if isArray(object) {
+ length := int64(objectLength(object))
+ for index := int64(0); index < length; index += 1 {
+ name := arrayIndexToString(index)
+ value := builtinJSON_reviveWalk(ctx, object, name)
+ if value.IsUndefined() {
+ object.delete(name, false)
+ } else {
+ object.defineProperty(name, value, 0111, false)
+ }
+ }
+ } else {
+ object.enumerate(false, func(name string) bool {
+ value := builtinJSON_reviveWalk(ctx, object, name)
+ if value.IsUndefined() {
+ object.delete(name, false)
+ } else {
+ object.defineProperty(name, value, 0111, false)
+ }
+ return true
+ })
+ }
+ }
+ return ctx.reviver.call(ctx.call.runtime, toValue_object(holder), name, value)
+}
+
+func builtinJSON_parseWalk(ctx _builtinJSON_parseContext, rawValue interface{}) (Value, bool) {
+ switch value := rawValue.(type) {
+ case nil:
+ return nullValue, true
+ case bool:
+ return toValue_bool(value), true
+ case string:
+ return toValue_string(value), true
+ case float64:
+ return toValue_float64(value), true
+ case []interface{}:
+ arrayValue := make([]Value, len(value))
+ for index, rawValue := range value {
+ if value, exists := builtinJSON_parseWalk(ctx, rawValue); exists {
+ arrayValue[index] = value
+ }
+ }
+ return toValue_object(ctx.call.runtime.newArrayOf(arrayValue)), true
+ case map[string]interface{}:
+ object := ctx.call.runtime.newObject()
+ for name, rawValue := range value {
+ if value, exists := builtinJSON_parseWalk(ctx, rawValue); exists {
+ object.put(name, value, false)
+ }
+ }
+ return toValue_object(object), true
+ }
+ return Value{}, false
+}
+
+type _builtinJSON_stringifyContext struct {
+ call FunctionCall
+ stack []*_object
+ propertyList []string
+ replacerFunction *Value
+ gap string
+}
+
+func builtinJSON_stringify(call FunctionCall) Value {
+ ctx := _builtinJSON_stringifyContext{
+ call: call,
+ stack: []*_object{nil},
+ }
+ replacer := call.Argument(1)._object()
+ if replacer != nil {
+ if isArray(replacer) {
+ length := objectLength(replacer)
+ seen := map[string]bool{}
+ propertyList := make([]string, length)
+ length = 0
+ for index, _ := range propertyList {
+ value := replacer.get(arrayIndexToString(int64(index)))
+ switch value.kind {
+ case valueObject:
+ switch value.value.(*_object).class {
+ case "String":
+ case "Number":
+ default:
+ continue
+ }
+ case valueString:
+ case valueNumber:
+ default:
+ continue
+ }
+ name := value.string()
+ if seen[name] {
+ continue
+ }
+ seen[name] = true
+ length += 1
+ propertyList[index] = name
+ }
+ ctx.propertyList = propertyList[0:length]
+ } else if replacer.class == "Function" {
+ value := toValue_object(replacer)
+ ctx.replacerFunction = &value
+ }
+ }
+ if spaceValue, exists := call.getArgument(2); exists {
+ if spaceValue.kind == valueObject {
+ switch spaceValue.value.(*_object).class {
+ case "String":
+ spaceValue = toValue_string(spaceValue.string())
+ case "Number":
+ spaceValue = spaceValue.numberValue()
+ }
+ }
+ switch spaceValue.kind {
+ case valueString:
+ value := spaceValue.string()
+ if len(value) > 10 {
+ ctx.gap = value[0:10]
+ } else {
+ ctx.gap = value
+ }
+ case valueNumber:
+ value := spaceValue.number().int64
+ if value > 10 {
+ value = 10
+ } else if value < 0 {
+ value = 0
+ }
+ ctx.gap = strings.Repeat(" ", int(value))
+ }
+ }
+ holder := call.runtime.newObject()
+ holder.put("", call.Argument(0), false)
+ value, exists := builtinJSON_stringifyWalk(ctx, "", holder)
+ if !exists {
+ return Value{}
+ }
+ valueJSON, err := json.Marshal(value)
+ if err != nil {
+ panic(call.runtime.panicTypeError(err.Error()))
+ }
+ if ctx.gap != "" {
+ valueJSON1 := bytes.Buffer{}
+ json.Indent(&valueJSON1, valueJSON, "", ctx.gap)
+ valueJSON = valueJSON1.Bytes()
+ }
+ return toValue_string(string(valueJSON))
+}
+
+func builtinJSON_stringifyWalk(ctx _builtinJSON_stringifyContext, key string, holder *_object) (interface{}, bool) {
+ value := holder.get(key)
+
+ if value.IsObject() {
+ object := value._object()
+ if toJSON := object.get("toJSON"); toJSON.IsFunction() {
+ value = toJSON.call(ctx.call.runtime, value, key)
+ } else {
+ // If the object is a GoStruct or something that implements json.Marshaler
+ if object.objectClass.marshalJSON != nil {
+ marshaler := object.objectClass.marshalJSON(object)
+ if marshaler != nil {
+ return marshaler, true
+ }
+ }
+ }
+ }
+
+ if ctx.replacerFunction != nil {
+ value = (*ctx.replacerFunction).call(ctx.call.runtime, toValue_object(holder), key, value)
+ }
+
+ if value.kind == valueObject {
+ switch value.value.(*_object).class {
+ case "Boolean":
+ value = value._object().value.(Value)
+ case "String":
+ value = toValue_string(value.string())
+ case "Number":
+ value = value.numberValue()
+ }
+ }
+
+ switch value.kind {
+ case valueBoolean:
+ return value.bool(), true
+ case valueString:
+ return value.string(), true
+ case valueNumber:
+ integer := value.number()
+ switch integer.kind {
+ case numberInteger:
+ return integer.int64, true
+ case numberFloat:
+ return integer.float64, true
+ default:
+ return nil, true
+ }
+ case valueNull:
+ return nil, true
+ case valueObject:
+ holder := value._object()
+ if value := value._object(); nil != value {
+ for _, object := range ctx.stack {
+ if holder == object {
+ panic(ctx.call.runtime.panicTypeError("Converting circular structure to JSON"))
+ }
+ }
+ ctx.stack = append(ctx.stack, value)
+ defer func() { ctx.stack = ctx.stack[:len(ctx.stack)-1] }()
+ }
+ if isArray(holder) {
+ var length uint32
+ switch value := holder.get("length").value.(type) {
+ case uint32:
+ length = value
+ case int:
+ if value >= 0 {
+ length = uint32(value)
+ }
+ default:
+ panic(ctx.call.runtime.panicTypeError(fmt.Sprintf("JSON.stringify: invalid length: %v (%[1]T)", value)))
+ }
+ array := make([]interface{}, length)
+ for index, _ := range array {
+ name := arrayIndexToString(int64(index))
+ value, _ := builtinJSON_stringifyWalk(ctx, name, holder)
+ array[index] = value
+ }
+ return array, true
+ } else if holder.class != "Function" {
+ object := map[string]interface{}{}
+ if ctx.propertyList != nil {
+ for _, name := range ctx.propertyList {
+ value, exists := builtinJSON_stringifyWalk(ctx, name, holder)
+ if exists {
+ object[name] = value
+ }
+ }
+ } else {
+ // Go maps are without order, so this doesn't conform to the ECMA ordering
+ // standard, but oh well...
+ holder.enumerate(false, func(name string) bool {
+ value, exists := builtinJSON_stringifyWalk(ctx, name, holder)
+ if exists {
+ object[name] = value
+ }
+ return true
+ })
+ }
+ return object, true
+ }
+ }
+ return nil, false
+}