2024-02-24 21:00:04 +00:00
|
|
|
package otto
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/robertkrimen/otto/file"
|
|
|
|
)
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
type exception struct {
|
2024-02-24 21:00:04 +00:00
|
|
|
value interface{}
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func newException(value interface{}) *exception {
|
|
|
|
return &exception{
|
2024-02-24 21:00:04 +00:00
|
|
|
value: value,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (e *exception) eject() interface{} {
|
|
|
|
value := e.value
|
|
|
|
e.value = nil // Prevent Go from holding on to the value, whatever it is
|
2024-02-24 21:00:04 +00:00
|
|
|
return value
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
type ottoError struct {
|
2024-02-24 21:00:04 +00:00
|
|
|
name string
|
|
|
|
message string
|
2024-04-04 14:46:14 +00:00
|
|
|
trace []frame
|
2024-02-24 21:00:04 +00:00
|
|
|
|
|
|
|
offset int
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (e ottoError) format() string {
|
|
|
|
if len(e.name) == 0 {
|
|
|
|
return e.message
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
2024-04-04 14:46:14 +00:00
|
|
|
if len(e.message) == 0 {
|
|
|
|
return e.name
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
2024-04-04 14:46:14 +00:00
|
|
|
return fmt.Sprintf("%s: %s", e.name, e.message)
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (e ottoError) formatWithStack() string {
|
|
|
|
str := e.format() + "\n"
|
|
|
|
for _, frm := range e.trace {
|
|
|
|
str += " at " + frm.location() + "\n"
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
return str
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
type frame struct {
|
2024-02-24 21:00:04 +00:00
|
|
|
native bool
|
|
|
|
nativeFile string
|
|
|
|
nativeLine int
|
|
|
|
file *file.File
|
|
|
|
offset int
|
|
|
|
callee string
|
|
|
|
fn interface{}
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
var nativeFrame = frame{}
|
2024-02-24 21:00:04 +00:00
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
type at int
|
2024-02-24 21:00:04 +00:00
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (fr frame) location() string {
|
2024-02-24 21:00:04 +00:00
|
|
|
str := "<unknown>"
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case fr.native:
|
|
|
|
str = "<native code>"
|
|
|
|
if fr.nativeFile != "" && fr.nativeLine != 0 {
|
|
|
|
str = fmt.Sprintf("%s:%d", fr.nativeFile, fr.nativeLine)
|
|
|
|
}
|
|
|
|
case fr.file != nil:
|
|
|
|
if p := fr.file.Position(file.Idx(fr.offset)); p != nil {
|
|
|
|
path, line, column := p.Filename, p.Line, p.Column
|
|
|
|
|
|
|
|
if path == "" {
|
|
|
|
path = "<anonymous>"
|
|
|
|
}
|
|
|
|
|
|
|
|
str = fmt.Sprintf("%s:%d:%d", path, line, column)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if fr.callee != "" {
|
|
|
|
str = fmt.Sprintf("%s (%s)", fr.callee, str)
|
|
|
|
}
|
|
|
|
|
|
|
|
return str
|
|
|
|
}
|
|
|
|
|
|
|
|
// An Error represents a runtime error, e.g. a TypeError, a ReferenceError, etc.
|
|
|
|
type Error struct {
|
2024-04-04 14:46:14 +00:00
|
|
|
ottoError
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Error returns a description of the error
|
|
|
|
//
|
|
|
|
// TypeError: 'def' is not a function
|
2024-04-04 14:46:14 +00:00
|
|
|
func (e Error) Error() string {
|
|
|
|
return e.format()
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// String returns a description of the error and a trace of where the
|
|
|
|
// error occurred.
|
|
|
|
//
|
|
|
|
// TypeError: 'def' is not a function
|
|
|
|
// at xyz (<anonymous>:3:9)
|
|
|
|
// at <anonymous>:7:1/
|
2024-04-04 14:46:14 +00:00
|
|
|
func (e Error) String() string {
|
|
|
|
return e.formatWithStack()
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GoString returns a description of the error and a trace of where the
|
|
|
|
// error occurred. Printing with %#v will trigger this behaviour.
|
2024-04-04 14:46:14 +00:00
|
|
|
func (e Error) GoString() string {
|
|
|
|
return e.formatWithStack()
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (e ottoError) describe(format string, in ...interface{}) string {
|
2024-02-24 21:00:04 +00:00
|
|
|
return fmt.Sprintf(format, in...)
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (e ottoError) messageValue() Value {
|
|
|
|
if e.message == "" {
|
2024-02-24 21:00:04 +00:00
|
|
|
return Value{}
|
|
|
|
}
|
2024-04-04 14:46:14 +00:00
|
|
|
return stringValue(e.message)
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (rt *runtime) typeErrorResult(throw bool) bool {
|
2024-02-24 21:00:04 +00:00
|
|
|
if throw {
|
|
|
|
panic(rt.panicTypeError())
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func newError(rt *runtime, name string, stackFramesToPop int, in ...interface{}) ottoError {
|
|
|
|
err := ottoError{
|
2024-02-24 21:00:04 +00:00
|
|
|
name: name,
|
|
|
|
offset: -1,
|
|
|
|
}
|
|
|
|
description := ""
|
|
|
|
length := len(in)
|
|
|
|
|
|
|
|
if rt != nil && rt.scope != nil {
|
2024-04-04 14:46:14 +00:00
|
|
|
curScope := rt.scope
|
2024-02-24 21:00:04 +00:00
|
|
|
|
|
|
|
for i := 0; i < stackFramesToPop; i++ {
|
2024-04-04 14:46:14 +00:00
|
|
|
if curScope.outer != nil {
|
|
|
|
curScope = curScope.outer
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
frm := curScope.frame
|
2024-02-24 21:00:04 +00:00
|
|
|
|
|
|
|
if length > 0 {
|
2024-04-04 14:46:14 +00:00
|
|
|
if atv, ok := in[length-1].(at); ok {
|
2024-02-24 21:00:04 +00:00
|
|
|
in = in[0 : length-1]
|
2024-04-04 14:46:14 +00:00
|
|
|
if curScope != nil {
|
|
|
|
frm.offset = int(atv)
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
length--
|
|
|
|
}
|
|
|
|
if length > 0 {
|
|
|
|
description, in = in[0].(string), in[1:]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
limit := rt.traceLimit
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
err.trace = append(err.trace, frm)
|
|
|
|
if curScope != nil {
|
|
|
|
for curScope = curScope.outer; curScope != nil; curScope = curScope.outer {
|
2024-02-24 21:00:04 +00:00
|
|
|
if limit--; limit == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
if curScope.frame.offset >= 0 {
|
|
|
|
err.trace = append(err.trace, curScope.frame)
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-04-04 14:46:14 +00:00
|
|
|
} else if length > 0 {
|
|
|
|
description, in = in[0].(string), in[1:]
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
err.message = err.describe(description, in...)
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (rt *runtime) panicTypeError(argumentList ...interface{}) *exception {
|
|
|
|
return &exception{
|
2024-02-24 21:00:04 +00:00
|
|
|
value: newError(rt, "TypeError", 0, argumentList...),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (rt *runtime) panicReferenceError(argumentList ...interface{}) *exception {
|
|
|
|
return &exception{
|
2024-02-24 21:00:04 +00:00
|
|
|
value: newError(rt, "ReferenceError", 0, argumentList...),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (rt *runtime) panicURIError(argumentList ...interface{}) *exception {
|
|
|
|
return &exception{
|
2024-02-24 21:00:04 +00:00
|
|
|
value: newError(rt, "URIError", 0, argumentList...),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (rt *runtime) panicSyntaxError(argumentList ...interface{}) *exception {
|
|
|
|
return &exception{
|
2024-02-24 21:00:04 +00:00
|
|
|
value: newError(rt, "SyntaxError", 0, argumentList...),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (rt *runtime) panicRangeError(argumentList ...interface{}) *exception {
|
|
|
|
return &exception{
|
2024-02-24 21:00:04 +00:00
|
|
|
value: newError(rt, "RangeError", 0, argumentList...),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func catchPanic(function func()) (err error) {
|
|
|
|
defer func() {
|
|
|
|
if caught := recover(); caught != nil {
|
2024-04-04 14:46:14 +00:00
|
|
|
if excep, ok := caught.(*exception); ok {
|
|
|
|
caught = excep.eject()
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
switch caught := caught.(type) {
|
|
|
|
case *Error:
|
|
|
|
err = caught
|
|
|
|
return
|
2024-04-04 14:46:14 +00:00
|
|
|
case ottoError:
|
2024-02-24 21:00:04 +00:00
|
|
|
err = &Error{caught}
|
|
|
|
return
|
|
|
|
case Value:
|
2024-04-04 14:46:14 +00:00
|
|
|
if vl := caught.object(); vl != nil {
|
|
|
|
if vl, ok := vl.value.(ottoError); ok {
|
2024-02-24 21:00:04 +00:00
|
|
|
err = &Error{vl}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
err = errors.New(caught.string())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
panic(caught)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
function()
|
|
|
|
return nil
|
|
|
|
}
|