ai_old/goja/object_template.go
Star 767d87ac3e update throw exception
add util.load and util.save
2024-09-24 13:34:32 +08:00

470 lines
11 KiB
Go

package goja
import (
"apigo.cc/ai/ai/goja/unistring"
"fmt"
"math"
"reflect"
"sort"
)
type templatePropFactory func(*Runtime) Value
type objectTemplate struct {
propNames []unistring.String
props map[unistring.String]templatePropFactory
symProps map[*Symbol]templatePropFactory
symPropNames []*Symbol
protoFactory func(*Runtime) *Object
}
type templatedObject struct {
baseObject
tmpl *objectTemplate
protoMaterialised bool
}
type templatedFuncObject struct {
templatedObject
f func(FunctionCall) Value
construct func(args []Value, newTarget *Object) *Object
}
// This type exists because Array.prototype is supposed to be an array itself and I could not find
// a different way of implementing it without either introducing another layer of interfaces or hoisting
// the templates to baseObject both of which would have had a negative effect on the performance.
// The implementation is as simple as possible and is not optimised in any way, but I very much doubt anybody
// uses Array.prototype as an actual array.
type templatedArrayObject struct {
templatedObject
}
func newObjectTemplate() *objectTemplate {
return &objectTemplate{
props: make(map[unistring.String]templatePropFactory),
}
}
func (t *objectTemplate) putStr(name unistring.String, f templatePropFactory) {
t.props[name] = f
t.propNames = append(t.propNames, name)
}
func (t *objectTemplate) putSym(s *Symbol, f templatePropFactory) {
if t.symProps == nil {
t.symProps = make(map[*Symbol]templatePropFactory)
}
t.symProps[s] = f
t.symPropNames = append(t.symPropNames, s)
}
func (r *Runtime) newTemplatedObject(tmpl *objectTemplate, obj *Object) *templatedObject {
if obj == nil {
obj = &Object{runtime: r}
}
o := &templatedObject{
baseObject: baseObject{
class: classObject,
val: obj,
extensible: true,
},
tmpl: tmpl,
}
obj.self = o
o.init()
return o
}
func (o *templatedObject) materialiseProto() {
if !o.protoMaterialised {
if o.tmpl.protoFactory != nil {
o.prototype = o.tmpl.protoFactory(o.val.runtime)
}
o.protoMaterialised = true
}
}
func (o *templatedObject) getStr(name unistring.String, receiver Value) Value {
ownProp := o.getOwnPropStr(name)
if ownProp == nil {
o.materialiseProto()
}
return o.getStrWithOwnProp(ownProp, name, receiver)
}
func (o *templatedObject) getSym(s *Symbol, receiver Value) Value {
ownProp := o.getOwnPropSym(s)
if ownProp == nil {
o.materialiseProto()
}
return o.getWithOwnProp(ownProp, s, receiver)
}
func (o *templatedObject) getOwnPropStr(p unistring.String) Value {
if v, exists := o.values[p]; exists {
return v
}
if f := o.tmpl.props[p]; f != nil {
v := f(o.val.runtime)
o.values[p] = v
return v
}
return nil
}
func (o *templatedObject) materialiseSymbols() {
if o.symValues == nil {
o.symValues = newOrderedMap(nil)
for _, p := range o.tmpl.symPropNames {
o.symValues.set(p, o.tmpl.symProps[p](o.val.runtime))
}
}
}
func (o *templatedObject) getOwnPropSym(s *Symbol) Value {
if o.symValues == nil && o.tmpl.symProps[s] == nil {
return nil
}
o.materialiseSymbols()
return o.baseObject.getOwnPropSym(s)
}
func (o *templatedObject) materialisePropNames() {
if o.propNames == nil {
o.propNames = append(([]unistring.String)(nil), o.tmpl.propNames...)
}
}
func (o *templatedObject) setOwnStr(p unistring.String, v Value, throw bool) bool {
existing := o.getOwnPropStr(p) // materialise property (in case it's an accessor)
if existing == nil {
o.materialiseProto()
o.materialisePropNames()
}
return o.baseObject.setOwnStr(p, v, throw)
}
func (o *templatedObject) setOwnSym(name *Symbol, val Value, throw bool) bool {
o.materialiseSymbols()
o.materialiseProto()
return o.baseObject.setOwnSym(name, val, throw)
}
func (o *templatedObject) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) {
ownProp := o.getOwnPropStr(name)
if ownProp == nil {
o.materialiseProto()
}
return o._setForeignStr(name, ownProp, val, receiver, throw)
}
func (o *templatedObject) proto() *Object {
o.materialiseProto()
return o.prototype
}
func (o *templatedObject) setProto(proto *Object, throw bool) bool {
o.protoMaterialised = true
ret := o.baseObject.setProto(proto, throw)
if ret {
o.protoMaterialised = true
}
return ret
}
func (o *templatedObject) setForeignIdx(name valueInt, val, receiver Value, throw bool) (bool, bool) {
return o.setForeignStr(name.string(), val, receiver, throw)
}
func (o *templatedObject) setForeignSym(name *Symbol, val, receiver Value, throw bool) (bool, bool) {
o.materialiseProto()
o.materialiseSymbols()
return o.baseObject.setForeignSym(name, val, receiver, throw)
}
func (o *templatedObject) hasPropertyStr(name unistring.String) bool {
if o.val.self.hasOwnPropertyStr(name) {
return true
}
o.materialiseProto()
if o.prototype != nil {
return o.prototype.self.hasPropertyStr(name)
}
return false
}
func (o *templatedObject) hasPropertySym(s *Symbol) bool {
if o.hasOwnPropertySym(s) {
return true
}
o.materialiseProto()
if o.prototype != nil {
return o.prototype.self.hasPropertySym(s)
}
return false
}
func (o *templatedObject) hasOwnPropertyStr(name unistring.String) bool {
if v, exists := o.values[name]; exists {
return v != nil
}
_, exists := o.tmpl.props[name]
return exists
}
func (o *templatedObject) hasOwnPropertySym(s *Symbol) bool {
if o.symValues != nil {
return o.symValues.has(s)
}
_, exists := o.tmpl.symProps[s]
return exists
}
func (o *templatedObject) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool {
existingVal := o.getOwnPropStr(name)
if v, ok := o._defineOwnProperty(name, existingVal, descr, throw); ok {
o.values[name] = v
if existingVal == nil {
o.materialisePropNames()
names := copyNamesIfNeeded(o.propNames, 1)
o.propNames = append(names, name)
}
return true
}
return false
}
func (o *templatedObject) defineOwnPropertySym(s *Symbol, descr PropertyDescriptor, throw bool) bool {
o.materialiseSymbols()
return o.baseObject.defineOwnPropertySym(s, descr, throw)
}
func (o *templatedObject) deleteStr(name unistring.String, throw bool) bool {
if val := o.getOwnPropStr(name); val != nil {
if !o.checkDelete(name, val, throw) {
return false
}
o.materialisePropNames()
o._delete(name)
if _, exists := o.tmpl.props[name]; exists {
o.values[name] = nil // white hole
}
}
return true
}
func (o *templatedObject) deleteSym(s *Symbol, throw bool) bool {
o.materialiseSymbols()
return o.baseObject.deleteSym(s, throw)
}
func (o *templatedObject) materialiseProps() {
for name, f := range o.tmpl.props {
if _, exists := o.values[name]; !exists {
o.values[name] = f(o.val.runtime)
}
}
o.materialisePropNames()
}
func (o *templatedObject) iterateStringKeys() iterNextFunc {
o.materialiseProps()
return o.baseObject.iterateStringKeys()
}
func (o *templatedObject) iterateSymbols() iterNextFunc {
o.materialiseSymbols()
return o.baseObject.iterateSymbols()
}
func (o *templatedObject) stringKeys(all bool, keys []Value) []Value {
if all {
o.materialisePropNames()
} else {
o.materialiseProps()
}
return o.baseObject.stringKeys(all, keys)
}
func (o *templatedObject) symbols(all bool, accum []Value) []Value {
o.materialiseSymbols()
return o.baseObject.symbols(all, accum)
}
func (o *templatedObject) keys(all bool, accum []Value) []Value {
return o.symbols(all, o.stringKeys(all, accum))
}
func (r *Runtime) newTemplatedFuncObject(tmpl *objectTemplate, obj *Object, f func(FunctionCall) Value, ctor func([]Value, *Object) *Object) *templatedFuncObject {
if obj == nil {
obj = &Object{runtime: r}
}
o := &templatedFuncObject{
templatedObject: templatedObject{
baseObject: baseObject{
class: classFunction,
val: obj,
extensible: true,
},
tmpl: tmpl,
},
f: f,
construct: ctor,
}
obj.self = o
o.init()
return o
}
func (f *templatedFuncObject) source() String {
return newStringValue(fmt.Sprintf("function %s() { [native code] }", nilSafe(f.getStr("name", nil)).toString()))
}
func (f *templatedFuncObject) export(*objectExportCtx) interface{} {
return f.f
}
func (f *templatedFuncObject) assertCallable() (func(FunctionCall) Value, bool) {
if f.f != nil {
return f.f, true
}
return nil, false
}
func (f *templatedFuncObject) vmCall(vm *vm, n int) {
var nf nativeFuncObject
nf.f = f.f
nf.vmCall(vm, n)
}
func (f *templatedFuncObject) assertConstructor() func(args []Value, newTarget *Object) *Object {
return f.construct
}
func (f *templatedFuncObject) exportType() reflect.Type {
return reflectTypeFunc
}
func (f *templatedFuncObject) typeOf() String {
return stringFunction
}
func (f *templatedFuncObject) hasInstance(v Value) bool {
return hasInstance(f.val, v)
}
func (r *Runtime) newTemplatedArrayObject(tmpl *objectTemplate, obj *Object) *templatedArrayObject {
if obj == nil {
obj = &Object{runtime: r}
}
o := &templatedArrayObject{
templatedObject: templatedObject{
baseObject: baseObject{
class: classArray,
val: obj,
extensible: true,
},
tmpl: tmpl,
},
}
obj.self = o
o.init()
return o
}
func (a *templatedArrayObject) getLenProp() *valueProperty {
lenProp, _ := a.getOwnPropStr("length").(*valueProperty)
if lenProp == nil {
panic(a.val.runtime.NewTypeError("missing length property"))
}
return lenProp
}
func (a *templatedArrayObject) _setOwnIdx(idx uint32) {
lenProp := a.getLenProp()
l := uint32(lenProp.value.ToInteger())
if idx >= l {
lenProp.value = intToValue(int64(idx) + 1)
}
}
func (a *templatedArrayObject) setLength(l uint32, throw bool) bool {
lenProp := a.getLenProp()
oldLen := uint32(lenProp.value.ToInteger())
if l == oldLen {
return true
}
if !lenProp.writable {
a.val.runtime.typeErrorResult(throw, "length is not writable")
return false
}
ret := true
if l < oldLen {
a.materialisePropNames()
a.fixPropOrder()
i := sort.Search(a.idxPropCount, func(idx int) bool {
return strToArrayIdx(a.propNames[idx]) >= l
})
for j := a.idxPropCount - 1; j >= i; j-- {
if !a.deleteStr(a.propNames[j], false) {
l = strToArrayIdx(a.propNames[j]) + 1
ret = false
break
}
}
}
lenProp.value = intToValue(int64(l))
return ret
}
func (a *templatedArrayObject) setOwnStr(name unistring.String, value Value, throw bool) bool {
if name == "length" {
return a.setLength(a.val.runtime.toLengthUint32(value), throw)
}
if !a.templatedObject.setOwnStr(name, value, throw) {
return false
}
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
a._setOwnIdx(idx)
}
return true
}
func (a *templatedArrayObject) setOwnIdx(p valueInt, v Value, throw bool) bool {
if !a.templatedObject.setOwnStr(p.string(), v, throw) {
return false
}
if idx := toIdx(p); idx != math.MaxUint32 {
a._setOwnIdx(idx)
}
return true
}
func (a *templatedArrayObject) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool {
if name == "length" {
return a.val.runtime.defineArrayLength(a.getLenProp(), descr, a.setLength, throw)
}
if !a.templatedObject.defineOwnPropertyStr(name, descr, throw) {
return false
}
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
a._setOwnIdx(idx)
}
return true
}
func (a *templatedArrayObject) defineOwnPropertyIdx(p valueInt, desc PropertyDescriptor, throw bool) bool {
if !a.templatedObject.defineOwnPropertyStr(p.string(), desc, throw) {
return false
}
if idx := toIdx(p); idx != math.MaxUint32 {
a._setOwnIdx(idx)
}
return true
}