aboutsummaryrefslogblamecommitdiffstats
path: root/vendor/github.com/robertkrimen/otto/error.go
blob: 41a5c10e7c126abc2a42e03c04ca1b03c4bd6157 (plain) (tree)
1
2
3
4
5




                



























                                                                                   

















                                                           
                    





                             








                                    
                          
 








                                                                                
 






                                                                         







                                                            









                                                                                
                           









                                                                     
                                    



















                                                                     
                                                                                          






                             
                                         
                                 






                                                       
                                    
 





                                                              
                                        




                                                                        


                                      

                                                    

                                                                                    

                                             
 


                                                                                  







                                                                
 




                                                                             
                                                                     




                                                                                  
                                                                          




                                                                            
                                                                    




                                                                               
                                                                       




                                                                              
                                                                      









                                                                      


                                            



















                                                                      
package otto

import (
    "errors"
    "fmt"

    "github.com/robertkrimen/otto/file"
)

type _exception struct {
    value interface{}
}

func newException(value interface{}) *_exception {
    return &_exception{
        value: value,
    }
}

func (self *_exception) eject() interface{} {
    value := self.value
    self.value = nil // Prevent Go from holding on to the value, whatever it is
    return value
}

type _error struct {
    name    string
    message string
    trace   []_frame

    offset int
}

func (err _error) format() string {
    if len(err.name) == 0 {
        return err.message
    }
    if len(err.message) == 0 {
        return err.name
    }
    return fmt.Sprintf("%s: %s", err.name, err.message)
}

func (err _error) formatWithStack() string {
    str := err.format() + "\n"
    for _, frame := range err.trace {
        str += "    at " + frame.location() + "\n"
    }
    return str
}

type _frame struct {
    native     bool
    nativeFile string
    nativeLine int
    file       *file.File
    offset     int
    callee     string
}

var (
    nativeFrame = _frame{}
)

type _at int

func (fr _frame) location() string {
    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 {
    _error
}

// Error returns a description of the error
//
//    TypeError: 'def' is not a function
//
func (err Error) Error() string {
    return err.format()
}

// 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/
//
func (err Error) String() string {
    return err.formatWithStack()
}

func (err _error) describe(format string, in ...interface{}) string {
    return fmt.Sprintf(format, in...)
}

func (self _error) messageValue() Value {
    if self.message == "" {
        return Value{}
    }
    return toValue_string(self.message)
}

func (rt *_runtime) typeErrorResult(throw bool) bool {
    if throw {
        panic(rt.panicTypeError())
    }
    return false
}

func newError(rt *_runtime, name string, stackFramesToPop int, in ...interface{}) _error {
    err := _error{
        name:   name,
        offset: -1,
    }
    description := ""
    length := len(in)

    if rt != nil && rt.scope != nil {
        scope := rt.scope

        for i := 0; i < stackFramesToPop; i++ {
            if scope.outer != nil {
                scope = scope.outer
            }
        }

        frame := scope.frame

        if length > 0 {
            if at, ok := in[length-1].(_at); ok {
                in = in[0 : length-1]
                if scope != nil {
                    frame.offset = int(at)
                }
                length--
            }
            if length > 0 {
                description, in = in[0].(string), in[1:]
            }
        }

        limit := rt.traceLimit

        err.trace = append(err.trace, frame)
        if scope != nil {
            for scope = scope.outer; scope != nil; scope = scope.outer {
                if limit--; limit == 0 {
                    break
                }

                if scope.frame.offset >= 0 {
                    err.trace = append(err.trace, scope.frame)
                }
            }
        }
    } else {
        if length > 0 {
            description, in = in[0].(string), in[1:]
        }
    }
    err.message = err.describe(description, in...)

    return err
}

func (rt *_runtime) panicTypeError(argumentList ...interface{}) *_exception {
    return &_exception{
        value: newError(rt, "TypeError", 0, argumentList...),
    }
}

func (rt *_runtime) panicReferenceError(argumentList ...interface{}) *_exception {
    return &_exception{
        value: newError(rt, "ReferenceError", 0, argumentList...),
    }
}

func (rt *_runtime) panicURIError(argumentList ...interface{}) *_exception {
    return &_exception{
        value: newError(rt, "URIError", 0, argumentList...),
    }
}

func (rt *_runtime) panicSyntaxError(argumentList ...interface{}) *_exception {
    return &_exception{
        value: newError(rt, "SyntaxError", 0, argumentList...),
    }
}

func (rt *_runtime) panicRangeError(argumentList ...interface{}) *_exception {
    return &_exception{
        value: newError(rt, "RangeError", 0, argumentList...),
    }
}

func catchPanic(function func()) (err error) {
    defer func() {
        if caught := recover(); caught != nil {
            if exception, ok := caught.(*_exception); ok {
                caught = exception.eject()
            }
            switch caught := caught.(type) {
            case *Error:
                err = caught
                return
            case _error:
                err = &Error{caught}
                return
            case Value:
                if vl := caught._object(); vl != nil {
                    switch vl := vl.value.(type) {
                    case _error:
                        err = &Error{vl}
                        return
                    }
                }
                err = errors.New(caught.string())
                return
            }
            panic(caught)
        }
    }()
    function()
    return nil
}