go.ketch.com/lib/goja@v0.0.1/builtin_json.go (about)

     1  package goja
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"math"
     9  	"strconv"
    10  	"strings"
    11  	"unicode/utf16"
    12  	"unicode/utf8"
    13  
    14  	"go.ketch.com/lib/goja/unistring"
    15  )
    16  
    17  const hex = "0123456789abcdef"
    18  
    19  func (r *Runtime) builtinJSON_parse(call FunctionCall) Value {
    20  	d := json.NewDecoder(strings.NewReader(call.Argument(0).toString().String()))
    21  
    22  	value, err := r.builtinJSON_decodeValue(d)
    23  	if err != nil {
    24  		panic(r.newError(r.global.SyntaxError, err.Error()))
    25  	}
    26  
    27  	if tok, err := d.Token(); err != io.EOF {
    28  		panic(r.newError(r.global.SyntaxError, "Unexpected token at the end: %v", tok))
    29  	}
    30  
    31  	var reviver func(FunctionCall) Value
    32  
    33  	if arg1 := call.Argument(1); arg1 != _undefined {
    34  		reviver, _ = arg1.ToObject(r).self.assertCallable()
    35  	}
    36  
    37  	if reviver != nil {
    38  		root := r.NewObject()
    39  		createDataPropertyOrThrow(root, stringEmpty, value)
    40  		return r.builtinJSON_reviveWalk(reviver, root, stringEmpty)
    41  	}
    42  
    43  	return value
    44  }
    45  
    46  func (r *Runtime) builtinJSON_decodeToken(d *json.Decoder, tok json.Token) (Value, error) {
    47  	switch tok := tok.(type) {
    48  	case json.Delim:
    49  		switch tok {
    50  		case '{':
    51  			return r.builtinJSON_decodeObject(d)
    52  		case '[':
    53  			return r.builtinJSON_decodeArray(d)
    54  		}
    55  	case nil:
    56  		return _null, nil
    57  	case string:
    58  		return newStringValue(tok), nil
    59  	case float64:
    60  		return floatToValue(tok), nil
    61  	case bool:
    62  		if tok {
    63  			return valueTrue, nil
    64  		}
    65  		return valueFalse, nil
    66  	}
    67  	return nil, fmt.Errorf("Unexpected token (%T): %v", tok, tok)
    68  }
    69  
    70  func (r *Runtime) builtinJSON_decodeValue(d *json.Decoder) (Value, error) {
    71  	tok, err := d.Token()
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  	return r.builtinJSON_decodeToken(d, tok)
    76  }
    77  
    78  func (r *Runtime) builtinJSON_decodeObject(d *json.Decoder) (*Object, error) {
    79  	object := r.NewObject()
    80  	for {
    81  		key, end, err := r.builtinJSON_decodeObjectKey(d)
    82  		if err != nil {
    83  			return nil, err
    84  		}
    85  		if end {
    86  			break
    87  		}
    88  		value, err := r.builtinJSON_decodeValue(d)
    89  		if err != nil {
    90  			return nil, err
    91  		}
    92  
    93  		object.self._putProp(unistring.NewFromString(key), value, true, true, true)
    94  	}
    95  	return object, nil
    96  }
    97  
    98  func (r *Runtime) builtinJSON_decodeObjectKey(d *json.Decoder) (string, bool, error) {
    99  	tok, err := d.Token()
   100  	if err != nil {
   101  		return "", false, err
   102  	}
   103  	switch tok := tok.(type) {
   104  	case json.Delim:
   105  		if tok == '}' {
   106  			return "", true, nil
   107  		}
   108  	case string:
   109  		return tok, false, nil
   110  	}
   111  
   112  	return "", false, fmt.Errorf("Unexpected token (%T): %v", tok, tok)
   113  }
   114  
   115  func (r *Runtime) builtinJSON_decodeArray(d *json.Decoder) (*Object, error) {
   116  	var arrayValue []Value
   117  	for {
   118  		tok, err := d.Token()
   119  		if err != nil {
   120  			return nil, err
   121  		}
   122  		if delim, ok := tok.(json.Delim); ok {
   123  			if delim == ']' {
   124  				break
   125  			}
   126  		}
   127  		value, err := r.builtinJSON_decodeToken(d, tok)
   128  		if err != nil {
   129  			return nil, err
   130  		}
   131  		arrayValue = append(arrayValue, value)
   132  	}
   133  	return r.newArrayValues(arrayValue), nil
   134  }
   135  
   136  func (r *Runtime) builtinJSON_reviveWalk(reviver func(FunctionCall) Value, holder *Object, name Value) Value {
   137  	value := nilSafe(holder.get(name, nil))
   138  
   139  	if object, ok := value.(*Object); ok {
   140  		if isArray(object) {
   141  			length := toLength(object.self.getStr("length", nil))
   142  			for index := int64(0); index < length; index++ {
   143  				name := asciiString(strconv.FormatInt(index, 10))
   144  				value := r.builtinJSON_reviveWalk(reviver, object, name)
   145  				if value == _undefined {
   146  					object.delete(name, false)
   147  				} else {
   148  					createDataProperty(object, name, value)
   149  				}
   150  			}
   151  		} else {
   152  			for _, name := range object.self.stringKeys(false, nil) {
   153  				value := r.builtinJSON_reviveWalk(reviver, object, name)
   154  				if value == _undefined {
   155  					object.self.deleteStr(name.string(), false)
   156  				} else {
   157  					createDataProperty(object, name, value)
   158  				}
   159  			}
   160  		}
   161  	}
   162  	return reviver(FunctionCall{
   163  		This:      holder,
   164  		Arguments: []Value{name, value},
   165  	})
   166  }
   167  
   168  type _builtinJSON_stringifyContext struct {
   169  	r                *Runtime
   170  	stack            []*Object
   171  	propertyList     []Value
   172  	replacerFunction func(FunctionCall) Value
   173  	gap, indent      string
   174  	buf              bytes.Buffer
   175  	allAscii         bool
   176  }
   177  
   178  func (r *Runtime) builtinJSON_stringify(call FunctionCall) Value {
   179  	ctx := _builtinJSON_stringifyContext{
   180  		r:        r,
   181  		allAscii: true,
   182  	}
   183  
   184  	replacer, _ := call.Argument(1).(*Object)
   185  	if replacer != nil {
   186  		if isArray(replacer) {
   187  			length := toLength(replacer.self.getStr("length", nil))
   188  			seen := map[string]bool{}
   189  			propertyList := make([]Value, length)
   190  			length = 0
   191  			for index := range propertyList {
   192  				var name string
   193  				value := replacer.self.getIdx(valueInt(int64(index)), nil)
   194  				switch v := value.(type) {
   195  				case valueFloat, valueInt, valueString:
   196  					name = value.String()
   197  				case *Object:
   198  					switch v.self.className() {
   199  					case classNumber, classString:
   200  						name = value.String()
   201  					default:
   202  						continue
   203  					}
   204  				default:
   205  					continue
   206  				}
   207  				if seen[name] {
   208  					continue
   209  				}
   210  				seen[name] = true
   211  				propertyList[length] = newStringValue(name)
   212  				length += 1
   213  			}
   214  			ctx.propertyList = propertyList[0:length]
   215  		} else if c, ok := replacer.self.assertCallable(); ok {
   216  			ctx.replacerFunction = c
   217  		}
   218  	}
   219  	if spaceValue := call.Argument(2); spaceValue != _undefined {
   220  		if o, ok := spaceValue.(*Object); ok {
   221  			switch oImpl := o.self.(type) {
   222  			case *primitiveValueObject:
   223  				switch oImpl.pValue.(type) {
   224  				case valueInt, valueFloat:
   225  					spaceValue = o.ToNumber()
   226  				}
   227  			case *stringObject:
   228  				spaceValue = o.ToString()
   229  			}
   230  		}
   231  		isNum := false
   232  		var num int64
   233  		if i, ok := spaceValue.(valueInt); ok {
   234  			num = int64(i)
   235  			isNum = true
   236  		} else if f, ok := spaceValue.(valueFloat); ok {
   237  			num = int64(f)
   238  			isNum = true
   239  		}
   240  		if isNum {
   241  			if num > 0 {
   242  				if num > 10 {
   243  					num = 10
   244  				}
   245  				ctx.gap = strings.Repeat(" ", int(num))
   246  			}
   247  		} else {
   248  			if s, ok := spaceValue.(valueString); ok {
   249  				str := s.String()
   250  				if len(str) > 10 {
   251  					ctx.gap = str[:10]
   252  				} else {
   253  					ctx.gap = str
   254  				}
   255  			}
   256  		}
   257  	}
   258  
   259  	if ctx.do(call.Argument(0)) {
   260  		if ctx.allAscii {
   261  			return asciiString(ctx.buf.String())
   262  		} else {
   263  			return &importedString{
   264  				s: ctx.buf.String(),
   265  			}
   266  		}
   267  	}
   268  	return _undefined
   269  }
   270  
   271  func (ctx *_builtinJSON_stringifyContext) do(v Value) bool {
   272  	holder := ctx.r.NewObject()
   273  	createDataPropertyOrThrow(holder, stringEmpty, v)
   274  	return ctx.str(stringEmpty, holder)
   275  }
   276  
   277  func (ctx *_builtinJSON_stringifyContext) str(key Value, holder *Object) bool {
   278  	value := nilSafe(holder.get(key, nil))
   279  
   280  	if object, ok := value.(*Object); ok {
   281  		if toJSON, ok := object.self.getStr("toJSON", nil).(*Object); ok {
   282  			if c, ok := toJSON.self.assertCallable(); ok {
   283  				value = c(FunctionCall{
   284  					This:      value,
   285  					Arguments: []Value{key},
   286  				})
   287  			}
   288  		}
   289  	}
   290  
   291  	if ctx.replacerFunction != nil {
   292  		value = ctx.replacerFunction(FunctionCall{
   293  			This:      holder,
   294  			Arguments: []Value{key, value},
   295  		})
   296  	}
   297  
   298  	if o, ok := value.(*Object); ok {
   299  		switch o1 := o.self.(type) {
   300  		case *primitiveValueObject:
   301  			switch pValue := o1.pValue.(type) {
   302  			case valueInt, valueFloat:
   303  				value = o.ToNumber()
   304  			default:
   305  				value = pValue
   306  			}
   307  		case *stringObject:
   308  			value = o.toString()
   309  		case *objectGoReflect:
   310  			if o1.toJson != nil {
   311  				value = ctx.r.ToValue(o1.toJson())
   312  			} else if v, ok := o1.origValue.Interface().(json.Marshaler); ok {
   313  				b, err := v.MarshalJSON()
   314  				if err != nil {
   315  					panic(err)
   316  				}
   317  				ctx.buf.Write(b)
   318  				ctx.allAscii = false
   319  				return true
   320  			} else {
   321  				switch o1.className() {
   322  				case classNumber:
   323  					value = o1.toPrimitiveNumber()
   324  				case classString:
   325  					value = o1.toPrimitiveString()
   326  				case classBoolean:
   327  					if o.ToInteger() != 0 {
   328  						value = valueTrue
   329  					} else {
   330  						value = valueFalse
   331  					}
   332  				}
   333  			}
   334  		}
   335  	}
   336  
   337  	switch value1 := value.(type) {
   338  	case valueBool:
   339  		if value1 {
   340  			ctx.buf.WriteString("true")
   341  		} else {
   342  			ctx.buf.WriteString("false")
   343  		}
   344  	case valueString:
   345  		ctx.quote(value1)
   346  	case valueInt:
   347  		ctx.buf.WriteString(value.String())
   348  	case valueFloat:
   349  		if !math.IsNaN(float64(value1)) && !math.IsInf(float64(value1), 0) {
   350  			ctx.buf.WriteString(value.String())
   351  		} else {
   352  			ctx.buf.WriteString("null")
   353  		}
   354  	case valueNull:
   355  		ctx.buf.WriteString("null")
   356  	case *Object:
   357  		for _, object := range ctx.stack {
   358  			if value1 == object {
   359  				ctx.r.typeErrorResult(true, "Converting circular structure to JSON")
   360  			}
   361  		}
   362  		ctx.stack = append(ctx.stack, value1)
   363  		defer func() { ctx.stack = ctx.stack[:len(ctx.stack)-1] }()
   364  		if _, ok := value1.self.assertCallable(); !ok {
   365  			if isArray(value1) {
   366  				ctx.ja(value1)
   367  			} else {
   368  				ctx.jo(value1)
   369  			}
   370  		} else {
   371  			return false
   372  		}
   373  	default:
   374  		return false
   375  	}
   376  	return true
   377  }
   378  
   379  func (ctx *_builtinJSON_stringifyContext) ja(array *Object) {
   380  	var stepback string
   381  	if ctx.gap != "" {
   382  		stepback = ctx.indent
   383  		ctx.indent += ctx.gap
   384  	}
   385  	length := toLength(array.self.getStr("length", nil))
   386  	if length == 0 {
   387  		ctx.buf.WriteString("[]")
   388  		return
   389  	}
   390  
   391  	ctx.buf.WriteByte('[')
   392  	var separator string
   393  	if ctx.gap != "" {
   394  		ctx.buf.WriteByte('\n')
   395  		ctx.buf.WriteString(ctx.indent)
   396  		separator = ",\n" + ctx.indent
   397  	} else {
   398  		separator = ","
   399  	}
   400  
   401  	for i := int64(0); i < length; i++ {
   402  		if !ctx.str(asciiString(strconv.FormatInt(i, 10)), array) {
   403  			ctx.buf.WriteString("null")
   404  		}
   405  		if i < length-1 {
   406  			ctx.buf.WriteString(separator)
   407  		}
   408  	}
   409  	if ctx.gap != "" {
   410  		ctx.buf.WriteByte('\n')
   411  		ctx.buf.WriteString(stepback)
   412  		ctx.indent = stepback
   413  	}
   414  	ctx.buf.WriteByte(']')
   415  }
   416  
   417  func (ctx *_builtinJSON_stringifyContext) jo(object *Object) {
   418  	var stepback string
   419  	if ctx.gap != "" {
   420  		stepback = ctx.indent
   421  		ctx.indent += ctx.gap
   422  	}
   423  
   424  	ctx.buf.WriteByte('{')
   425  	mark := ctx.buf.Len()
   426  	var separator string
   427  	if ctx.gap != "" {
   428  		ctx.buf.WriteByte('\n')
   429  		ctx.buf.WriteString(ctx.indent)
   430  		separator = ",\n" + ctx.indent
   431  	} else {
   432  		separator = ","
   433  	}
   434  
   435  	var props []Value
   436  	if ctx.propertyList == nil {
   437  		props = object.self.stringKeys(false, nil)
   438  	} else {
   439  		props = ctx.propertyList
   440  	}
   441  
   442  	empty := true
   443  	for _, name := range props {
   444  		off := ctx.buf.Len()
   445  		if !empty {
   446  			ctx.buf.WriteString(separator)
   447  		}
   448  		ctx.quote(name.toString())
   449  		if ctx.gap != "" {
   450  			ctx.buf.WriteString(": ")
   451  		} else {
   452  			ctx.buf.WriteByte(':')
   453  		}
   454  		if ctx.str(name, object) {
   455  			if empty {
   456  				empty = false
   457  			}
   458  		} else {
   459  			ctx.buf.Truncate(off)
   460  		}
   461  	}
   462  
   463  	if empty {
   464  		ctx.buf.Truncate(mark)
   465  	} else {
   466  		if ctx.gap != "" {
   467  			ctx.buf.WriteByte('\n')
   468  			ctx.buf.WriteString(stepback)
   469  			ctx.indent = stepback
   470  		}
   471  	}
   472  	ctx.buf.WriteByte('}')
   473  }
   474  
   475  func (ctx *_builtinJSON_stringifyContext) quote(str valueString) {
   476  	ctx.buf.WriteByte('"')
   477  	reader := &lenientUtf16Decoder{utf16Reader: str.utf16Reader()}
   478  	for {
   479  		r, _, err := reader.ReadRune()
   480  		if err != nil {
   481  			break
   482  		}
   483  		switch r {
   484  		case '"', '\\':
   485  			ctx.buf.WriteByte('\\')
   486  			ctx.buf.WriteByte(byte(r))
   487  		case 0x08:
   488  			ctx.buf.WriteString(`\b`)
   489  		case 0x09:
   490  			ctx.buf.WriteString(`\t`)
   491  		case 0x0A:
   492  			ctx.buf.WriteString(`\n`)
   493  		case 0x0C:
   494  			ctx.buf.WriteString(`\f`)
   495  		case 0x0D:
   496  			ctx.buf.WriteString(`\r`)
   497  		default:
   498  			if r < 0x20 {
   499  				ctx.buf.WriteString(`\u00`)
   500  				ctx.buf.WriteByte(hex[r>>4])
   501  				ctx.buf.WriteByte(hex[r&0xF])
   502  			} else {
   503  				if utf16.IsSurrogate(r) {
   504  					ctx.buf.WriteString(`\u`)
   505  					ctx.buf.WriteByte(hex[r>>12])
   506  					ctx.buf.WriteByte(hex[(r>>8)&0xF])
   507  					ctx.buf.WriteByte(hex[(r>>4)&0xF])
   508  					ctx.buf.WriteByte(hex[r&0xF])
   509  				} else {
   510  					ctx.buf.WriteRune(r)
   511  					if ctx.allAscii && r >= utf8.RuneSelf {
   512  						ctx.allAscii = false
   513  					}
   514  				}
   515  			}
   516  		}
   517  	}
   518  	ctx.buf.WriteByte('"')
   519  }
   520  
   521  func (r *Runtime) initJSON() {
   522  	JSON := r.newBaseObject(r.global.ObjectPrototype, "JSON")
   523  	JSON._putProp("parse", r.newNativeFunc(r.builtinJSON_parse, nil, "parse", nil, 2), true, false, true)
   524  	JSON._putProp("stringify", r.newNativeFunc(r.builtinJSON_stringify, nil, "stringify", nil, 3), true, false, true)
   525  	JSON._putSym(SymToStringTag, valueProp(asciiString(classJSON), false, false, true))
   526  
   527  	r.addToGlobal("JSON", JSON.val)
   528  }