diff options
Diffstat (limited to 'decode.go')
-rw-r--r-- | decode.go | 66 |
1 files changed, 43 insertions, 23 deletions
@@ -91,20 +91,25 @@ func (dec *Decoder) stackStr() string { } func (dec *Decoder) stackParent() string { - if len(dec.stack) > 0 && dec.stack[len(dec.stack)-1].par.Kind() == reflect.Struct { - return dec.stack[len(dec.stack)-1].par.Name() + last := len(dec.stack) - 1 + if last > 0 && dec.stack[last].par.Kind() != reflect.Struct && dec.stack[last-1].par.Kind() == reflect.Struct { + last-- + } + if last >= 0 && dec.stack[last].par.Kind() == reflect.Struct { + return dec.stack[last].par.Name() } return "" } func (dec *Decoder) stackName() string { - var fields []string - for i := len(dec.stack) - 1; i >= 0 && dec.stack[i].par.Kind() == reflect.Struct; i-- { - fields = append(fields, dec.stack[i].idx.(string)) + if dec.stackParent() == "" { + return "" } - for i := 0; i < len(fields)/2; i++ { - j := (len(fields) - 1) - i - fields[i], fields[j] = fields[j], fields[i] + var fields []string + for _, elem := range dec.stack { + if elem.par.Kind() == reflect.Struct { + fields = append(fields, elem.idx.(string)) + } } return strings.Join(fields, ".") } @@ -298,10 +303,10 @@ func (dec *Decoder) decode(val reflect.Value, nullOK bool) { return } var buf bytes.Buffer - dec.decodeString(typ, &buf) + dec.decodeString(reflect.PointerTo(typ), &buf) obj := val.Addr().Interface().(encoding.TextUnmarshaler) if err := obj.UnmarshalText(buf.Bytes()); err != nil { - dec.panicType("string", typ, err) + dec.panicType("string", reflect.PointerTo(typ), err) } default: switch kind := typ.Kind(); kind { @@ -354,7 +359,13 @@ func (dec *Decoder) decode(val reflect.Value, nullOK bool) { } var buf strings.Builder if typ == numberType { - dec.scanNumber(typ, &buf) + t := dec.peekRuneType() + dec.scan(&buf) + if !t.IsNumber() { + dec.panicType(t.jsonType(), typ, + fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", + buf.String())) + } val.SetString(buf.String()) } else { dec.decodeString(typ, &buf) @@ -362,25 +373,32 @@ func (dec *Decoder) decode(val reflect.Value, nullOK bool) { } case reflect.Interface: if typ.NumMethod() > 0 { - dec.panicType("", typ, fmt.Errorf("cannot decode in to non-empty interface")) + dec.panicType(dec.peekRuneType().jsonType(), typ, ErrDecodeNonEmptyInterface) } // If the interface stores a pointer, try to use the type information of the pointer. if !val.IsNil() && val.Elem().Kind() == reflect.Pointer { // Follow a chain of pointers until we find the first settable // pointer (if any). ptr := val.Elem() - for ptr.Kind() == reflect.Pointer { - if ptr.CanSet() { - break - } - if ptr.IsNil() { + for { + if ptr.CanSet() || ptr.IsNil() || ptr.Elem().Kind() != reflect.Pointer { + // We've reached the end of the line, good or bad. break } ptr = ptr.Elem() } - // We only neet to be able to set the pointer itself if we're - // decoding "null", so add a "||" clause. - if ptr.Kind() == reflect.Pointer && (ptr.CanSet() || dec.peekRuneType() != RuneTypeNullN) { + // ptr.Elem() != val + // + // Avoid the loop of an interface storing a pointer to its own + // address. We only need to worry about this at the leaf (and not + // in the loop) because the only way it's possible is if there's + // an interface in there, which'd break from the loop on its own. + // + // ptr.CanSet() || dec.peekRuneType() != RuneTypeNullN + // + // We only need the pointer itself to be settable if we're + // decoding null. + if ptr.Elem() != val && (ptr.CanSet() || dec.peekRuneType() != RuneTypeNullN) { dec.decode(ptr, false) break } @@ -406,8 +424,8 @@ func (dec *Decoder) decode(val reflect.Value, nullOK bool) { defer dec.stackPop() idx, ok := index.byName[name] if !ok { - for oname, oidx := range index.byName { - if strings.EqualFold(name, oname) { + for oidx := range index.byPos { + if strings.EqualFold(name, index.byPos[oidx].Name) { idx = oidx ok = true break @@ -426,7 +444,9 @@ func (dec *Decoder) decode(val reflect.Value, nullOK bool) { for _, idx := range field.Path { if fVal.Kind() == reflect.Pointer { if fVal.IsNil() && !fVal.CanSet() { // https://golang.org/issue/21357 - dec.panicType("", fVal.Type().Elem(), fmt.Errorf("cannot set embedded pointer to unexported type")) + dec.panicType("", fVal.Type().Elem(), + fmt.Errorf("json: cannot set embedded pointer to unexported struct: %v", + fVal.Type().Elem())) } if dec.peekRuneType() != RuneTypeNullN { if fVal.IsNil() { |