123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542 |
- package goja
- import (
- "bytes"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "math"
- "strconv"
- "strings"
- "unicode/utf16"
- "unicode/utf8"
- "github.com/dop251/goja/unistring"
- )
- const hex = "0123456789abcdef"
- func (r *Runtime) builtinJSON_parse(call FunctionCall) Value {
- d := json.NewDecoder(strings.NewReader(call.Argument(0).toString().String()))
- value, err := r.builtinJSON_decodeValue(d)
- if errors.Is(err, io.EOF) {
- panic(r.newError(r.getSyntaxError(), "Unexpected end of JSON input (%v)", err.Error()))
- }
- if err != nil {
- panic(r.newError(r.getSyntaxError(), err.Error()))
- }
- if tok, err := d.Token(); err != io.EOF {
- panic(r.newError(r.getSyntaxError(), "Unexpected token at the end: %v", tok))
- }
- var reviver func(FunctionCall) Value
- if arg1 := call.Argument(1); arg1 != _undefined {
- reviver, _ = arg1.ToObject(r).self.assertCallable()
- }
- if reviver != nil {
- root := r.NewObject()
- createDataPropertyOrThrow(root, stringEmpty, value)
- return r.builtinJSON_reviveWalk(reviver, root, stringEmpty)
- }
- return value
- }
- func (r *Runtime) builtinJSON_decodeToken(d *json.Decoder, tok json.Token) (Value, error) {
- switch tok := tok.(type) {
- case json.Delim:
- switch tok {
- case '{':
- return r.builtinJSON_decodeObject(d)
- case '[':
- return r.builtinJSON_decodeArray(d)
- }
- case nil:
- return _null, nil
- case string:
- return newStringValue(tok), nil
- case float64:
- return floatToValue(tok), nil
- case bool:
- if tok {
- return valueTrue, nil
- }
- return valueFalse, nil
- }
- return nil, fmt.Errorf("Unexpected token (%T): %v", tok, tok)
- }
- func (r *Runtime) builtinJSON_decodeValue(d *json.Decoder) (Value, error) {
- tok, err := d.Token()
- if err != nil {
- return nil, err
- }
- return r.builtinJSON_decodeToken(d, tok)
- }
- func (r *Runtime) builtinJSON_decodeObject(d *json.Decoder) (*Object, error) {
- object := r.NewObject()
- for {
- key, end, err := r.builtinJSON_decodeObjectKey(d)
- if err != nil {
- return nil, err
- }
- if end {
- break
- }
- value, err := r.builtinJSON_decodeValue(d)
- if err != nil {
- return nil, err
- }
- object.self._putProp(unistring.NewFromString(key), value, true, true, true)
- }
- return object, nil
- }
- func (r *Runtime) builtinJSON_decodeObjectKey(d *json.Decoder) (string, bool, error) {
- tok, err := d.Token()
- if err != nil {
- return "", false, err
- }
- switch tok := tok.(type) {
- case json.Delim:
- if tok == '}' {
- return "", true, nil
- }
- case string:
- return tok, false, nil
- }
- return "", false, fmt.Errorf("Unexpected token (%T): %v", tok, tok)
- }
- func (r *Runtime) builtinJSON_decodeArray(d *json.Decoder) (*Object, error) {
- var arrayValue []Value
- for {
- tok, err := d.Token()
- if err != nil {
- return nil, err
- }
- if delim, ok := tok.(json.Delim); ok {
- if delim == ']' {
- break
- }
- }
- value, err := r.builtinJSON_decodeToken(d, tok)
- if err != nil {
- return nil, err
- }
- arrayValue = append(arrayValue, value)
- }
- return r.newArrayValues(arrayValue), nil
- }
- func (r *Runtime) builtinJSON_reviveWalk(reviver func(FunctionCall) Value, holder *Object, name Value) Value {
- value := nilSafe(holder.get(name, nil))
- if object, ok := value.(*Object); ok {
- if isArray(object) {
- length := toLength(object.self.getStr("length", nil))
- for index := int64(0); index < length; index++ {
- name := asciiString(strconv.FormatInt(index, 10))
- value := r.builtinJSON_reviveWalk(reviver, object, name)
- if value == _undefined {
- object.delete(name, false)
- } else {
- createDataProperty(object, name, value)
- }
- }
- } else {
- for _, name := range object.self.stringKeys(false, nil) {
- value := r.builtinJSON_reviveWalk(reviver, object, name)
- if value == _undefined {
- object.self.deleteStr(name.string(), false)
- } else {
- createDataProperty(object, name, value)
- }
- }
- }
- }
- return reviver(FunctionCall{
- This: holder,
- Arguments: []Value{name, value},
- })
- }
- type _builtinJSON_stringifyContext struct {
- r *Runtime
- stack []*Object
- propertyList []Value
- replacerFunction func(FunctionCall) Value
- gap, indent string
- buf bytes.Buffer
- allAscii bool
- }
- func (r *Runtime) builtinJSON_stringify(call FunctionCall) Value {
- ctx := _builtinJSON_stringifyContext{
- r: r,
- allAscii: true,
- }
- replacer, _ := call.Argument(1).(*Object)
- if replacer != nil {
- if isArray(replacer) {
- length := toLength(replacer.self.getStr("length", nil))
- seen := map[string]bool{}
- propertyList := make([]Value, length)
- length = 0
- for index := range propertyList {
- var name string
- value := replacer.self.getIdx(valueInt(int64(index)), nil)
- switch v := value.(type) {
- case valueFloat, valueInt, String:
- name = value.String()
- case *Object:
- switch v.self.className() {
- case classNumber, classString:
- name = value.String()
- default:
- continue
- }
- default:
- continue
- }
- if seen[name] {
- continue
- }
- seen[name] = true
- propertyList[length] = newStringValue(name)
- length += 1
- }
- ctx.propertyList = propertyList[0:length]
- } else if c, ok := replacer.self.assertCallable(); ok {
- ctx.replacerFunction = c
- }
- }
- if spaceValue := call.Argument(2); spaceValue != _undefined {
- if o, ok := spaceValue.(*Object); ok {
- switch oImpl := o.self.(type) {
- case *primitiveValueObject:
- switch oImpl.pValue.(type) {
- case valueInt, valueFloat:
- spaceValue = o.ToNumber()
- }
- case *stringObject:
- spaceValue = o.ToString()
- }
- }
- isNum := false
- var num int64
- if i, ok := spaceValue.(valueInt); ok {
- num = int64(i)
- isNum = true
- } else if f, ok := spaceValue.(valueFloat); ok {
- num = int64(f)
- isNum = true
- }
- if isNum {
- if num > 0 {
- if num > 10 {
- num = 10
- }
- ctx.gap = strings.Repeat(" ", int(num))
- }
- } else {
- if s, ok := spaceValue.(String); ok {
- str := s.String()
- if len(str) > 10 {
- ctx.gap = str[:10]
- } else {
- ctx.gap = str
- }
- }
- }
- }
- if ctx.do(call.Argument(0)) {
- if ctx.allAscii {
- return asciiString(ctx.buf.String())
- } else {
- return &importedString{
- s: ctx.buf.String(),
- }
- }
- }
- return _undefined
- }
- func (ctx *_builtinJSON_stringifyContext) do(v Value) bool {
- holder := ctx.r.NewObject()
- createDataPropertyOrThrow(holder, stringEmpty, v)
- return ctx.str(stringEmpty, holder)
- }
- func (ctx *_builtinJSON_stringifyContext) str(key Value, holder *Object) bool {
- value := nilSafe(holder.get(key, nil))
- switch value.(type) {
- case *Object, *valueBigInt:
- if toJSON, ok := ctx.r.getVStr(value, "toJSON").(*Object); ok {
- if c, ok := toJSON.self.assertCallable(); ok {
- value = c(FunctionCall{
- This: value,
- Arguments: []Value{key},
- })
- }
- }
- }
- if ctx.replacerFunction != nil {
- value = ctx.replacerFunction(FunctionCall{
- This: holder,
- Arguments: []Value{key, value},
- })
- }
- if o, ok := value.(*Object); ok {
- switch o1 := o.self.(type) {
- case *primitiveValueObject:
- switch pValue := o1.pValue.(type) {
- case valueInt, valueFloat:
- value = o.ToNumber()
- default:
- value = pValue
- }
- case *stringObject:
- value = o.toString()
- case *objectGoReflect:
- if o1.toJson != nil {
- value = ctx.r.ToValue(o1.toJson())
- } else if v, ok := o1.origValue.Interface().(json.Marshaler); ok {
- b, err := v.MarshalJSON()
- if err != nil {
- panic(ctx.r.NewGoError(err))
- }
- ctx.buf.Write(b)
- ctx.allAscii = false
- return true
- } else {
- switch o1.className() {
- case classNumber:
- value = o1.val.ordinaryToPrimitiveNumber()
- case classString:
- value = o1.val.ordinaryToPrimitiveString()
- case classBoolean:
- if o.ToInteger() != 0 {
- value = valueTrue
- } else {
- value = valueFalse
- }
- }
- if o1.exportType() == typeBigInt {
- value = o1.val.ordinaryToPrimitiveNumber()
- }
- }
- }
- }
- switch value1 := value.(type) {
- case valueBool:
- if value1 {
- ctx.buf.WriteString("true")
- } else {
- ctx.buf.WriteString("false")
- }
- case String:
- ctx.quote(value1)
- case valueInt:
- ctx.buf.WriteString(value.String())
- case valueFloat:
- if !math.IsNaN(float64(value1)) && !math.IsInf(float64(value1), 0) {
- ctx.buf.WriteString(value.String())
- } else {
- ctx.buf.WriteString("null")
- }
- case valueNull:
- ctx.buf.WriteString("null")
- case *valueBigInt:
- ctx.r.typeErrorResult(true, "Do not know how to serialize a BigInt")
- case *Object:
- for _, object := range ctx.stack {
- if value1.SameAs(object) {
- ctx.r.typeErrorResult(true, "Converting circular structure to JSON")
- }
- }
- ctx.stack = append(ctx.stack, value1)
- defer func() { ctx.stack = ctx.stack[:len(ctx.stack)-1] }()
- if _, ok := value1.self.assertCallable(); !ok {
- if isArray(value1) {
- ctx.ja(value1)
- } else {
- ctx.jo(value1)
- }
- } else {
- return false
- }
- default:
- return false
- }
- return true
- }
- func (ctx *_builtinJSON_stringifyContext) ja(array *Object) {
- var stepback string
- if ctx.gap != "" {
- stepback = ctx.indent
- ctx.indent += ctx.gap
- }
- length := toLength(array.self.getStr("length", nil))
- if length == 0 {
- ctx.buf.WriteString("[]")
- return
- }
- ctx.buf.WriteByte('[')
- var separator string
- if ctx.gap != "" {
- ctx.buf.WriteByte('\n')
- ctx.buf.WriteString(ctx.indent)
- separator = ",\n" + ctx.indent
- } else {
- separator = ","
- }
- for i := int64(0); i < length; i++ {
- if !ctx.str(asciiString(strconv.FormatInt(i, 10)), array) {
- ctx.buf.WriteString("null")
- }
- if i < length-1 {
- ctx.buf.WriteString(separator)
- }
- }
- if ctx.gap != "" {
- ctx.buf.WriteByte('\n')
- ctx.buf.WriteString(stepback)
- ctx.indent = stepback
- }
- ctx.buf.WriteByte(']')
- }
- func (ctx *_builtinJSON_stringifyContext) jo(object *Object) {
- var stepback string
- if ctx.gap != "" {
- stepback = ctx.indent
- ctx.indent += ctx.gap
- }
- ctx.buf.WriteByte('{')
- mark := ctx.buf.Len()
- var separator string
- if ctx.gap != "" {
- ctx.buf.WriteByte('\n')
- ctx.buf.WriteString(ctx.indent)
- separator = ",\n" + ctx.indent
- } else {
- separator = ","
- }
- var props []Value
- if ctx.propertyList == nil {
- props = object.self.stringKeys(false, nil)
- } else {
- props = ctx.propertyList
- }
- empty := true
- for _, name := range props {
- off := ctx.buf.Len()
- if !empty {
- ctx.buf.WriteString(separator)
- }
- ctx.quote(name.toString())
- if ctx.gap != "" {
- ctx.buf.WriteString(": ")
- } else {
- ctx.buf.WriteByte(':')
- }
- if ctx.str(name, object) {
- if empty {
- empty = false
- }
- } else {
- ctx.buf.Truncate(off)
- }
- }
- if empty {
- ctx.buf.Truncate(mark)
- } else {
- if ctx.gap != "" {
- ctx.buf.WriteByte('\n')
- ctx.buf.WriteString(stepback)
- ctx.indent = stepback
- }
- }
- ctx.buf.WriteByte('}')
- }
- func (ctx *_builtinJSON_stringifyContext) quote(str String) {
- ctx.buf.WriteByte('"')
- reader := &lenientUtf16Decoder{utf16Reader: str.utf16Reader()}
- for {
- r, _, err := reader.ReadRune()
- if err != nil {
- break
- }
- switch r {
- case '"', '\\':
- ctx.buf.WriteByte('\\')
- ctx.buf.WriteByte(byte(r))
- case 0x08:
- ctx.buf.WriteString(`\b`)
- case 0x09:
- ctx.buf.WriteString(`\t`)
- case 0x0A:
- ctx.buf.WriteString(`\n`)
- case 0x0C:
- ctx.buf.WriteString(`\f`)
- case 0x0D:
- ctx.buf.WriteString(`\r`)
- default:
- if r < 0x20 {
- ctx.buf.WriteString(`\u00`)
- ctx.buf.WriteByte(hex[r>>4])
- ctx.buf.WriteByte(hex[r&0xF])
- } else {
- if utf16.IsSurrogate(r) {
- ctx.buf.WriteString(`\u`)
- ctx.buf.WriteByte(hex[r>>12])
- ctx.buf.WriteByte(hex[(r>>8)&0xF])
- ctx.buf.WriteByte(hex[(r>>4)&0xF])
- ctx.buf.WriteByte(hex[r&0xF])
- } else {
- ctx.buf.WriteRune(r)
- if ctx.allAscii && r >= utf8.RuneSelf {
- ctx.allAscii = false
- }
- }
- }
- }
- }
- ctx.buf.WriteByte('"')
- }
- func (r *Runtime) getJSON() *Object {
- ret := r.global.JSON
- if ret == nil {
- JSON := r.newBaseObject(r.global.ObjectPrototype, classObject)
- ret = JSON.val
- r.global.JSON = ret
- JSON._putProp("parse", r.newNativeFunc(r.builtinJSON_parse, "parse", 2), true, false, true)
- JSON._putProp("stringify", r.newNativeFunc(r.builtinJSON_stringify, "stringify", 3), true, false, true)
- JSON._putSym(SymToStringTag, valueProp(asciiString(classJSON), false, false, true))
- }
- return ret
- }
|