File size: 6,101 Bytes
e36aeda
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build goexperiment.jsonv2

package jsontext

import (
	"bytes"
	"io"
	"strconv"

	"encoding/json/internal/jsonwire"
)

const errorPrefix = "jsontext: "

type ioError struct {
	action string // either "read" or "write"
	err    error
}

func (e *ioError) Error() string {
	return errorPrefix + e.action + " error: " + e.err.Error()
}
func (e *ioError) Unwrap() error {
	return e.err
}

// SyntacticError is a description of a syntactic error that occurred when
// encoding or decoding JSON according to the grammar.
//
// The contents of this error as produced by this package may change over time.
type SyntacticError struct {
	requireKeyedLiterals
	nonComparable

	// ByteOffset indicates that an error occurred after this byte offset.
	ByteOffset int64
	// JSONPointer indicates that an error occurred within this JSON value
	// as indicated using the JSON Pointer notation (see RFC 6901).
	JSONPointer Pointer

	// Err is the underlying error.
	Err error
}

// wrapSyntacticError wraps an error and annotates it with a precise location
// using the provided [encoderState] or [decoderState].
// If err is an [ioError] or [io.EOF], then it is not wrapped.
//
// It takes a relative offset pos that can be resolved into
// an absolute offset using state.offsetAt.
//
// It takes a where that specify how the JSON pointer is derived.
// If the underlying error is a [pointerSuffixError],
// then the suffix is appended to the derived pointer.
func wrapSyntacticError(state interface {
	offsetAt(pos int) int64
	AppendStackPointer(b []byte, where int) []byte
}, err error, pos, where int) error {
	if _, ok := err.(*ioError); err == io.EOF || ok {
		return err
	}
	offset := state.offsetAt(pos)
	ptr := state.AppendStackPointer(nil, where)
	if serr, ok := err.(*pointerSuffixError); ok {
		ptr = serr.appendPointer(ptr)
		err = serr.error
	}
	if d, ok := state.(*decoderState); ok && err == errMismatchDelim {
		where := "at start of value"
		if len(d.Tokens.Stack) > 0 && d.Tokens.Last.Length() > 0 {
			switch {
			case d.Tokens.Last.isArray():
				where = "after array element (expecting ',' or ']')"
				ptr = []byte(Pointer(ptr).Parent()) // problem is with parent array
			case d.Tokens.Last.isObject():
				where = "after object value (expecting ',' or '}')"
				ptr = []byte(Pointer(ptr).Parent()) // problem is with parent object
			}
		}
		err = jsonwire.NewInvalidCharacterError(d.buf[pos:], where)
	}
	return &SyntacticError{ByteOffset: offset, JSONPointer: Pointer(ptr), Err: err}
}

func (e *SyntacticError) Error() string {
	pointer := e.JSONPointer
	offset := e.ByteOffset
	b := []byte(errorPrefix)
	if e.Err != nil {
		b = append(b, e.Err.Error()...)
		if e.Err == ErrDuplicateName {
			b = strconv.AppendQuote(append(b, ' '), pointer.LastToken())
			pointer = pointer.Parent()
			offset = 0 // not useful to print offset for duplicate names
		}
	} else {
		b = append(b, "syntactic error"...)
	}
	if pointer != "" {
		b = strconv.AppendQuote(append(b, " within "...), jsonwire.TruncatePointer(string(pointer), 100))
	}
	if offset > 0 {
		b = strconv.AppendInt(append(b, " after offset "...), offset, 10)
	}
	return string(b)
}

func (e *SyntacticError) Unwrap() error {
	return e.Err
}

// pointerSuffixError represents a JSON pointer suffix to be appended
// to [SyntacticError.JSONPointer]. It is an internal error type
// used within this package and does not appear in the public API.
//
// This type is primarily used to annotate errors in Encoder.WriteValue
// and Decoder.ReadValue with precise positions.
// At the time WriteValue or ReadValue is called, a JSON pointer to the
// upcoming value can be constructed using the Encoder/Decoder state.
// However, tracking pointers within values during normal operation
// would incur a performance penalty in the error-free case.
//
// To provide precise error locations without this overhead,
// the error is wrapped with object names or array indices
// as the call stack is popped when an error occurs.
// Since this happens in reverse order, pointerSuffixError holds
// the pointer in reverse and is only later reversed when appending to
// the pointer prefix.
//
// For example, if the encoder is at "/alpha/bravo/charlie"
// and an error occurs in WriteValue at "/xray/yankee/zulu", then
// the final pointer should be "/alpha/bravo/charlie/xray/yankee/zulu".
//
// As pointerSuffixError is populated during the error return path,
// it first contains "/zulu", then "/zulu/yankee",
// and finally "/zulu/yankee/xray".
// These tokens are reversed and concatenated to "/alpha/bravo/charlie"
// to form the full pointer.
type pointerSuffixError struct {
	error

	// reversePointer is a JSON pointer, but with each token in reverse order.
	reversePointer []byte
}

// wrapWithObjectName wraps err with a JSON object name access,
// which must be a valid quoted JSON string.
func wrapWithObjectName(err error, quotedName []byte) error {
	serr, _ := err.(*pointerSuffixError)
	if serr == nil {
		serr = &pointerSuffixError{error: err}
	}
	name := jsonwire.UnquoteMayCopy(quotedName, false)
	serr.reversePointer = appendEscapePointerName(append(serr.reversePointer, '/'), name)
	return serr
}

// wrapWithArrayIndex wraps err with a JSON array index access.
func wrapWithArrayIndex(err error, index int64) error {
	serr, _ := err.(*pointerSuffixError)
	if serr == nil {
		serr = &pointerSuffixError{error: err}
	}
	serr.reversePointer = strconv.AppendUint(append(serr.reversePointer, '/'), uint64(index), 10)
	return serr
}

// appendPointer appends the path encoded in e to the end of pointer.
func (e *pointerSuffixError) appendPointer(pointer []byte) []byte {
	// Copy each token in reversePointer to the end of pointer in reverse order.
	// Double reversal means that the appended suffix is now in forward order.
	bi, bo := e.reversePointer, pointer
	for len(bi) > 0 {
		i := bytes.LastIndexByte(bi, '/')
		bi, bo = bi[:i], append(bo, bi[i:]...)
	}
	return bo
}