github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/builtin_json.go (about)

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