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()
}