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 }