mirror of
https://github.com/robertkrimen/otto
synced 2025-09-28 18:45:22 +08:00
309 lines
8.3 KiB
Go
309 lines
8.3 KiB
Go
package otto
|
|
|
|
import (
|
|
"strconv"
|
|
//"fmt"
|
|
)
|
|
|
|
type _runtime struct {
|
|
Stack [](*_executionContext)
|
|
|
|
GlobalObject *_object
|
|
GlobalEnvironment *_objectEnvironment
|
|
|
|
Global struct {
|
|
Object *_object // Object( ... ), new Object( ... ) - 1 (length)
|
|
Function *_object // Function( ... ), new Function( ... ) - 1
|
|
Array *_object // Array( ... ), new Array( ... ) - 1
|
|
String *_object // String( ... ), new String( ... ) - 1
|
|
Boolean *_object // Boolean( ... ), new Boolean( ... ) - 1
|
|
Number *_object // Number( ... ), new Number( ... ) - 1
|
|
Math *_object
|
|
Date *_object // Date( ... ), new Date( ... ) - 7
|
|
RegExp *_object // RegExp( ... ), new RegExp( ... ) - 2
|
|
Error *_object // Error( ... ), new Error( ... ) - 1
|
|
// JSON
|
|
|
|
ObjectPrototype *_object // Object.prototype
|
|
FunctionPrototype *_object // Function.prototype
|
|
ArrayPrototype *_object // Array.prototype
|
|
StringPrototype *_object // String.prototype
|
|
BooleanPrototype *_object // Boolean.prototype
|
|
NumberPrototype *_object // Number.prototype
|
|
DatePrototype *_object // Date.prototype
|
|
RegExpPrototype *_object // RegExp.prototype
|
|
ErrorPrototype *_object // Error.prototype
|
|
}
|
|
|
|
_newError map[string] func(Value) *_object
|
|
}
|
|
|
|
func (self *_runtime) EnterGlobalExecutionContext() {
|
|
self.EnterExecutionContext(newExecutionContext(self.GlobalEnvironment, self.GlobalEnvironment, self.GlobalObject))
|
|
}
|
|
|
|
func (self *_runtime) EnterExecutionContext(scope *_executionContext) {
|
|
self.Stack = append(self.Stack, scope)
|
|
}
|
|
|
|
func (self *_runtime) LeaveExecutionContext() {
|
|
self.Stack = self.Stack[:len(self.Stack)-1]
|
|
}
|
|
|
|
func (self *_runtime) _executionContext(depth int) *_executionContext {
|
|
if depth == 0 {
|
|
return self.Stack[len(self.Stack)-1]
|
|
}
|
|
if len(self.Stack)-1+depth >= 0 {
|
|
return self.Stack[len(self.Stack)-1+depth]
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (self *_runtime) EnterFunctionExecutionContext(function *_object, this Value) *_functionEnvironment {
|
|
scopeEnvironment := function.Function.Call.ScopeEnvironment()
|
|
if scopeEnvironment == nil {
|
|
scopeEnvironment = self.GlobalEnvironment
|
|
}
|
|
environment := self.newFunctionEnvironment(scopeEnvironment)
|
|
var thisObject *_object
|
|
switch this._valueType {
|
|
case valueUndefined, valueNull:
|
|
thisObject = self.GlobalObject
|
|
default:
|
|
thisObject = self.toObject(this)
|
|
}
|
|
self.EnterExecutionContext(newExecutionContext(environment, environment, thisObject))
|
|
return environment
|
|
}
|
|
|
|
func (self *_runtime) EnterEvalExecutionContext(call FunctionCall) {
|
|
// Skip the current function lexical/variable environment, which is of the function execution context call
|
|
// to eval (the global execution context). Instead, execute in the context of where the eval was called,
|
|
// which is essentially dynamic scoping
|
|
_executionContext := self._executionContext(-1)
|
|
self.EnterExecutionContext(newExecutionContext(_executionContext.LexicalEnvironment, _executionContext.VariableEnvironment, nil))
|
|
}
|
|
|
|
func (self *_runtime) Return(value Value) {
|
|
panic(newReturnResult(value))
|
|
}
|
|
|
|
func (self *_runtime) Continue(target string) {
|
|
panic(newContinueResult(target))
|
|
}
|
|
|
|
func (self *_runtime) Break(target string) {
|
|
panic(newBreakResult(target))
|
|
}
|
|
|
|
func (self *_runtime) Throw(value Value) {
|
|
panic(newThrowResult(value))
|
|
}
|
|
|
|
func (self *_runtime) GetValue(value Value) Value {
|
|
if value.isReference() {
|
|
return value.reference().GetValue()
|
|
}
|
|
return value
|
|
}
|
|
|
|
func (self *_runtime) PutValue(reference _reference, value Value) {
|
|
if !reference.PutValue(value) {
|
|
// Why? -- If reference.Base == nil
|
|
strict := false
|
|
self.GlobalObject.WriteValue(reference.Name(), value, strict)
|
|
}
|
|
}
|
|
|
|
func (self *_runtime) _callNode(environment *_functionEnvironment, node *_functionNode, this Value, argumentList []Value) Value {
|
|
|
|
self.declare("function", node.FunctionList)
|
|
|
|
if node.ArgumentsIsParameter {
|
|
for i, name := range node.ParameterList {
|
|
value := UndefinedValue()
|
|
if i < len(argumentList) {
|
|
value = argumentList[i]
|
|
}
|
|
self.localSet(name, value)
|
|
}
|
|
} else {
|
|
// FIXME Why does this work before but not after?
|
|
for index := len(argumentList); index < len(node.ParameterList); index++ {
|
|
name := node.ParameterList[index]
|
|
self.localSet(name, UndefinedValue())
|
|
}
|
|
arguments := self.newArgumentsObject(argumentList)
|
|
environment.arguments = arguments
|
|
self.localSet("arguments", toValue(arguments))
|
|
indexOfArgumentName := map[string]string{}
|
|
for index, _ := range argumentList {
|
|
if index >= len(node.ParameterList) {
|
|
break
|
|
}
|
|
name := node.ParameterList[index]
|
|
indexOfArgumentName[name] = strconv.FormatInt(int64(index), 10)
|
|
}
|
|
environment.indexOfArgumentName = indexOfArgumentName
|
|
}
|
|
|
|
self.declare("variable", node.VariableList)
|
|
|
|
self.evaluateBody(node.Body)
|
|
|
|
return UndefinedValue()
|
|
}
|
|
|
|
func (self *_runtime) Call(function *_object, this Value, argumentList []Value) (returnValue Value) {
|
|
_functionEnvironment := self.EnterFunctionExecutionContext(function, this)
|
|
defer func(){
|
|
self.LeaveExecutionContext();
|
|
if caught := recover(); caught != nil {
|
|
if result, ok := caught.(_result); ok {
|
|
if result.Kind == resultReturn {
|
|
returnValue = result.Value
|
|
return
|
|
}
|
|
}
|
|
panic(caught)
|
|
}
|
|
}()
|
|
|
|
returnValue = function.Function.Call.Dispatch(_functionEnvironment, self, this, argumentList)
|
|
return
|
|
}
|
|
|
|
func (self *_runtime) tryEvaluate(inner func() Value) (tryValue Value, throw bool, throwValue Value) {
|
|
defer func(){
|
|
if caught := recover(); caught != nil {
|
|
switch caught := caught.(type) {
|
|
case _result:
|
|
if caught.Kind == resultThrow {
|
|
throw = true
|
|
throwValue = caught.Value
|
|
return
|
|
}
|
|
case _error:
|
|
throw = true
|
|
throwValue = toValue(self.newError(caught.Name, caught.MessageValue()))
|
|
return
|
|
}
|
|
panic(caught)
|
|
}
|
|
}()
|
|
|
|
tryValue = inner()
|
|
return
|
|
}
|
|
|
|
func (self *_runtime) breakEvaluate(_labelSet map[string]bool, inner func() Value) Value {
|
|
defer func(){
|
|
if caught := recover(); caught != nil {
|
|
if result, ok := caught.(_result); ok {
|
|
if result.Kind == resultBreak && _labelSet[result.Target] == true {
|
|
return
|
|
}
|
|
}
|
|
panic(caught)
|
|
}
|
|
}()
|
|
|
|
return inner()
|
|
}
|
|
|
|
func (self *_runtime) continueEvaluate(node _node, _labelSet map[string]bool) (returnResult Value, skip bool) {
|
|
defer func(){
|
|
if caught := recover(); caught != nil {
|
|
if result, ok := caught.(_result); ok {
|
|
if result.Kind == resultContinue && _labelSet[result.Target] == true {
|
|
returnResult = emptyValue()
|
|
skip = true
|
|
return
|
|
}
|
|
}
|
|
panic(caught)
|
|
}
|
|
}()
|
|
return self.evaluate(node), false
|
|
}
|
|
|
|
func (self *_runtime) declare(kind string, declarationList []_declaration) {
|
|
for _, _declaration := range declarationList {
|
|
self.localSet(_declaration.Name, UndefinedValue())
|
|
if kind == "function" {
|
|
value := self.evaluate(_declaration.Definition)
|
|
self.localSet(_declaration.Name, value)
|
|
}
|
|
}
|
|
}
|
|
|
|
// _executionContext Proxy
|
|
|
|
func (self *_runtime) localGet(name string) Value {
|
|
return self._executionContext(0).GetValue(name)
|
|
}
|
|
|
|
func (self *_runtime) localSet(name string, value Value) {
|
|
self._executionContext(0).SetValue(name, value, false)
|
|
}
|
|
|
|
func (self *_runtime) VariableEnvironment() _environment {
|
|
return self._executionContext(0).VariableEnvironment
|
|
}
|
|
|
|
func (self *_runtime) LexicalEnvironment() _environment {
|
|
return self._executionContext(0).LexicalEnvironment
|
|
}
|
|
|
|
// toObject
|
|
|
|
func (self *_runtime) toObject(value Value) *_object {
|
|
switch value._valueType {
|
|
case valueEmpty, valueUndefined, valueNull:
|
|
panic(newTypeError())
|
|
case valueBoolean:
|
|
return self.newBoolean(value)
|
|
case valueString:
|
|
return self.newString(value)
|
|
case valueNumber:
|
|
return self.newNumber(value)
|
|
case valueObject:
|
|
return value._object()
|
|
}
|
|
panic(newTypeError())
|
|
}
|
|
|
|
func checkObjectCoercible(value Value) {
|
|
isObject, mustCoerce := testObjectCoercible(value)
|
|
if !isObject && !mustCoerce {
|
|
panic(newTypeError())
|
|
}
|
|
}
|
|
|
|
// testObjectCoercible
|
|
|
|
func testObjectCoercible(value Value) (isObject bool, mustCoerce bool) {
|
|
switch value._valueType {
|
|
case valueReference, valueEmpty, valueNull, valueUndefined:
|
|
return false, false
|
|
case valueNumber, valueString, valueBoolean:
|
|
isObject = false
|
|
mustCoerce = true
|
|
case valueObject:
|
|
isObject = true
|
|
mustCoerce = false
|
|
}
|
|
return
|
|
}
|
|
|
|
func (self *_runtime) toValue(value interface{}) Value {
|
|
switch value := value.(type) {
|
|
case func(FunctionCall) Value:
|
|
return toValue(self.newNativeFunction(value, 0))
|
|
case _nativeFunction:
|
|
return toValue(self.newNativeFunction(value, 0))
|
|
}
|
|
return toValue(value)
|
|
}
|