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 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 objectImpl, throw bool) bool toPrimitiveNumber() Value toPrimitiveString() Value toPrimitive() Value assertCallable() (call func(FunctionCall) Value, ok bool) // defineOwnProperty(Value, property, bool) 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 // clone(*_object, *_object, *_clone) *_object // marshalJSON() json.Marshaler } 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 } func (f FunctionCall) 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 Value, existingValue Value, descr objectImpl, throw bool) (val Value, ok bool) { var hasWritable, hasEnumerable, hasConfigurable bool var writable, enumerable, configurable bool value := descr.getStr("value") if p := descr.getStr("writable"); p != nil { hasWritable = true writable = p.ToBoolean() } if p := descr.getStr("enumerable"); p != nil { hasEnumerable = true enumerable = p.ToBoolean() } if p := descr.getStr("configurable"); p != nil { hasConfigurable = true configurable = p.ToBoolean() } getter := descr.getStr("get") setter := descr.getStr("set") if (getter != nil || setter != nil) && (value != nil || hasWritable) { o.val.runtime.typeErrorResult(throw, "Invalid property descriptor. Cannot both specify accessors and a value or writable attribute") return nil, false } getterObj, _ := getter.(*Object) setterObj, _ := 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 configurable { goto Reject } if hasEnumerable && enumerable != existing.enumerable { goto Reject } } if existing.accessor && value != nil || !existing.accessor && (getterObj != nil || setterObj != nil) { if !existing.configurable { goto Reject } } else if !existing.accessor { if !existing.configurable { if !existing.writable { if writable { goto Reject } if value != nil && !value.SameAs(existing.value) { goto Reject } } } } else { if !existing.configurable { if getter != nil && existing.getterFunc != getterObj || setter != nil && existing.setterFunc != setterObj { goto Reject } } } } if writable && enumerable && configurable && value != nil { return value, true } if hasWritable { existing.writable = writable } if hasEnumerable { existing.enumerable = enumerable } if hasConfigurable { existing.configurable = configurable } if value != nil { existing.value = value existing.getterFunc = nil existing.setterFunc = nil } if value != nil || hasWritable { existing.accessor = false } if getter != nil { existing.getterFunc = propGetter(o.val, getter, o.val.runtime) existing.value = nil existing.accessor = true } if setter != nil { existing.setterFunc = propSetter(o.val, 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 objectImpl, throw bool) bool { name := n.String() val := o.values[name] if v, ok := o._defineOwnProperty(n, val, descr, throw); ok { o.values[name] = v if val == 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(recusrive bool) iterNextFunc { propNames := make([]string, len(o.propNames)) copy(propNames, o.propNames) return (&objectPropIter{ o: o, propNames: propNames, recursive: recusrive, }).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") }