src.dualinventive.com/go/devsim/vendor/github.com/dop251/goja/object.go

626 lines
13 KiB
Go

package goja
import "reflect"
const (
classObject = "Object"
classArray = "Array"
classFunction = "Function"
classNumber = "Number"
classString = "String"
classBoolean = "Boolean"
classError = "Error"
classRegExp = "RegExp"
classDate = "Date"
)
type Object struct {
runtime *Runtime
self objectImpl
}
type iterNextFunc func() (propIterItem, iterNextFunc)
type propertyDescr struct {
Value Value
Writable, Configurable, Enumerable Flag
Getter, Setter Value
}
type objectImpl interface {
sortable
className() string
get(Value) Value
getProp(Value) Value
getPropStr(string) Value
getStr(string) Value
getOwnProp(string) Value
put(Value, Value, bool)
putStr(string, Value, bool)
hasProperty(Value) bool
hasPropertyStr(string) bool
hasOwnProperty(Value) bool
hasOwnPropertyStr(string) bool
_putProp(name string, value Value, writable, enumerable, configurable bool) Value
defineOwnProperty(name Value, descr propertyDescr, throw bool) bool
toPrimitiveNumber() Value
toPrimitiveString() Value
toPrimitive() Value
assertCallable() (call func(FunctionCall) Value, ok bool)
deleteStr(name string, throw bool) bool
delete(name Value, throw bool) bool
proto() *Object
hasInstance(v Value) bool
isExtensible() bool
preventExtensions()
enumerate(all, recusrive bool) iterNextFunc
_enumerate(recursive bool) iterNextFunc
export() interface{}
exportType() reflect.Type
equal(objectImpl) bool
}
type baseObject struct {
class string
val *Object
prototype *Object
extensible bool
values map[string]Value
propNames []string
}
type primitiveValueObject struct {
baseObject
pValue Value
}
func (o *primitiveValueObject) export() interface{} {
return o.pValue.Export()
}
func (o *primitiveValueObject) exportType() reflect.Type {
return o.pValue.ExportType()
}
type FunctionCall struct {
This Value
Arguments []Value
}
type ConstructorCall struct {
This *Object
Arguments []Value
}
func (f FunctionCall) Argument(idx int) Value {
if idx < len(f.Arguments) {
return f.Arguments[idx]
}
return _undefined
}
func (f ConstructorCall) Argument(idx int) Value {
if idx < len(f.Arguments) {
return f.Arguments[idx]
}
return _undefined
}
func (o *baseObject) init() {
o.values = make(map[string]Value)
}
func (o *baseObject) className() string {
return o.class
}
func (o *baseObject) getPropStr(name string) Value {
if val := o.getOwnProp(name); val != nil {
return val
}
if o.prototype != nil {
return o.prototype.self.getPropStr(name)
}
return nil
}
func (o *baseObject) getProp(n Value) Value {
return o.val.self.getPropStr(n.String())
}
func (o *baseObject) hasProperty(n Value) bool {
return o.val.self.getProp(n) != nil
}
func (o *baseObject) hasPropertyStr(name string) bool {
return o.val.self.getPropStr(name) != nil
}
func (o *baseObject) _getStr(name string) Value {
p := o.getOwnProp(name)
if p == nil && o.prototype != nil {
p = o.prototype.self.getPropStr(name)
}
if p, ok := p.(*valueProperty); ok {
return p.get(o.val)
}
return p
}
func (o *baseObject) getStr(name string) Value {
p := o.val.self.getPropStr(name)
if p, ok := p.(*valueProperty); ok {
return p.get(o.val)
}
return p
}
func (o *baseObject) get(n Value) Value {
return o.getStr(n.String())
}
func (o *baseObject) checkDeleteProp(name string, prop *valueProperty, throw bool) bool {
if !prop.configurable {
o.val.runtime.typeErrorResult(throw, "Cannot delete property '%s' of %s", name, o.val.ToString())
return false
}
return true
}
func (o *baseObject) checkDelete(name string, val Value, throw bool) bool {
if val, ok := val.(*valueProperty); ok {
return o.checkDeleteProp(name, val, throw)
}
return true
}
func (o *baseObject) _delete(name string) {
delete(o.values, name)
for i, n := range o.propNames {
if n == name {
copy(o.propNames[i:], o.propNames[i+1:])
o.propNames = o.propNames[:len(o.propNames)-1]
break
}
}
}
func (o *baseObject) deleteStr(name string, throw bool) bool {
if val, exists := o.values[name]; exists {
if !o.checkDelete(name, val, throw) {
return false
}
o._delete(name)
return true
}
return true
}
func (o *baseObject) delete(n Value, throw bool) bool {
return o.deleteStr(n.String(), throw)
}
func (o *baseObject) put(n Value, val Value, throw bool) {
o.putStr(n.String(), val, throw)
}
func (o *baseObject) getOwnProp(name string) Value {
v := o.values[name]
if v == nil && name == "__proto" {
return o.prototype
}
return v
}
func (o *baseObject) putStr(name string, val Value, throw bool) {
if v, exists := o.values[name]; exists {
if prop, ok := v.(*valueProperty); ok {
if !prop.isWritable() {
o.val.runtime.typeErrorResult(throw, "Cannot assign to read only property '%s'", name)
return
}
prop.set(o.val, val)
return
}
o.values[name] = val
return
}
if name == "__proto__" {
if !o.extensible {
o.val.runtime.typeErrorResult(throw, "%s is not extensible", o.val)
return
}
if val == _undefined || val == _null {
o.prototype = nil
return
} else {
if val, ok := val.(*Object); ok {
o.prototype = val
}
}
return
}
var pprop Value
if proto := o.prototype; proto != nil {
pprop = proto.self.getPropStr(name)
}
if pprop != nil {
if prop, ok := pprop.(*valueProperty); ok {
if !prop.isWritable() {
o.val.runtime.typeErrorResult(throw)
return
}
if prop.accessor {
prop.set(o.val, val)
return
}
}
} else {
if !o.extensible {
o.val.runtime.typeErrorResult(throw)
return
}
}
o.values[name] = val
o.propNames = append(o.propNames, name)
}
func (o *baseObject) hasOwnProperty(n Value) bool {
v := o.values[n.String()]
return v != nil
}
func (o *baseObject) hasOwnPropertyStr(name string) bool {
v := o.values[name]
return v != nil
}
func (o *baseObject) _defineOwnProperty(name, existingValue Value, descr propertyDescr, throw bool) (val Value, ok bool) {
getterObj, _ := descr.Getter.(*Object)
setterObj, _ := descr.Setter.(*Object)
var existing *valueProperty
if existingValue == nil {
if !o.extensible {
o.val.runtime.typeErrorResult(throw)
return nil, false
}
existing = &valueProperty{}
} else {
if existing, ok = existingValue.(*valueProperty); !ok {
existing = &valueProperty{
writable: true,
enumerable: true,
configurable: true,
value: existingValue,
}
}
if !existing.configurable {
if descr.Configurable == FLAG_TRUE {
goto Reject
}
if descr.Enumerable != FLAG_NOT_SET && descr.Enumerable.Bool() != existing.enumerable {
goto Reject
}
}
if existing.accessor && descr.Value != nil || !existing.accessor && (getterObj != nil || setterObj != nil) {
if !existing.configurable {
goto Reject
}
} else if !existing.accessor {
if !existing.configurable {
if !existing.writable {
if descr.Writable == FLAG_TRUE {
goto Reject
}
if descr.Value != nil && !descr.Value.SameAs(existing.value) {
goto Reject
}
}
}
} else {
if !existing.configurable {
if descr.Getter != nil && existing.getterFunc != getterObj || descr.Setter != nil && existing.setterFunc != setterObj {
goto Reject
}
}
}
}
if descr.Writable == FLAG_TRUE && descr.Enumerable == FLAG_TRUE && descr.Configurable == FLAG_TRUE && descr.Value != nil {
return descr.Value, true
}
if descr.Writable != FLAG_NOT_SET {
existing.writable = descr.Writable.Bool()
}
if descr.Enumerable != FLAG_NOT_SET {
existing.enumerable = descr.Enumerable.Bool()
}
if descr.Configurable != FLAG_NOT_SET {
existing.configurable = descr.Configurable.Bool()
}
if descr.Value != nil {
existing.value = descr.Value
existing.getterFunc = nil
existing.setterFunc = nil
}
if descr.Value != nil || descr.Writable != FLAG_NOT_SET {
existing.accessor = false
}
if descr.Getter != nil {
existing.getterFunc = propGetter(o.val, descr.Getter, o.val.runtime)
existing.value = nil
existing.accessor = true
}
if descr.Setter != nil {
existing.setterFunc = propSetter(o.val, descr.Setter, o.val.runtime)
existing.value = nil
existing.accessor = true
}
if !existing.accessor && existing.value == nil {
existing.value = _undefined
}
return existing, true
Reject:
o.val.runtime.typeErrorResult(throw, "Cannot redefine property: %s", name.ToString())
return nil, false
}
func (o *baseObject) defineOwnProperty(n Value, descr propertyDescr, throw bool) bool {
name := n.String()
existingVal := o.values[name]
if v, ok := o._defineOwnProperty(n, existingVal, descr, throw); ok {
o.values[name] = v
if existingVal == nil {
o.propNames = append(o.propNames, name)
}
return true
}
return false
}
func (o *baseObject) _put(name string, v Value) {
if _, exists := o.values[name]; !exists {
o.propNames = append(o.propNames, name)
}
o.values[name] = v
}
func (o *baseObject) _putProp(name string, value Value, writable, enumerable, configurable bool) Value {
if writable && enumerable && configurable {
o._put(name, value)
return value
} else {
p := &valueProperty{
value: value,
writable: writable,
enumerable: enumerable,
configurable: configurable,
}
o._put(name, p)
return p
}
}
func (o *baseObject) tryPrimitive(methodName string) Value {
if method, ok := o.getStr(methodName).(*Object); ok {
if call, ok := method.self.assertCallable(); ok {
v := call(FunctionCall{
This: o.val,
})
if _, fail := v.(*Object); !fail {
return v
}
}
}
return nil
}
func (o *baseObject) toPrimitiveNumber() Value {
if v := o.tryPrimitive("valueOf"); v != nil {
return v
}
if v := o.tryPrimitive("toString"); v != nil {
return v
}
o.val.runtime.typeErrorResult(true, "Could not convert %v to primitive", o)
return nil
}
func (o *baseObject) toPrimitiveString() Value {
if v := o.tryPrimitive("toString"); v != nil {
return v
}
if v := o.tryPrimitive("valueOf"); v != nil {
return v
}
o.val.runtime.typeErrorResult(true, "Could not convert %v to primitive", o)
return nil
}
func (o *baseObject) toPrimitive() Value {
return o.toPrimitiveNumber()
}
func (o *baseObject) assertCallable() (func(FunctionCall) Value, bool) {
return nil, false
}
func (o *baseObject) proto() *Object {
return o.prototype
}
func (o *baseObject) isExtensible() bool {
return o.extensible
}
func (o *baseObject) preventExtensions() {
o.extensible = false
}
func (o *baseObject) sortLen() int64 {
return toLength(o.val.self.getStr("length"))
}
func (o *baseObject) sortGet(i int64) Value {
return o.val.self.get(intToValue(i))
}
func (o *baseObject) swap(i, j int64) {
ii := intToValue(i)
jj := intToValue(j)
x := o.val.self.get(ii)
y := o.val.self.get(jj)
o.val.self.put(ii, y, false)
o.val.self.put(jj, x, false)
}
func (o *baseObject) export() interface{} {
m := make(map[string]interface{})
for item, f := o.enumerate(false, false)(); f != nil; item, f = f() {
v := item.value
if v == nil {
v = o.getStr(item.name)
}
if v != nil {
m[item.name] = v.Export()
} else {
m[item.name] = nil
}
}
return m
}
func (o *baseObject) exportType() reflect.Type {
return reflectTypeMap
}
type enumerableFlag int
const (
_ENUM_UNKNOWN enumerableFlag = iota
_ENUM_FALSE
_ENUM_TRUE
)
type propIterItem struct {
name string
value Value // set only when enumerable == _ENUM_UNKNOWN
enumerable enumerableFlag
}
type objectPropIter struct {
o *baseObject
propNames []string
recursive bool
idx int
}
type propFilterIter struct {
wrapped iterNextFunc
all bool
seen map[string]bool
}
func (i *propFilterIter) next() (propIterItem, iterNextFunc) {
for {
var item propIterItem
item, i.wrapped = i.wrapped()
if i.wrapped == nil {
return propIterItem{}, nil
}
if !i.seen[item.name] {
i.seen[item.name] = true
if !i.all {
if item.enumerable == _ENUM_FALSE {
continue
}
if item.enumerable == _ENUM_UNKNOWN {
if prop, ok := item.value.(*valueProperty); ok {
if !prop.enumerable {
continue
}
}
}
}
return item, i.next
}
}
}
func (i *objectPropIter) next() (propIterItem, iterNextFunc) {
for i.idx < len(i.propNames) {
name := i.propNames[i.idx]
i.idx++
prop := i.o.values[name]
if prop != nil {
return propIterItem{name: name, value: prop}, i.next
}
}
if i.recursive && i.o.prototype != nil {
return i.o.prototype.self._enumerate(i.recursive)()
}
return propIterItem{}, nil
}
func (o *baseObject) _enumerate(recursive bool) iterNextFunc {
propNames := make([]string, len(o.propNames))
copy(propNames, o.propNames)
return (&objectPropIter{
o: o,
propNames: propNames,
recursive: recursive,
}).next
}
func (o *baseObject) enumerate(all, recursive bool) iterNextFunc {
return (&propFilterIter{
wrapped: o._enumerate(recursive),
all: all,
seen: make(map[string]bool),
}).next
}
func (o *baseObject) equal(other objectImpl) bool {
// Rely on parent reference comparison
return false
}
func (o *baseObject) hasInstance(v Value) bool {
o.val.runtime.typeErrorResult(true, "Expecting a function in instanceof check, but got %s", o.val.ToString())
panic("Unreachable")
}