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 }