mirror of
https://github.com/robertkrimen/otto
synced 2025-10-05 19:19:10 +08:00
Merge pull request #147 from darkliquid/issue-145
Add Context method to aid debugging
This commit is contained in:
commit
f28ccbfeea
69
otto.go
69
otto.go
|
@ -363,6 +363,75 @@ func (self Otto) SetDebuggerHandler(fn func(vm *Otto)) {
|
|||
self.runtime.debugger = fn
|
||||
}
|
||||
|
||||
// Context is a structure that contains information about the current execution
|
||||
// context.
|
||||
type Context struct {
|
||||
Filename string
|
||||
Line int
|
||||
Column int
|
||||
Callee string
|
||||
Symbols map[string]Value
|
||||
This Value
|
||||
Stacktrace []string
|
||||
}
|
||||
|
||||
// Context returns the current execution context of the vm
|
||||
func (self Otto) Context() (ctx Context) {
|
||||
// Ensure we are operating in a scope
|
||||
if self.runtime.scope == nil {
|
||||
self.runtime.enterGlobalScope()
|
||||
defer self.runtime.leaveScope()
|
||||
}
|
||||
|
||||
scope := self.runtime.scope
|
||||
frame := scope.frame
|
||||
|
||||
// Get location information
|
||||
ctx.Filename = "<unknown>"
|
||||
ctx.Callee = frame.callee
|
||||
if frame.file != nil {
|
||||
ctx.Filename = frame.file.Name()
|
||||
if ctx.Filename == "" {
|
||||
ctx.Filename = "<anonymous>"
|
||||
}
|
||||
ctx.Line, ctx.Column = _position(frame.file, frame.offset)
|
||||
}
|
||||
|
||||
// Get the current scope this Value
|
||||
ctx.This = toValue_object(scope.this)
|
||||
|
||||
// Build stacktrace (up to 10 levels deep)
|
||||
limit := 10
|
||||
ctx.Symbols = make(map[string]Value)
|
||||
ctx.Stacktrace = append(ctx.Stacktrace, frame.location())
|
||||
for limit > 0 {
|
||||
// Get variables
|
||||
stash := scope.lexical
|
||||
for {
|
||||
for _, name := range getStashProperties(stash) {
|
||||
if _, ok := ctx.Symbols[name]; !ok {
|
||||
ctx.Symbols[name] = stash.getBinding(name, true)
|
||||
}
|
||||
}
|
||||
stash = stash.outer()
|
||||
if stash == nil || stash.outer() == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
scope = scope.outer
|
||||
if scope == nil {
|
||||
break
|
||||
}
|
||||
if scope.frame.offset >= 0 {
|
||||
ctx.Stacktrace = append(ctx.Stacktrace, scope.frame.location())
|
||||
}
|
||||
limit--
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Call the given JavaScript with a given this and arguments.
|
||||
//
|
||||
// If this is nil, then some special handling takes place to determine the proper
|
||||
|
|
129
otto_test.go
129
otto_test.go
|
@ -360,7 +360,7 @@ func TestTryFinally(t *testing.T) {
|
|||
finally {
|
||||
def = 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
def -= 1;
|
||||
}
|
||||
while (abc < 2)
|
||||
|
@ -1473,6 +1473,133 @@ func TestOttoEval(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestOttoContext(t *testing.T) {
|
||||
// These are all the builtin global scope symbols
|
||||
builtins := []string{
|
||||
"escape",
|
||||
"URIError",
|
||||
"RegExp",
|
||||
"ReferenceError",
|
||||
"parseFloat",
|
||||
"parseInt",
|
||||
"SyntaxError",
|
||||
"decodeURIComponent",
|
||||
"encodeURIComponent",
|
||||
"Infinity",
|
||||
"JSON",
|
||||
"isNaN",
|
||||
"unescape",
|
||||
"decodeURI",
|
||||
"Object",
|
||||
"Function",
|
||||
"RangeError",
|
||||
"Error",
|
||||
"get_context",
|
||||
"eval",
|
||||
"Number",
|
||||
"Math",
|
||||
"NaN",
|
||||
"Date",
|
||||
"Boolean",
|
||||
"console",
|
||||
"encodeURI",
|
||||
"EvalError",
|
||||
"Array",
|
||||
"TypeError",
|
||||
"String",
|
||||
"isFinite",
|
||||
"undefined",
|
||||
}
|
||||
|
||||
tt(t, func() {
|
||||
vm := New()
|
||||
|
||||
vm.Set("get_context", func(c FunctionCall) Value {
|
||||
ctx := c.Otto.Context()
|
||||
is(ctx.Callee, "f1")
|
||||
is(ctx.Filename, "<anonymous>")
|
||||
is(ctx.Line, 8)
|
||||
is(ctx.Column, 5)
|
||||
is(ctx.Stacktrace, []string{
|
||||
"f1 (<anonymous>:8:5)",
|
||||
"f2 (<anonymous>:15:5)",
|
||||
"f3 (<anonymous>:19:5)",
|
||||
"t (<anonymous>:22:4)",
|
||||
})
|
||||
is(len(ctx.Symbols), 9+len(builtins))
|
||||
is(ctx.Symbols["a"], 1)
|
||||
is(ctx.Symbols["b"], "hello")
|
||||
is(ctx.Symbols["c"], true)
|
||||
is(ctx.Symbols["j"], 2)
|
||||
is(ctx.Symbols["f1"].IsFunction(), true)
|
||||
is(ctx.Symbols["f2"].IsFunction(), true)
|
||||
is(ctx.Symbols["f3"].IsFunction(), true)
|
||||
is(ctx.Symbols["t"].IsFunction(), true)
|
||||
callee, _ := ctx.Symbols["arguments"].Object().Get("callee")
|
||||
is(callee.IsDefined(), true)
|
||||
|
||||
return Value{}
|
||||
})
|
||||
|
||||
_, err := vm.Run(`(function t() {
|
||||
var a = 1;
|
||||
var b = 'hello';
|
||||
var c = true;
|
||||
|
||||
function f1() {
|
||||
var j = 2;
|
||||
get_context();
|
||||
(function() {
|
||||
var d = 4;
|
||||
})()
|
||||
}
|
||||
|
||||
function f2() {
|
||||
f1();
|
||||
}
|
||||
|
||||
function f3() {
|
||||
f2();
|
||||
}
|
||||
|
||||
f3();
|
||||
|
||||
a = 2;
|
||||
b = 'goodbye';
|
||||
c = false;
|
||||
}())`)
|
||||
|
||||
is(err, nil)
|
||||
})
|
||||
|
||||
// this test makes sure that `Context` works on global scope by default, if
|
||||
// there is not a current scope.
|
||||
tt(t, func() {
|
||||
vm := New()
|
||||
|
||||
vm.Set("get_context", func(c FunctionCall) Value {
|
||||
ctx := c.Otto.Context()
|
||||
is(ctx.Callee, "")
|
||||
is(ctx.Filename, "<anonymous>")
|
||||
is(ctx.Line, 3)
|
||||
is(ctx.Column, 4)
|
||||
is(ctx.Stacktrace, []string{"<anonymous>:3:4"})
|
||||
is(len(ctx.Symbols), 2+len(builtins))
|
||||
is(ctx.Symbols["a"], 1)
|
||||
is(ctx.Symbols["b"], UndefinedValue())
|
||||
|
||||
return Value{}
|
||||
})
|
||||
|
||||
_, err := vm.Run(`
|
||||
var a = 1;
|
||||
get_context()
|
||||
var b = 2;
|
||||
`)
|
||||
is(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_objectLength(t *testing.T) {
|
||||
tt(t, func() {
|
||||
_, vm := test()
|
||||
|
|
21
stash.go
21
stash.go
|
@ -273,3 +273,24 @@ func (in *_fnStash) clone(clone *_clone) _stash {
|
|||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func getStashProperties(stash _stash) (keys []string) {
|
||||
switch vars := stash.(type) {
|
||||
case *_dclStash:
|
||||
for k := range vars.property {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
case *_fnStash:
|
||||
for k := range vars.property {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
case *_objectStash:
|
||||
for k := range vars.object.property {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
default:
|
||||
panic("unknown stash type")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user