2024-02-24 21:00:04 +00:00
|
|
|
package otto
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
)
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
// stasher is implemented by types which can stash data.
|
|
|
|
type stasher interface {
|
2024-02-24 21:00:04 +00:00
|
|
|
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
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
outer() stasher
|
|
|
|
runtime() *runtime
|
2024-02-24 21:00:04 +00:00
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
newReference(string, bool, at) referencer
|
2024-02-24 21:00:04 +00:00
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
clone(*cloner) stasher
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
type objectStash struct {
|
|
|
|
rt *runtime
|
|
|
|
outr stasher
|
|
|
|
object *object
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (s *objectStash) runtime() *runtime {
|
|
|
|
return s.rt
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (rt *runtime) newObjectStash(obj *object, outer stasher) *objectStash {
|
|
|
|
if obj == nil {
|
|
|
|
obj = rt.newBaseObject()
|
|
|
|
obj.class = "environment"
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
2024-04-04 14:46:14 +00:00
|
|
|
return &objectStash{
|
|
|
|
rt: rt,
|
|
|
|
outr: outer,
|
|
|
|
object: obj,
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (s *objectStash) clone(c *cloner) stasher {
|
|
|
|
out, exists := c.objectStash(s)
|
2024-02-24 21:00:04 +00:00
|
|
|
if exists {
|
|
|
|
return out
|
|
|
|
}
|
2024-04-04 14:46:14 +00:00
|
|
|
*out = objectStash{
|
|
|
|
c.runtime,
|
|
|
|
c.stash(s.outr),
|
|
|
|
c.object(s.object),
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (s *objectStash) hasBinding(name string) bool {
|
|
|
|
return s.object.hasProperty(name)
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (s *objectStash) createBinding(name string, deletable bool, value Value) {
|
|
|
|
if s.object.hasProperty(name) {
|
2024-02-24 21:00:04 +00:00
|
|
|
panic(hereBeDragons())
|
|
|
|
}
|
2024-04-04 14:46:14 +00:00
|
|
|
mode := propertyMode(0o111)
|
2024-02-24 21:00:04 +00:00
|
|
|
if !deletable {
|
2024-04-04 14:46:14 +00:00
|
|
|
mode = propertyMode(0o110)
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
// TODO False?
|
2024-04-04 14:46:14 +00:00
|
|
|
s.object.defineProperty(name, value, mode, false)
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (s *objectStash) setBinding(name string, value Value, strict bool) {
|
|
|
|
s.object.put(name, value, strict)
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (s *objectStash) setValue(name string, value Value, throw bool) {
|
|
|
|
if !s.hasBinding(name) {
|
|
|
|
s.createBinding(name, true, value) // Configurable by default
|
2024-02-24 21:00:04 +00:00
|
|
|
} else {
|
2024-04-04 14:46:14 +00:00
|
|
|
s.setBinding(name, value, throw)
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (s *objectStash) getBinding(name string, throw bool) Value {
|
|
|
|
if s.object.hasProperty(name) {
|
|
|
|
return s.object.get(name)
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
if throw { // strict?
|
2024-04-04 14:46:14 +00:00
|
|
|
panic(s.rt.panicReferenceError("Not Defined", name))
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
return Value{}
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (s *objectStash) deleteBinding(name string) bool {
|
|
|
|
return s.object.delete(name, false)
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (s *objectStash) outer() stasher {
|
|
|
|
return s.outr
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (s *objectStash) newReference(name string, strict bool, atv at) referencer {
|
|
|
|
return newPropertyReference(s.rt, s.object, name, strict, atv)
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
type dclStash struct {
|
|
|
|
rt *runtime
|
|
|
|
outr stasher
|
|
|
|
property map[string]dclProperty
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
type dclProperty struct {
|
2024-02-24 21:00:04 +00:00
|
|
|
value Value
|
|
|
|
mutable bool
|
|
|
|
deletable bool
|
|
|
|
readable bool
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (rt *runtime) newDeclarationStash(outer stasher) *dclStash {
|
|
|
|
return &dclStash{
|
|
|
|
rt: rt,
|
|
|
|
outr: outer,
|
|
|
|
property: map[string]dclProperty{},
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (s *dclStash) clone(c *cloner) stasher {
|
|
|
|
out, exists := c.dclStash(s)
|
2024-02-24 21:00:04 +00:00
|
|
|
if exists {
|
|
|
|
return out
|
|
|
|
}
|
2024-04-04 14:46:14 +00:00
|
|
|
prop := make(map[string]dclProperty, len(s.property))
|
|
|
|
for index, value := range s.property {
|
|
|
|
prop[index] = c.dclProperty(value)
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
2024-04-04 14:46:14 +00:00
|
|
|
*out = dclStash{
|
|
|
|
c.runtime,
|
|
|
|
c.stash(s.outr),
|
|
|
|
prop,
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (s *dclStash) hasBinding(name string) bool {
|
|
|
|
_, exists := s.property[name]
|
2024-02-24 21:00:04 +00:00
|
|
|
return exists
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (s *dclStash) runtime() *runtime {
|
|
|
|
return s.rt
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (s *dclStash) createBinding(name string, deletable bool, value Value) {
|
|
|
|
if _, exists := s.property[name]; exists {
|
2024-02-24 21:00:04 +00:00
|
|
|
panic(fmt.Errorf("createBinding: %s: already exists", name))
|
|
|
|
}
|
2024-04-04 14:46:14 +00:00
|
|
|
s.property[name] = dclProperty{
|
2024-02-24 21:00:04 +00:00
|
|
|
value: value,
|
|
|
|
mutable: true,
|
|
|
|
deletable: deletable,
|
|
|
|
readable: false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (s *dclStash) setBinding(name string, value Value, strict bool) {
|
|
|
|
prop, exists := s.property[name]
|
2024-02-24 21:00:04 +00:00
|
|
|
if !exists {
|
|
|
|
panic(fmt.Errorf("setBinding: %s: missing", name))
|
|
|
|
}
|
2024-04-04 14:46:14 +00:00
|
|
|
if prop.mutable {
|
|
|
|
prop.value = value
|
|
|
|
s.property[name] = prop
|
2024-02-24 21:00:04 +00:00
|
|
|
} else {
|
2024-04-04 14:46:14 +00:00
|
|
|
s.rt.typeErrorResult(strict)
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (s *dclStash) setValue(name string, value Value, throw bool) {
|
|
|
|
if !s.hasBinding(name) {
|
|
|
|
s.createBinding(name, false, value) // NOT deletable by default
|
2024-02-24 21:00:04 +00:00
|
|
|
} else {
|
2024-04-04 14:46:14 +00:00
|
|
|
s.setBinding(name, value, throw)
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
// FIXME This is called a __lot__.
|
|
|
|
func (s *dclStash) getBinding(name string, throw bool) Value {
|
|
|
|
prop, exists := s.property[name]
|
2024-02-24 21:00:04 +00:00
|
|
|
if !exists {
|
|
|
|
panic(fmt.Errorf("getBinding: %s: missing", name))
|
|
|
|
}
|
2024-04-04 14:46:14 +00:00
|
|
|
if !prop.mutable && !prop.readable {
|
2024-02-24 21:00:04 +00:00
|
|
|
if throw { // strict?
|
2024-04-04 14:46:14 +00:00
|
|
|
panic(s.rt.panicTypeError("getBinding property %s not mutable and not readable", name))
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
return Value{}
|
|
|
|
}
|
2024-04-04 14:46:14 +00:00
|
|
|
return prop.value
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (s *dclStash) deleteBinding(name string) bool {
|
|
|
|
prop, exists := s.property[name]
|
2024-02-24 21:00:04 +00:00
|
|
|
if !exists {
|
|
|
|
return true
|
|
|
|
}
|
2024-04-04 14:46:14 +00:00
|
|
|
if !prop.deletable {
|
2024-02-24 21:00:04 +00:00
|
|
|
return false
|
|
|
|
}
|
2024-04-04 14:46:14 +00:00
|
|
|
delete(s.property, name)
|
2024-02-24 21:00:04 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (s *dclStash) outer() stasher {
|
|
|
|
return s.outr
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (s *dclStash) newReference(name string, strict bool, _ at) referencer {
|
|
|
|
return &stashReference{
|
2024-02-24 21:00:04 +00:00
|
|
|
name: name,
|
2024-04-04 14:46:14 +00:00
|
|
|
base: s,
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ========
|
|
|
|
// _fnStash
|
|
|
|
// ========
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
type fnStash struct {
|
|
|
|
dclStash
|
|
|
|
arguments *object
|
2024-02-24 21:00:04 +00:00
|
|
|
indexOfArgumentName map[string]string
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (rt *runtime) newFunctionStash(outer stasher) *fnStash {
|
|
|
|
return &fnStash{
|
|
|
|
dclStash: dclStash{
|
|
|
|
rt: rt,
|
|
|
|
outr: outer,
|
|
|
|
property: map[string]dclProperty{},
|
2024-02-24 21:00:04 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (s *fnStash) clone(c *cloner) stasher {
|
|
|
|
out, exists := c.fnStash(s)
|
2024-02-24 21:00:04 +00:00
|
|
|
if exists {
|
|
|
|
return out
|
|
|
|
}
|
2024-04-04 14:46:14 +00:00
|
|
|
dclStash := s.dclStash.clone(c).(*dclStash)
|
|
|
|
index := make(map[string]string, len(s.indexOfArgumentName))
|
|
|
|
for name, value := range s.indexOfArgumentName {
|
2024-02-24 21:00:04 +00:00
|
|
|
index[name] = value
|
|
|
|
}
|
2024-04-04 14:46:14 +00:00
|
|
|
*out = fnStash{
|
|
|
|
dclStash: *dclStash,
|
|
|
|
arguments: c.object(s.arguments),
|
2024-02-24 21:00:04 +00:00
|
|
|
indexOfArgumentName: index,
|
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
// getStashProperties returns the properties from stash.
|
|
|
|
func getStashProperties(stash stasher) []string {
|
2024-02-24 21:00:04 +00:00
|
|
|
switch vars := stash.(type) {
|
2024-04-04 14:46:14 +00:00
|
|
|
case *dclStash:
|
|
|
|
keys := make([]string, 0, len(vars.property))
|
2024-02-24 21:00:04 +00:00
|
|
|
for k := range vars.property {
|
|
|
|
keys = append(keys, k)
|
|
|
|
}
|
2024-04-04 14:46:14 +00:00
|
|
|
return keys
|
|
|
|
case *fnStash:
|
|
|
|
keys := make([]string, 0, len(vars.property))
|
2024-02-24 21:00:04 +00:00
|
|
|
for k := range vars.property {
|
|
|
|
keys = append(keys, k)
|
|
|
|
}
|
2024-04-04 14:46:14 +00:00
|
|
|
return keys
|
|
|
|
case *objectStash:
|
|
|
|
keys := make([]string, 0, len(vars.object.property))
|
2024-02-24 21:00:04 +00:00
|
|
|
for k := range vars.object.property {
|
|
|
|
keys = append(keys, k)
|
|
|
|
}
|
2024-04-04 14:46:14 +00:00
|
|
|
return keys
|
2024-02-24 21:00:04 +00:00
|
|
|
default:
|
|
|
|
panic("unknown stash type")
|
|
|
|
}
|
|
|
|
}
|