mirror of
https://github.com/google/go-jsonnet.git
synced 2025-09-29 01:11:02 +02:00
Better stacktrace for manifestation, +: and object assertions.
Fixes #282
This commit is contained in:
parent
7d3bda3911
commit
6140a2f75a
@ -62,7 +62,12 @@ func desugarFields(nodeBase ast.NodeBase, fields *ast.ObjectFields, objLevel int
|
|||||||
if msg == nil {
|
if msg == nil {
|
||||||
msg = buildLiteralString("Object assertion failed.")
|
msg = buildLiteralString("Object assertion failed.")
|
||||||
}
|
}
|
||||||
onFailure := &ast.Error{Expr: msg}
|
onFailure := &ast.Error{
|
||||||
|
NodeBase: ast.NodeBase{
|
||||||
|
LocRange: field.LocRange,
|
||||||
|
},
|
||||||
|
Expr: msg,
|
||||||
|
}
|
||||||
asserts = append(asserts, &ast.Conditional{
|
asserts = append(asserts, &ast.Conditional{
|
||||||
NodeBase: ast.NodeBase{
|
NodeBase: ast.NodeBase{
|
||||||
LocRange: field.LocRange,
|
LocRange: field.LocRange,
|
||||||
|
@ -35,9 +35,9 @@ type environment struct {
|
|||||||
|
|
||||||
// Bindings introduced in this frame. The way previous bindings are treated
|
// Bindings introduced in this frame. The way previous bindings are treated
|
||||||
// depends on the type of a frame.
|
// depends on the type of a frame.
|
||||||
// If isCall == true then previous bindings are ignored (it's a clean
|
// If cleanEnv == true then previous bindings are ignored (it's a clean
|
||||||
// environment with just the variables we have here).
|
// environment with just the variables we have here).
|
||||||
// If isCall == false then if this frame doesn't contain a binding
|
// If cleanEnv == false then if this frame doesn't contain a binding
|
||||||
// previous bindings will be used.
|
// previous bindings will be used.
|
||||||
upValues bindingFrame
|
upValues bindingFrame
|
||||||
}
|
}
|
||||||
@ -52,7 +52,7 @@ func makeEnvironment(upValues bindingFrame, sb selfBinding) environment {
|
|||||||
func (i *interpreter) getCurrentStackTrace() []traceFrame {
|
func (i *interpreter) getCurrentStackTrace() []traceFrame {
|
||||||
var result []traceFrame
|
var result []traceFrame
|
||||||
for _, f := range i.stack.stack {
|
for _, f := range i.stack.stack {
|
||||||
if f.isCall {
|
if f.cleanEnv {
|
||||||
result = append(result, traceElementToTraceFrame(f.trace))
|
result = append(result, traceElementToTraceFrame(f.trace))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -65,8 +65,7 @@ func (i *interpreter) getCurrentStackTrace() []traceFrame {
|
|||||||
type callFrame struct {
|
type callFrame struct {
|
||||||
// True if it switches to a clean environment (function call or array element)
|
// True if it switches to a clean environment (function call or array element)
|
||||||
// False otherwise, e.g. for local
|
// False otherwise, e.g. for local
|
||||||
// This makes callFrame a misnomer as it is technically not always a call...
|
cleanEnv bool
|
||||||
isCall bool
|
|
||||||
|
|
||||||
// Tracing information about the place where it was called from.
|
// Tracing information about the place where it was called from.
|
||||||
trace traceElement
|
trace traceElement
|
||||||
@ -87,8 +86,8 @@ func dumpCallFrame(c *callFrame) string {
|
|||||||
} else {
|
} else {
|
||||||
loc = *c.trace.loc
|
loc = *c.trace.loc
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("<callFrame isCall = %t location = %v trimmable = %t>",
|
return fmt.Sprintf("<callFrame cleanEnv = %t location = %v trimmable = %t>",
|
||||||
c.isCall,
|
c.cleanEnv,
|
||||||
loc,
|
loc,
|
||||||
c.trimmable,
|
c.trimmable,
|
||||||
)
|
)
|
||||||
@ -121,7 +120,7 @@ func (s *callStack) top() *callFrame {
|
|||||||
// of the frame we want to pop.
|
// of the frame we want to pop.
|
||||||
func (s *callStack) popIfExists(whichFrame int) {
|
func (s *callStack) popIfExists(whichFrame int) {
|
||||||
if len(s.stack) == whichFrame {
|
if len(s.stack) == whichFrame {
|
||||||
if s.top().isCall {
|
if s.top().cleanEnv {
|
||||||
s.calls--
|
s.calls--
|
||||||
}
|
}
|
||||||
s.setCurrentTrace(s.stack[len(s.stack)-1].trace)
|
s.setCurrentTrace(s.stack[len(s.stack)-1].trace)
|
||||||
@ -132,7 +131,7 @@ func (s *callStack) popIfExists(whichFrame int) {
|
|||||||
/** If there is a trimmable frame followed by some locals, pop them all. */
|
/** If there is a trimmable frame followed by some locals, pop them all. */
|
||||||
func (s *callStack) tailCallTrimStack() {
|
func (s *callStack) tailCallTrimStack() {
|
||||||
for i := len(s.stack) - 1; i >= 0; i-- {
|
for i := len(s.stack) - 1; i >= 0; i-- {
|
||||||
if s.stack[i].isCall {
|
if s.stack[i].cleanEnv {
|
||||||
if !s.stack[i].trimmable {
|
if !s.stack[i].trimmable {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -167,7 +166,7 @@ func (s *callStack) newCall(env environment, trimmable bool) {
|
|||||||
panic("Saving empty traceElement on stack")
|
panic("Saving empty traceElement on stack")
|
||||||
}
|
}
|
||||||
s.stack = append(s.stack, &callFrame{
|
s.stack = append(s.stack, &callFrame{
|
||||||
isCall: true,
|
cleanEnv: true,
|
||||||
trace: s.currentTrace,
|
trace: s.currentTrace,
|
||||||
env: env,
|
env: env,
|
||||||
trimmable: trimmable,
|
trimmable: trimmable,
|
||||||
@ -187,7 +186,7 @@ func (s *callStack) newLocal(vars bindingFrame) {
|
|||||||
// getSelfBinding resolves the self construct
|
// getSelfBinding resolves the self construct
|
||||||
func (s *callStack) getSelfBinding() selfBinding {
|
func (s *callStack) getSelfBinding() selfBinding {
|
||||||
for i := len(s.stack) - 1; i >= 0; i-- {
|
for i := len(s.stack) - 1; i >= 0; i-- {
|
||||||
if s.stack[i].isCall {
|
if s.stack[i].cleanEnv {
|
||||||
return s.stack[i].env.selfBinding
|
return s.stack[i].env.selfBinding
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -201,7 +200,7 @@ func (s *callStack) lookUpVar(id ast.Identifier) *cachedThunk {
|
|||||||
if present {
|
if present {
|
||||||
return bind
|
return bind
|
||||||
}
|
}
|
||||||
if s.stack[i].isCall {
|
if s.stack[i].cleanEnv {
|
||||||
// Nothing beyond the captured environment of the thunk / closure.
|
// Nothing beyond the captured environment of the thunk / closure.
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -654,6 +653,15 @@ func (i *interpreter) manifestJSON(v value) (interface{}, error) {
|
|||||||
if i.stack.currentTrace == (traceElement{}) {
|
if i.stack.currentTrace == (traceElement{}) {
|
||||||
panic("manifesting JSON with empty traceElement")
|
panic("manifesting JSON with empty traceElement")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fresh frame for better stack traces
|
||||||
|
err := i.newCall(environment{}, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
stackSize := len(i.stack.stack)
|
||||||
|
defer i.stack.popIfExists(stackSize)
|
||||||
|
|
||||||
switch v := v.(type) {
|
switch v := v.(type) {
|
||||||
|
|
||||||
case *valueBoolean:
|
case *valueBoolean:
|
||||||
@ -673,16 +681,23 @@ func (i *interpreter) manifestJSON(v value) (interface{}, error) {
|
|||||||
|
|
||||||
case *valueArray:
|
case *valueArray:
|
||||||
result := make([]interface{}, 0, len(v.elements))
|
result := make([]interface{}, 0, len(v.elements))
|
||||||
for _, th := range v.elements {
|
for index, th := range v.elements {
|
||||||
|
msg := ast.MakeLocationRangeMessage(fmt.Sprintf("Array element %d", index))
|
||||||
|
i.stack.setCurrentTrace(traceElement{
|
||||||
|
loc: &msg,
|
||||||
|
})
|
||||||
elVal, err := i.evaluatePV(th)
|
elVal, err := i.evaluatePV(th)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
i.stack.clearCurrentTrace()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
elem, err := i.manifestJSON(elVal)
|
elem, err := i.manifestJSON(elVal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
i.stack.clearCurrentTrace()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
result = append(result, elem)
|
result = append(result, elem)
|
||||||
|
i.stack.clearCurrentTrace()
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
|
|
||||||
@ -690,24 +705,37 @@ func (i *interpreter) manifestJSON(v value) (interface{}, error) {
|
|||||||
fieldNames := objectFields(v, withoutHidden)
|
fieldNames := objectFields(v, withoutHidden)
|
||||||
sort.Strings(fieldNames)
|
sort.Strings(fieldNames)
|
||||||
|
|
||||||
|
msg := ast.MakeLocationRangeMessage("Checking object assertions")
|
||||||
|
i.stack.setCurrentTrace(traceElement{
|
||||||
|
loc: &msg,
|
||||||
|
})
|
||||||
err := checkAssertions(i, v)
|
err := checkAssertions(i, v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
i.stack.clearCurrentTrace()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
i.stack.clearCurrentTrace()
|
||||||
|
|
||||||
result := make(map[string]interface{})
|
result := make(map[string]interface{})
|
||||||
|
|
||||||
for _, fieldName := range fieldNames {
|
for _, fieldName := range fieldNames {
|
||||||
|
msg := ast.MakeLocationRangeMessage(fmt.Sprintf("Field %#v", fieldName))
|
||||||
|
i.stack.setCurrentTrace(traceElement{
|
||||||
|
loc: &msg,
|
||||||
|
})
|
||||||
fieldVal, err := v.index(i, fieldName)
|
fieldVal, err := v.index(i, fieldName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
i.stack.clearCurrentTrace()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
field, err := i.manifestJSON(fieldVal)
|
field, err := i.manifestJSON(fieldVal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
i.stack.clearCurrentTrace()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
result[fieldName] = field
|
result[fieldName] = field
|
||||||
|
i.stack.clearCurrentTrace()
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@ -944,10 +972,13 @@ func (i *interpreter) EvalInCleanEnv(env *environment, ast ast.Node, trimmable b
|
|||||||
stackSize := len(i.stack.stack)
|
stackSize := len(i.stack.stack)
|
||||||
|
|
||||||
val, err := i.evaluate(ast, tailCall)
|
val, err := i.evaluate(ast, tailCall)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
i.stack.popIfExists(stackSize)
|
i.stack.popIfExists(stackSize)
|
||||||
|
|
||||||
return val, err
|
return val, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *interpreter) evaluatePV(ph potentialValue) (value, error) {
|
func (i *interpreter) evaluatePV(ph potentialValue) (value, error) {
|
||||||
@ -1211,7 +1242,14 @@ func makeInitialEnv(filename string, baseStd *valueObject) environment {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func evaluateAux(i *interpreter, node ast.Node, tla vmExtMap) (value, traceElement, error) {
|
func manifestationTrace() traceElement {
|
||||||
|
manifestationLoc := ast.MakeLocationRangeMessage("During manifestation")
|
||||||
|
return traceElement{
|
||||||
|
loc: &manifestationLoc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateAux(i *interpreter, node ast.Node, tla vmExtMap) (value, error) {
|
||||||
evalLoc := ast.MakeLocationRangeMessage("During evaluation")
|
evalLoc := ast.MakeLocationRangeMessage("During evaluation")
|
||||||
evalTrace := traceElement{
|
evalTrace := traceElement{
|
||||||
loc: &evalLoc,
|
loc: &evalLoc,
|
||||||
@ -1221,7 +1259,7 @@ func evaluateAux(i *interpreter, node ast.Node, tla vmExtMap) (value, traceEleme
|
|||||||
result, err := i.EvalInCleanEnv(&env, node, false)
|
result, err := i.EvalInCleanEnv(&env, node, false)
|
||||||
i.stack.clearCurrentTrace()
|
i.stack.clearCurrentTrace()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, traceElement{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// If it's not a function, ignore TLA
|
// If it's not a function, ignore TLA
|
||||||
if f, ok := result.(*valueFunction); ok {
|
if f, ok := result.(*valueFunction); ok {
|
||||||
@ -1238,14 +1276,10 @@ func evaluateAux(i *interpreter, node ast.Node, tla vmExtMap) (value, traceEleme
|
|||||||
result, err = f.call(i, args)
|
result, err = f.call(i, args)
|
||||||
i.stack.clearCurrentTrace()
|
i.stack.clearCurrentTrace()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, traceElement{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
manifestationLoc := ast.MakeLocationRangeMessage("During manifestation")
|
return result, nil
|
||||||
manifestationTrace := traceElement{
|
|
||||||
loc: &manifestationLoc,
|
|
||||||
}
|
|
||||||
return result, manifestationTrace, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(sbarzowski) this function takes far too many arguments - build interpreter in vm instead
|
// TODO(sbarzowski) this function takes far too many arguments - build interpreter in vm instead
|
||||||
@ -1257,13 +1291,13 @@ func evaluate(node ast.Node, ext vmExtMap, tla vmExtMap, nativeFuncs map[string]
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
result, manifestationTrace, err := evaluateAux(i, node, tla)
|
result, err := evaluateAux(i, node, tla)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
i.stack.setCurrentTrace(manifestationTrace)
|
i.stack.setCurrentTrace(manifestationTrace())
|
||||||
if stringOutputMode {
|
if stringOutputMode {
|
||||||
err = i.manifestString(&buf, result)
|
err = i.manifestString(&buf, result)
|
||||||
} else {
|
} else {
|
||||||
@ -1286,12 +1320,12 @@ func evaluateMulti(node ast.Node, ext vmExtMap, tla vmExtMap, nativeFuncs map[st
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
result, manifestationTrace, err := evaluateAux(i, node, tla)
|
result, err := evaluateAux(i, node, tla)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
i.stack.setCurrentTrace(manifestationTrace)
|
i.stack.setCurrentTrace(manifestationTrace())
|
||||||
manifested, err := i.manifestAndSerializeMulti(result, stringOutputMode)
|
manifested, err := i.manifestAndSerializeMulti(result, stringOutputMode)
|
||||||
i.stack.clearCurrentTrace()
|
i.stack.clearCurrentTrace()
|
||||||
return manifested, err
|
return manifested, err
|
||||||
@ -1306,12 +1340,12 @@ func evaluateStream(node ast.Node, ext vmExtMap, tla vmExtMap, nativeFuncs map[s
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
result, manifestationTrace, err := evaluateAux(i, node, tla)
|
result, err := evaluateAux(i, node, tla)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
i.stack.setCurrentTrace(manifestationTrace)
|
i.stack.setCurrentTrace(manifestationTrace())
|
||||||
manifested, err := i.manifestAndSerializeYAMLStream(result)
|
manifested, err := i.manifestAndSerializeYAMLStream(result)
|
||||||
i.stack.clearCurrentTrace()
|
i.stack.clearCurrentTrace()
|
||||||
return manifested, err
|
return manifested, err
|
||||||
|
@ -124,7 +124,7 @@ func prepareStdlib(g *typeGraph) {
|
|||||||
"setInter": g.newFuncType(anyArrayType, []ast.Parameter{required("a"), required("b"), optional("keyF")}),
|
"setInter": g.newFuncType(anyArrayType, []ast.Parameter{required("a"), required("b"), optional("keyF")}),
|
||||||
"setUnion": g.newFuncType(anyArrayType, []ast.Parameter{required("a"), required("b"), optional("keyF")}),
|
"setUnion": g.newFuncType(anyArrayType, []ast.Parameter{required("a"), required("b"), optional("keyF")}),
|
||||||
"setDiff": g.newFuncType(anyArrayType, []ast.Parameter{required("a"), required("b"), optional("keyF")}),
|
"setDiff": g.newFuncType(anyArrayType, []ast.Parameter{required("a"), required("b"), optional("keyF")}),
|
||||||
"setMember": g.newFuncType(anyArrayType, []ast.Parameter{required("x"), required("arr"), optional("keyF")}),
|
"setMember": g.newFuncType(boolType, []ast.Parameter{required("x"), required("arr"), optional("keyF")}),
|
||||||
|
|
||||||
// Encoding
|
// Encoding
|
||||||
|
|
||||||
|
1
linter/testdata/stdlib_return_types.jsonnet
vendored
Normal file
1
linter/testdata/stdlib_return_types.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
!std.setMember([1, 2, 3], 1)
|
0
linter/testdata/stdlib_return_types.linter.golden
vendored
Normal file
0
linter/testdata/stdlib_return_types.linter.golden
vendored
Normal file
3
testdata/error_in_object_local.golden
vendored
3
testdata/error_in_object_local.golden
vendored
@ -9,6 +9,9 @@ RUNTIME ERROR: xxx
|
|||||||
|
|
||||||
{ local foo(bar) = error bar, baz: foo("xxx") }
|
{ local foo(bar) = error bar, baz: foo("xxx") }
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
Field "baz"
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
During manifestation
|
During manifestation
|
||||||
|
|
||||||
|
3
testdata/function_manifested.golden
vendored
3
testdata/function_manifested.golden
vendored
@ -1,4 +1,7 @@
|
|||||||
RUNTIME ERROR: couldn't manifest function as JSON
|
RUNTIME ERROR: couldn't manifest function as JSON
|
||||||
|
-------------------------------------------------
|
||||||
|
Field "f"
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
During manifestation
|
During manifestation
|
||||||
|
|
||||||
|
3
testdata/missing_super.golden
vendored
3
testdata/missing_super.golden
vendored
@ -4,6 +4,9 @@ RUNTIME ERROR: Attempt to use super when there is no super class.
|
|||||||
|
|
||||||
{ x: super.x }
|
{ x: super.x }
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
Field "x"
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
During manifestation
|
During manifestation
|
||||||
|
|
||||||
|
3
testdata/object_comp_err_elem.golden
vendored
3
testdata/object_comp_err_elem.golden
vendored
@ -4,6 +4,9 @@ RUNTIME ERROR: xxx
|
|||||||
|
|
||||||
{ ["x"]: error "xxx" for x in [1] }
|
{ ["x"]: error "xxx" for x in [1] }
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
Field "x"
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
During manifestation
|
During manifestation
|
||||||
|
|
||||||
|
5
testdata/object_invariant10.golden
vendored
5
testdata/object_invariant10.golden
vendored
@ -1,6 +1,11 @@
|
|||||||
RUNTIME ERROR: Object assertion failed.
|
RUNTIME ERROR: Object assertion failed.
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
testdata/object_invariant10:1:16-28
|
||||||
|
|
||||||
|
{ assert true, assert false }
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
Checking object assertions
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
During manifestation
|
During manifestation
|
||||||
|
2
testdata/object_invariant11.golden
vendored
2
testdata/object_invariant11.golden
vendored
@ -1,6 +1,8 @@
|
|||||||
RUNTIME ERROR: Object assertion failed.
|
RUNTIME ERROR: Object assertion failed.
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
testdata/object_invariant11:1:3-15
|
||||||
|
|
||||||
|
{ assert false }.x
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
testdata/object_invariant11:1:1-19 $
|
testdata/object_invariant11:1:1-19 $
|
||||||
|
3
testdata/object_invariant13.golden
vendored
3
testdata/object_invariant13.golden
vendored
@ -4,6 +4,9 @@ RUNTIME ERROR: x
|
|||||||
|
|
||||||
{ assert error "x" }
|
{ assert error "x" }
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
Checking object assertions
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
During manifestation
|
During manifestation
|
||||||
|
|
||||||
|
5
testdata/object_invariant14.golden
vendored
5
testdata/object_invariant14.golden
vendored
@ -1,6 +1,11 @@
|
|||||||
RUNTIME ERROR: xxx
|
RUNTIME ERROR: xxx
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
testdata/object_invariant14:1:3-22
|
||||||
|
|
||||||
|
{ assert false: "xxx" }
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
Checking object assertions
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
During manifestation
|
During manifestation
|
||||||
|
5
testdata/object_invariant2.golden
vendored
5
testdata/object_invariant2.golden
vendored
@ -1,6 +1,11 @@
|
|||||||
RUNTIME ERROR: Object assertion failed.
|
RUNTIME ERROR: Object assertion failed.
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
testdata/object_invariant2:1:3-15
|
||||||
|
|
||||||
|
{ assert false }
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
Checking object assertions
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
During manifestation
|
During manifestation
|
||||||
|
3
testdata/object_invariant7.golden
vendored
3
testdata/object_invariant7.golden
vendored
@ -4,6 +4,9 @@ RUNTIME ERROR: Attempt to use super when there is no super class.
|
|||||||
|
|
||||||
{ x: 5, assert super.x == 5 }
|
{ x: 5, assert super.x == 5 }
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
Checking object assertions
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
During manifestation
|
During manifestation
|
||||||
|
|
||||||
|
5
testdata/object_invariant8.golden
vendored
5
testdata/object_invariant8.golden
vendored
@ -1,6 +1,11 @@
|
|||||||
RUNTIME ERROR: Object assertion failed.
|
RUNTIME ERROR: Object assertion failed.
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
testdata/object_invariant8:1:9-27
|
||||||
|
|
||||||
|
{ x: 5, assert self.x == 4 }
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
Checking object assertions
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
During manifestation
|
During manifestation
|
||||||
|
5
testdata/object_invariant9.golden
vendored
5
testdata/object_invariant9.golden
vendored
@ -1,6 +1,11 @@
|
|||||||
RUNTIME ERROR: Object assertion failed.
|
RUNTIME ERROR: Object assertion failed.
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
testdata/object_invariant9:1:16-28
|
||||||
|
|
||||||
|
{ assert true, assert false }
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
Checking object assertions
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
During manifestation
|
During manifestation
|
||||||
|
5
testdata/object_invariant_plus.golden
vendored
5
testdata/object_invariant_plus.golden
vendored
@ -1,6 +1,11 @@
|
|||||||
RUNTIME ERROR: Object assertion failed.
|
RUNTIME ERROR: Object assertion failed.
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
testdata/object_invariant_plus:1:2-14
|
||||||
|
|
||||||
|
{assert false} + {assert true}
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
Checking object assertions
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
During manifestation
|
During manifestation
|
||||||
|
5
testdata/object_invariant_plus2.golden
vendored
5
testdata/object_invariant_plus2.golden
vendored
@ -1,6 +1,11 @@
|
|||||||
RUNTIME ERROR: Object assertion failed.
|
RUNTIME ERROR: Object assertion failed.
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
testdata/object_invariant_plus2:1:18-30
|
||||||
|
|
||||||
|
{assert true} + {assert false}
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
Checking object assertions
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
During manifestation
|
During manifestation
|
||||||
|
5
testdata/object_invariant_plus6.golden
vendored
5
testdata/object_invariant_plus6.golden
vendored
@ -1,6 +1,11 @@
|
|||||||
RUNTIME ERROR: yyy
|
RUNTIME ERROR: yyy
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
testdata/object_invariant_plus6:1:27-46
|
||||||
|
|
||||||
|
{ assert false: "xxx" } { assert false: "yyy" }
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
Checking object assertions
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
During manifestation
|
During manifestation
|
||||||
|
13
testdata/stacktrace_assert.golden
vendored
Normal file
13
testdata/stacktrace_assert.golden
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
RUNTIME ERROR: Object assertion failed.
|
||||||
|
-------------------------------------------------
|
||||||
|
testdata/stacktrace_assert:1:3-15
|
||||||
|
|
||||||
|
{ assert false }
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
Checking object assertions
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
During manifestation
|
||||||
|
|
||||||
|
|
1
testdata/stacktrace_assert.jsonnet
vendored
Normal file
1
testdata/stacktrace_assert.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{ assert false }
|
0
testdata/stacktrace_assert.linter.golden
vendored
Normal file
0
testdata/stacktrace_assert.linter.golden
vendored
Normal file
13
testdata/stacktrace_plussuper.golden
vendored
Normal file
13
testdata/stacktrace_plussuper.golden
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
RUNTIME ERROR: Unexpected type null
|
||||||
|
-------------------------------------------------
|
||||||
|
testdata/stacktrace_plussuper:2:7-9 +:
|
||||||
|
|
||||||
|
a+: {},
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
Field "a"
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
During manifestation
|
||||||
|
|
||||||
|
|
5
testdata/stacktrace_plussuper.jsonnet
vendored
Normal file
5
testdata/stacktrace_plussuper.jsonnet
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
local a(input) = input + {
|
||||||
|
a+: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
a({a: null})
|
0
testdata/stacktrace_plussuper.linter.golden
vendored
Normal file
0
testdata/stacktrace_plussuper.linter.golden
vendored
Normal file
5
testdata/supersugar8.golden
vendored
5
testdata/supersugar8.golden
vendored
@ -1,6 +1,11 @@
|
|||||||
RUNTIME ERROR: Object assertion failed.
|
RUNTIME ERROR: Object assertion failed.
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
testdata/supersugar8:1:3-16
|
||||||
|
|
||||||
|
{ assert self.x } { x +: false }
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
Checking object assertions
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
During manifestation
|
During manifestation
|
||||||
|
43
thunks.go
43
thunks.go
@ -36,6 +36,10 @@ func (rv *readyValue) evaluate(i *interpreter, sb selfBinding, origBinding bindi
|
|||||||
return rv.content, nil
|
return rv.content, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rv *readyValue) loc() *ast.LocationRange {
|
||||||
|
return &ast.LocationRange{}
|
||||||
|
}
|
||||||
|
|
||||||
// potentialValues
|
// potentialValues
|
||||||
// -------------------------------------
|
// -------------------------------------
|
||||||
|
|
||||||
@ -89,7 +93,12 @@ type codeUnboundField struct {
|
|||||||
|
|
||||||
func (f *codeUnboundField) evaluate(i *interpreter, sb selfBinding, origBindings bindingFrame, fieldName string) (value, error) {
|
func (f *codeUnboundField) evaluate(i *interpreter, sb selfBinding, origBindings bindingFrame, fieldName string) (value, error) {
|
||||||
env := makeEnvironment(origBindings, sb)
|
env := makeEnvironment(origBindings, sb)
|
||||||
return i.EvalInCleanEnv(&env, f.body, false)
|
val, err := i.EvalInCleanEnv(&env, f.body, false)
|
||||||
|
return val, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *codeUnboundField) loc() *ast.LocationRange {
|
||||||
|
return f.body.Loc()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provide additional bindings for a field. It shadows bindings from the object.
|
// Provide additional bindings for a field. It shadows bindings from the object.
|
||||||
@ -110,24 +119,54 @@ func (f *bindingsUnboundField) evaluate(i *interpreter, sb selfBinding, origBind
|
|||||||
return f.inner.evaluate(i, sb, upValues, fieldName)
|
return f.inner.evaluate(i, sb, upValues, fieldName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *bindingsUnboundField) loc() *ast.LocationRange {
|
||||||
|
return f.inner.loc()
|
||||||
|
}
|
||||||
|
|
||||||
// plusSuperUnboundField represents a `field+: ...` that hasn't been bound to an object.
|
// plusSuperUnboundField represents a `field+: ...` that hasn't been bound to an object.
|
||||||
type plusSuperUnboundField struct {
|
type plusSuperUnboundField struct {
|
||||||
inner unboundField
|
inner unboundField
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *plusSuperUnboundField) evaluate(i *interpreter, sb selfBinding, origBinding bindingFrame, fieldName string) (value, error) {
|
func (f *plusSuperUnboundField) evaluate(i *interpreter, sb selfBinding, origBinding bindingFrame, fieldName string) (value, error) {
|
||||||
|
err := i.newCall(environment{}, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
stackSize := len(i.stack.stack)
|
||||||
|
defer i.stack.popIfExists(stackSize)
|
||||||
|
|
||||||
|
context := "+:"
|
||||||
|
i.stack.setCurrentTrace(traceElement{
|
||||||
|
loc: f.loc(),
|
||||||
|
context: &context,
|
||||||
|
})
|
||||||
|
defer i.stack.clearCurrentTrace()
|
||||||
|
|
||||||
right, err := f.inner.evaluate(i, sb, origBinding, fieldName)
|
right, err := f.inner.evaluate(i, sb, origBinding, fieldName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !objectHasField(sb.super(), fieldName, withHidden) {
|
if !objectHasField(sb.super(), fieldName, withHidden) {
|
||||||
return right, nil
|
return right, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
left, err := objectIndex(i, sb.super(), fieldName)
|
left, err := objectIndex(i, sb.super(), fieldName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return builtinPlus(i, left, right)
|
|
||||||
|
value, err := builtinPlus(i, left, right)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *plusSuperUnboundField) loc() *ast.LocationRange {
|
||||||
|
return f.inner.loc()
|
||||||
}
|
}
|
||||||
|
|
||||||
// evalCallables
|
// evalCallables
|
||||||
|
2
value.go
2
value.go
@ -572,6 +572,7 @@ func checkAssertionsHelper(i *interpreter, obj *valueObject, curr uncachedObject
|
|||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("Unknown object type %#v", curr))
|
panic(fmt.Sprintf("Unknown object type %#v", curr))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkAssertions(i *interpreter, obj *valueObject) error {
|
func checkAssertions(i *interpreter, obj *valueObject) error {
|
||||||
@ -611,6 +612,7 @@ type simpleObjectField struct {
|
|||||||
// unboundField is a field that doesn't know yet in which object it is.
|
// unboundField is a field that doesn't know yet in which object it is.
|
||||||
type unboundField interface {
|
type unboundField interface {
|
||||||
evaluate(i *interpreter, sb selfBinding, origBinding bindingFrame, fieldName string) (value, error)
|
evaluate(i *interpreter, sb selfBinding, origBinding bindingFrame, fieldName string) (value, error)
|
||||||
|
loc() *ast.LocationRange
|
||||||
}
|
}
|
||||||
|
|
||||||
// extendedObject represents an object created through inheritance (left + right).
|
// extendedObject represents an object created through inheritance (left + right).
|
||||||
|
Loading…
x
Reference in New Issue
Block a user