aboutsummaryrefslogblamecommitdiffstats
path: root/vendor/github.com/robertkrimen/otto/type_function.go
blob: 5637dc60512b402efe759b9e54069dcc97e37343 (plain) (tree)
































                                                                             

                        



                                                     
                                                                                                                            

                                                  


                                






















































































                                                                                                             



                                                                                                     
















                                                                     
















                                                                          

                                             
                                             
                 


                                       












































































































                                                                                                           



                                                                                                            
                                                        
 
package otto

// _constructFunction
type _constructFunction func(*_object, []Value) Value

// 13.2.2 [[Construct]]
func defaultConstruct(fn *_object, argumentList []Value) Value {
    object := fn.runtime.newObject()
    object.class = "Object"

    prototype := fn.get("prototype")
    if prototype.kind != valueObject {
        prototype = toValue_object(fn.runtime.global.ObjectPrototype)
    }
    object.prototype = prototype._object()

    this := toValue_object(object)
    value := fn.call(this, argumentList, false, nativeFrame)
    if value.kind == valueObject {
        return value
    }
    return this
}

// _nativeFunction
type _nativeFunction func(FunctionCall) Value

// ===================== //
// _nativeFunctionObject //
// ===================== //

type _nativeFunctionObject struct {
    name      string
    file      string
    line      int
    call      _nativeFunction    // [[Call]]
    construct _constructFunction // [[Construct]]
}

func (runtime *_runtime) newNativeFunctionObject(name, file string, line int, native _nativeFunction, length int) *_object {
    self := runtime.newClassObject("Function")
    self.value = _nativeFunctionObject{
        name:      name,
        file:      file,
        line:      line,
        call:      native,
        construct: defaultConstruct,
    }
    self.defineProperty("length", toValue_int(length), 0000, false)
    return self
}

// =================== //
// _bindFunctionObject //
// =================== //

type _bindFunctionObject struct {
    target       *_object
    this         Value
    argumentList []Value
}

func (runtime *_runtime) newBoundFunctionObject(target *_object, this Value, argumentList []Value) *_object {
    self := runtime.newClassObject("Function")
    self.value = _bindFunctionObject{
        target:       target,
        this:         this,
        argumentList: argumentList,
    }
    length := int(toInt32(target.get("length")))
    length -= len(argumentList)
    if length < 0 {
        length = 0
    }
    self.defineProperty("length", toValue_int(length), 0000, false)
    self.defineProperty("caller", Value{}, 0000, false)    // TODO Should throw a TypeError
    self.defineProperty("arguments", Value{}, 0000, false) // TODO Should throw a TypeError
    return self
}

// [[Construct]]
func (fn _bindFunctionObject) construct(argumentList []Value) Value {
    object := fn.target
    switch value := object.value.(type) {
    case _nativeFunctionObject:
        return value.construct(object, fn.argumentList)
    case _nodeFunctionObject:
        argumentList = append(fn.argumentList, argumentList...)
        return object.construct(argumentList)
    }
    panic(fn.target.runtime.panicTypeError())
}

// =================== //
// _nodeFunctionObject //
// =================== //

type _nodeFunctionObject struct {
    node  *_nodeFunctionLiteral
    stash _stash
}

func (runtime *_runtime) newNodeFunctionObject(node *_nodeFunctionLiteral, stash _stash) *_object {
    self := runtime.newClassObject("Function")
    self.value = _nodeFunctionObject{
        node:  node,
        stash: stash,
    }
    self.defineProperty("length", toValue_int(len(node.parameterList)), 0000, false)
    return self
}

// ======= //
// _object //
// ======= //

func (self *_object) isCall() bool {
    switch fn := self.value.(type) {
    case _nativeFunctionObject:
        return fn.call != nil
    case _bindFunctionObject:
        return true
    case _nodeFunctionObject:
        return true
    }
    return false
}

func (self *_object) call(this Value, argumentList []Value, eval bool, frame _frame) Value {
    switch fn := self.value.(type) {

    case _nativeFunctionObject:
        // Since eval is a native function, we only have to check for it here
        if eval {
            eval = self == self.runtime.eval // If eval is true, then it IS a direct eval
        }

        // Enter a scope, name from the native object...
        rt := self.runtime
        if rt.scope != nil && !eval {
            rt.enterFunctionScope(rt.scope.lexical, this)
            rt.scope.frame = _frame{
                native:     true,
                nativeFile: fn.file,
                nativeLine: fn.line,
                callee:     fn.name,
                file:       nil,
            }
            defer func() {
                rt.leaveScope()
            }()
        }

        return fn.call(FunctionCall{
            runtime: self.runtime,
            eval:    eval,

            This:         this,
            ArgumentList: argumentList,
            Otto:         self.runtime.otto,
        })

    case _bindFunctionObject:
        // TODO Passthrough site, do not enter a scope
        argumentList = append(fn.argumentList, argumentList...)
        return fn.target.call(fn.this, argumentList, false, frame)

    case _nodeFunctionObject:
        rt := self.runtime
        stash := rt.enterFunctionScope(fn.stash, this)
        rt.scope.frame = _frame{
            callee: fn.node.name,
            file:   fn.node.file,
        }
        defer func() {
            rt.leaveScope()
        }()
        callValue := rt.cmpl_call_nodeFunction(self, stash, fn.node, this, argumentList)
        if value, valid := callValue.value.(_result); valid {
            return value.value
        }
        return callValue
    }

    panic(self.runtime.panicTypeError("%v is not a function", toValue_object(self)))
}

func (self *_object) construct(argumentList []Value) Value {
    switch fn := self.value.(type) {

    case _nativeFunctionObject:
        if fn.call == nil {
            panic(self.runtime.panicTypeError("%v is not a function", toValue_object(self)))
        }
        if fn.construct == nil {
            panic(self.runtime.panicTypeError("%v is not a constructor", toValue_object(self)))
        }
        return fn.construct(self, argumentList)

    case _bindFunctionObject:
        return fn.construct(argumentList)

    case _nodeFunctionObject:
        return defaultConstruct(self, argumentList)
    }

    panic(self.runtime.panicTypeError("%v is not a function", toValue_object(self)))
}

// 15.3.5.3
func (self *_object) hasInstance(of Value) bool {
    if !self.isCall() {
        // We should not have a hasInstance method
        panic(self.runtime.panicTypeError())
    }
    if !of.IsObject() {
        return false
    }
    prototype := self.get("prototype")
    if !prototype.IsObject() {
        panic(self.runtime.panicTypeError())
    }
    prototypeObject := prototype._object()

    value := of._object().prototype
    for value != nil {
        if value == prototypeObject {
            return true
        }
        value = value.prototype
    }
    return false
}

// ============ //
// FunctionCall //
// ============ //

// FunctionCall is an encapsulation of a JavaScript function call.
type FunctionCall struct {
    runtime     *_runtime
    _thisObject *_object
    eval        bool // This call is a direct call to eval

    This         Value
    ArgumentList []Value
    Otto         *Otto
}

// Argument will return the value of the argument at the given index.
//
// If no such argument exists, undefined is returned.
func (self FunctionCall) Argument(index int) Value {
    return valueOfArrayIndex(self.ArgumentList, index)
}

func (self FunctionCall) getArgument(index int) (Value, bool) {
    return getValueOfArrayIndex(self.ArgumentList, index)
}

func (self FunctionCall) slice(index int) []Value {
    if index < len(self.ArgumentList) {
        return self.ArgumentList[index:]
    }
    return []Value{}
}

func (self *FunctionCall) thisObject() *_object {
    if self._thisObject == nil {
        this := self.This.resolve() // FIXME Is this right?
        self._thisObject = self.runtime.toObject(this)
    }
    return self._thisObject
}

func (self *FunctionCall) thisClassObject(class string) *_object {
    thisObject := self.thisObject()
    if thisObject.class != class {
        panic(self.runtime.panicTypeError())
    }
    return self._thisObject
}

func (self FunctionCall) toObject(value Value) *_object {
    return self.runtime.toObject(value)
}

// CallerLocation will return file location information (file:line:pos) where this function is being called.
func (self FunctionCall) CallerLocation() string {
    // see error.go for location()
    return self.runtime.scope.outer.frame.location()
}