123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299 |
- 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 classString:
- case classNumber:
- 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 == classFunction {
- value := toValue_object(replacer)
- ctx.replacerFunction = &value
- }
- }
- if spaceValue, exists := call.getArgument(2); exists {
- if spaceValue.kind == valueObject {
- switch spaceValue.value.(*_object).class {
- case classString:
- spaceValue = toValue_string(spaceValue.string())
- case classNumber:
- 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 classBoolean:
- value = value._object().value.(Value)
- case classString:
- value = toValue_string(value.string())
- case classNumber:
- 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(propertyLength).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 != classFunction {
- 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
- }
|