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/dop251/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.getSyntaxError(), "Unexpected end of JSON input (%v)", err.Error()))
26 }
27 if err != nil {
28 panic(r.newError(r.getSyntaxError(), err.Error()))
29 }
30
31 if tok, err := d.Token(); err != io.EOF {
32 panic(r.newError(r.getSyntaxError(), "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(ctx.r.NewGoError(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.SameAs(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) getJSON() *Object {
526 ret := r.global.JSON
527 if ret == nil {
528 JSON := r.newBaseObject(r.global.ObjectPrototype, classObject)
529 ret = JSON.val
530 r.global.JSON = ret
531 JSON._putProp("parse", r.newNativeFunc(r.builtinJSON_parse, "parse", 2), true, false, true)
532 JSON._putProp("stringify", r.newNativeFunc(r.builtinJSON_stringify, "stringify", 3), true, false, true)
533 JSON._putSym(SymToStringTag, valueProp(asciiString(classJSON), false, false, true))
534 }
535 return ret
536 }
537
View as plain text