mirror of
https://github.com/robertkrimen/otto
synced 2025-10-19 19:55:30 +08:00

* Streamline what we get from "otto/parser" * Get rid of some "otto/parser" cruft * FunctionExpression => FunctionLiteral * The debugger statement (debugger) should do nothing (not panic) * Fix aspects of function expression call evaluation
277 lines
7.6 KiB
Go
277 lines
7.6 KiB
Go
package otto
|
|
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
type _functionObject struct {
|
|
call _callFunction
|
|
construct _constructFunction
|
|
}
|
|
|
|
func (self _functionObject) source(object *_object) string {
|
|
return self.call.Source(object)
|
|
}
|
|
|
|
func (self0 _functionObject) clone(clone *_clone) _functionObject {
|
|
return _functionObject{
|
|
clone.callFunction(self0.call),
|
|
self0.construct,
|
|
}
|
|
}
|
|
|
|
func (runtime *_runtime) newNativeFunctionObject(name string, native _nativeFunction, length int) *_object {
|
|
self := runtime.newClassObject("Function")
|
|
self.value = _functionObject{
|
|
call: newNativeCallFunction(native),
|
|
construct: defaultConstructFunction,
|
|
}
|
|
self.defineProperty("length", toValue_int(length), 0000, false)
|
|
return self
|
|
}
|
|
|
|
func (runtime *_runtime) newBoundFunctionObject(target *_object, this Value, argumentList []Value) *_object {
|
|
self := runtime.newClassObject("Function")
|
|
self.value = _functionObject{
|
|
call: newBoundCallFunction(target, this, argumentList),
|
|
construct: newBoundConstructFunction(target),
|
|
}
|
|
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", UndefinedValue(), 0000, false) // TODO Should throw a TypeError
|
|
self.defineProperty("arguments", UndefinedValue(), 0000, false) // TODO Should throw a TypeError
|
|
return self
|
|
}
|
|
|
|
func (runtime *_runtime) newBoundFunction(target *_object, this Value, argumentList []Value) *_object {
|
|
self := runtime.newBoundFunctionObject(target, this, argumentList)
|
|
self.prototype = runtime.Global.FunctionPrototype
|
|
prototype := runtime.newObject()
|
|
self.defineProperty("prototype", toValue_object(prototype), 0100, false)
|
|
prototype.defineProperty("constructor", toValue_object(self), 0100, false)
|
|
return self
|
|
}
|
|
|
|
func (self *_object) functionValue() _functionObject {
|
|
value, _ := self.value.(_functionObject)
|
|
return value
|
|
}
|
|
|
|
func (self *_object) Call(this Value, argumentList ...interface{}) Value {
|
|
if self.functionValue().call == nil {
|
|
panic(newTypeError("%v is not a function", toValue_object(self)))
|
|
}
|
|
return self.runtime.Call(self, this, self.runtime.toValueArray(argumentList...), false)
|
|
// ... -> runtime -> self.Function.Call.Dispatch -> ...
|
|
}
|
|
|
|
func (self *_object) Construct(this Value, argumentList ...interface{}) Value {
|
|
function := self.functionValue()
|
|
if function.call == nil {
|
|
panic(newTypeError("%v is not a function", toValue_object(self)))
|
|
}
|
|
if function.construct == nil {
|
|
panic(newTypeError("%v is not a constructor", toValue_object(self)))
|
|
}
|
|
return function.construct(self, this, self.runtime.toValueArray(argumentList...))
|
|
}
|
|
|
|
func defaultConstructFunction(self *_object, this Value, argumentList []Value) Value {
|
|
newObject := self.runtime.newObject()
|
|
newObject.class = "Object"
|
|
prototypeValue := self.get("prototype")
|
|
if !prototypeValue.IsObject() {
|
|
prototypeValue = toValue_object(self.runtime.Global.ObjectPrototype)
|
|
}
|
|
newObject.prototype = prototypeValue._object()
|
|
newObjectValue := toValue_object(newObject)
|
|
result := self.Call(newObjectValue, argumentList)
|
|
if result.IsObject() {
|
|
return result
|
|
}
|
|
return newObjectValue
|
|
}
|
|
|
|
func (self *_object) callGet(this Value) Value {
|
|
return self.runtime.Call(self, this, []Value(nil), false)
|
|
}
|
|
|
|
func (self *_object) callSet(this Value, value Value) {
|
|
self.runtime.Call(self, this, []Value{value}, false)
|
|
}
|
|
|
|
// 15.3.5.3
|
|
func (self *_object) HasInstance(of Value) bool {
|
|
if self.functionValue().call == nil {
|
|
// We should not have a HasInstance method
|
|
panic(newTypeError())
|
|
}
|
|
if !of.IsObject() {
|
|
return false
|
|
}
|
|
prototype := self.get("prototype")
|
|
if !prototype.IsObject() {
|
|
panic(newTypeError())
|
|
}
|
|
prototypeObject := prototype._object()
|
|
|
|
value := of._object().prototype
|
|
for value != nil {
|
|
if value == prototypeObject {
|
|
return true
|
|
}
|
|
value = value.prototype
|
|
}
|
|
return false
|
|
}
|
|
|
|
type _nativeFunction func(FunctionCall) Value
|
|
|
|
// _constructFunction
|
|
type _constructFunction func(*_object, Value, []Value) Value
|
|
|
|
// _callFunction
|
|
type _callFunction interface {
|
|
Dispatch(*_object, *_functionEnvironment, *_runtime, Value, []Value, bool) Value
|
|
Source(*_object) string
|
|
ScopeEnvironment() _environment
|
|
clone(clone *_clone) _callFunction
|
|
}
|
|
|
|
// _nativeCallFunction
|
|
type _nativeCallFunction struct {
|
|
name string
|
|
function _nativeFunction
|
|
}
|
|
|
|
func newNativeCallFunction(native _nativeFunction) _nativeCallFunction {
|
|
return _nativeCallFunction{"", native}
|
|
}
|
|
|
|
func (self _nativeCallFunction) Dispatch(_ *_object, _ *_functionEnvironment, runtime *_runtime, this Value, argumentList []Value, evalHint bool) Value {
|
|
return self.function(FunctionCall{
|
|
runtime: runtime,
|
|
evalHint: evalHint,
|
|
|
|
This: this,
|
|
ArgumentList: argumentList,
|
|
Otto: runtime.Otto,
|
|
})
|
|
}
|
|
|
|
func (self _nativeCallFunction) ScopeEnvironment() _environment {
|
|
return nil
|
|
}
|
|
|
|
func (self _nativeCallFunction) Source(*_object) string {
|
|
return fmt.Sprintf("function %s() { [native code] }", self.name)
|
|
}
|
|
|
|
func (self0 _nativeCallFunction) clone(clone *_clone) _callFunction {
|
|
return self0
|
|
}
|
|
|
|
// _boundCallFunction
|
|
type _boundCallFunction struct {
|
|
target *_object
|
|
this Value
|
|
argumentList []Value
|
|
}
|
|
|
|
func newBoundCallFunction(target *_object, this Value, argumentList []Value) *_boundCallFunction {
|
|
self := &_boundCallFunction{
|
|
target: target,
|
|
this: this,
|
|
argumentList: argumentList,
|
|
}
|
|
return self
|
|
}
|
|
|
|
func (self _boundCallFunction) Dispatch(_ *_object, _ *_functionEnvironment, runtime *_runtime, this Value, argumentList []Value, _ bool) Value {
|
|
argumentList = append(self.argumentList, argumentList...)
|
|
return runtime.Call(self.target, self.this, argumentList, false)
|
|
}
|
|
|
|
func (self _boundCallFunction) ScopeEnvironment() _environment {
|
|
return nil
|
|
}
|
|
|
|
func (self _boundCallFunction) Source(*_object) string {
|
|
return ""
|
|
}
|
|
|
|
func (self0 _boundCallFunction) clone(clone *_clone) _callFunction {
|
|
return _boundCallFunction{
|
|
target: clone.object(self0.target),
|
|
this: clone.value(self0.this),
|
|
argumentList: clone.valueArray(self0.argumentList),
|
|
}
|
|
}
|
|
|
|
func newBoundConstructFunction(target *_object) _constructFunction {
|
|
// This is not exactly as described in 15.3.4.5.2, we let [[Call]] supply the
|
|
// bound arguments, etc.
|
|
return func(self *_object, this Value, argumentList []Value) Value {
|
|
switch value := target.value.(type) {
|
|
case _functionObject:
|
|
return value.construct(self, this, argumentList)
|
|
}
|
|
panic(newTypeError())
|
|
}
|
|
}
|
|
|
|
// FunctionCall{}
|
|
|
|
// FunctionCall is an encapsulation of a JavaScript function call.
|
|
type FunctionCall struct {
|
|
runtime *_runtime
|
|
_thisObject *_object
|
|
evalHint bool
|
|
|
|
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.runtime.GetValue(self.This) // 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(newTypeError())
|
|
}
|
|
return self._thisObject
|
|
}
|
|
|
|
func (self FunctionCall) toObject(value Value) *_object {
|
|
return self.runtime.toObject(value)
|
|
}
|