mirror of
https://github.com/robertkrimen/otto
synced 2025-10-12 20:27:30 +08:00

This change introduces a Context method to otto that allows developers to get information about the current execution context. The method returns a Context struct that contains information such as the filename, line and column of the current execution, the current value of this, the stacktrace and the available symbols at the current context.
297 lines
6.2 KiB
Go
297 lines
6.2 KiB
Go
package otto
|
|
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
// ======
|
|
// _stash
|
|
// ======
|
|
|
|
type _stash interface {
|
|
hasBinding(string) bool //
|
|
createBinding(string, bool, Value) // CreateMutableBinding
|
|
setBinding(string, Value, bool) // SetMutableBinding
|
|
getBinding(string, bool) Value // GetBindingValue
|
|
deleteBinding(string) bool //
|
|
setValue(string, Value, bool) // createBinding + setBinding
|
|
|
|
outer() _stash
|
|
runtime() *_runtime
|
|
|
|
newReference(string, bool, _at) _reference
|
|
|
|
clone(clone *_clone) _stash
|
|
}
|
|
|
|
// ==========
|
|
// _objectStash
|
|
// ==========
|
|
|
|
type _objectStash struct {
|
|
_runtime *_runtime
|
|
_outer _stash
|
|
object *_object
|
|
}
|
|
|
|
func (self *_objectStash) runtime() *_runtime {
|
|
return self._runtime
|
|
}
|
|
|
|
func (runtime *_runtime) newObjectStash(object *_object, outer _stash) *_objectStash {
|
|
if object == nil {
|
|
object = runtime.newBaseObject()
|
|
object.class = "environment"
|
|
}
|
|
return &_objectStash{
|
|
_runtime: runtime,
|
|
_outer: outer,
|
|
object: object,
|
|
}
|
|
}
|
|
|
|
func (in *_objectStash) clone(clone *_clone) _stash {
|
|
out, exists := clone.objectStash(in)
|
|
if exists {
|
|
return out
|
|
}
|
|
*out = _objectStash{
|
|
clone.runtime,
|
|
clone.stash(in._outer),
|
|
clone.object(in.object),
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (self *_objectStash) hasBinding(name string) bool {
|
|
return self.object.hasProperty(name)
|
|
}
|
|
|
|
func (self *_objectStash) createBinding(name string, deletable bool, value Value) {
|
|
if self.object.hasProperty(name) {
|
|
panic(hereBeDragons())
|
|
}
|
|
mode := _propertyMode(0111)
|
|
if !deletable {
|
|
mode = _propertyMode(0110)
|
|
}
|
|
// TODO False?
|
|
self.object.defineProperty(name, value, mode, false)
|
|
}
|
|
|
|
func (self *_objectStash) setBinding(name string, value Value, strict bool) {
|
|
self.object.put(name, value, strict)
|
|
}
|
|
|
|
func (self *_objectStash) setValue(name string, value Value, throw bool) {
|
|
if !self.hasBinding(name) {
|
|
self.createBinding(name, true, value) // Configurable by default
|
|
} else {
|
|
self.setBinding(name, value, throw)
|
|
}
|
|
}
|
|
|
|
func (self *_objectStash) getBinding(name string, throw bool) Value {
|
|
if self.object.hasProperty(name) {
|
|
return self.object.get(name)
|
|
}
|
|
if throw { // strict?
|
|
panic(self._runtime.panicReferenceError("Not Defined", name))
|
|
}
|
|
return Value{}
|
|
}
|
|
|
|
func (self *_objectStash) deleteBinding(name string) bool {
|
|
return self.object.delete(name, false)
|
|
}
|
|
|
|
func (self *_objectStash) outer() _stash {
|
|
return self._outer
|
|
}
|
|
|
|
func (self *_objectStash) newReference(name string, strict bool, at _at) _reference {
|
|
return newPropertyReference(self._runtime, self.object, name, strict, at)
|
|
}
|
|
|
|
// =========
|
|
// _dclStash
|
|
// =========
|
|
|
|
type _dclStash struct {
|
|
_runtime *_runtime
|
|
_outer _stash
|
|
property map[string]_dclProperty
|
|
}
|
|
|
|
type _dclProperty struct {
|
|
value Value
|
|
mutable bool
|
|
deletable bool
|
|
readable bool
|
|
}
|
|
|
|
func (runtime *_runtime) newDeclarationStash(outer _stash) *_dclStash {
|
|
return &_dclStash{
|
|
_runtime: runtime,
|
|
_outer: outer,
|
|
property: map[string]_dclProperty{},
|
|
}
|
|
}
|
|
|
|
func (in *_dclStash) clone(clone *_clone) _stash {
|
|
out, exists := clone.dclStash(in)
|
|
if exists {
|
|
return out
|
|
}
|
|
property := make(map[string]_dclProperty, len(in.property))
|
|
for index, value := range in.property {
|
|
property[index] = clone.dclProperty(value)
|
|
}
|
|
*out = _dclStash{
|
|
clone.runtime,
|
|
clone.stash(in._outer),
|
|
property,
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (self *_dclStash) hasBinding(name string) bool {
|
|
_, exists := self.property[name]
|
|
return exists
|
|
}
|
|
|
|
func (self *_dclStash) runtime() *_runtime {
|
|
return self._runtime
|
|
}
|
|
|
|
func (self *_dclStash) createBinding(name string, deletable bool, value Value) {
|
|
_, exists := self.property[name]
|
|
if exists {
|
|
panic(fmt.Errorf("createBinding: %s: already exists", name))
|
|
}
|
|
self.property[name] = _dclProperty{
|
|
value: value,
|
|
mutable: true,
|
|
deletable: deletable,
|
|
readable: false,
|
|
}
|
|
}
|
|
|
|
func (self *_dclStash) setBinding(name string, value Value, strict bool) {
|
|
property, exists := self.property[name]
|
|
if !exists {
|
|
panic(fmt.Errorf("setBinding: %s: missing", name))
|
|
}
|
|
if property.mutable {
|
|
property.value = value
|
|
self.property[name] = property
|
|
} else {
|
|
self._runtime.typeErrorResult(strict)
|
|
}
|
|
}
|
|
|
|
func (self *_dclStash) setValue(name string, value Value, throw bool) {
|
|
if !self.hasBinding(name) {
|
|
self.createBinding(name, false, value) // NOT deletable by default
|
|
} else {
|
|
self.setBinding(name, value, throw)
|
|
}
|
|
}
|
|
|
|
// FIXME This is called a __lot__
|
|
func (self *_dclStash) getBinding(name string, throw bool) Value {
|
|
property, exists := self.property[name]
|
|
if !exists {
|
|
panic(fmt.Errorf("getBinding: %s: missing", name))
|
|
}
|
|
if !property.mutable && !property.readable {
|
|
if throw { // strict?
|
|
panic(self._runtime.panicTypeError())
|
|
}
|
|
return Value{}
|
|
}
|
|
return property.value
|
|
}
|
|
|
|
func (self *_dclStash) deleteBinding(name string) bool {
|
|
property, exists := self.property[name]
|
|
if !exists {
|
|
return true
|
|
}
|
|
if !property.deletable {
|
|
return false
|
|
}
|
|
delete(self.property, name)
|
|
return true
|
|
}
|
|
|
|
func (self *_dclStash) outer() _stash {
|
|
return self._outer
|
|
}
|
|
|
|
func (self *_dclStash) newReference(name string, strict bool, _ _at) _reference {
|
|
return &_stashReference{
|
|
name: name,
|
|
base: self,
|
|
}
|
|
}
|
|
|
|
// ========
|
|
// _fnStash
|
|
// ========
|
|
|
|
type _fnStash struct {
|
|
_dclStash
|
|
arguments *_object
|
|
indexOfArgumentName map[string]string
|
|
}
|
|
|
|
func (runtime *_runtime) newFunctionStash(outer _stash) *_fnStash {
|
|
return &_fnStash{
|
|
_dclStash: _dclStash{
|
|
_runtime: runtime,
|
|
_outer: outer,
|
|
property: map[string]_dclProperty{},
|
|
},
|
|
}
|
|
}
|
|
|
|
func (in *_fnStash) clone(clone *_clone) _stash {
|
|
out, exists := clone.fnStash(in)
|
|
if exists {
|
|
return out
|
|
}
|
|
dclStash := in._dclStash.clone(clone).(*_dclStash)
|
|
index := make(map[string]string, len(in.indexOfArgumentName))
|
|
for name, value := range in.indexOfArgumentName {
|
|
index[name] = value
|
|
}
|
|
*out = _fnStash{
|
|
_dclStash: *dclStash,
|
|
arguments: clone.object(in.arguments),
|
|
indexOfArgumentName: index,
|
|
}
|
|
return out
|
|
}
|
|
|
|
func getStashProperties(stash _stash) (keys []string) {
|
|
switch vars := stash.(type) {
|
|
case *_dclStash:
|
|
for k := range vars.property {
|
|
keys = append(keys, k)
|
|
}
|
|
case *_fnStash:
|
|
for k := range vars.property {
|
|
keys = append(keys, k)
|
|
}
|
|
case *_objectStash:
|
|
for k := range vars.object.property {
|
|
keys = append(keys, k)
|
|
}
|
|
default:
|
|
panic("unknown stash type")
|
|
}
|
|
|
|
return
|
|
}
|