...

Source file src/github.com/dop251/goja/object_template.go

Documentation: github.com/dop251/goja

     1  package goja
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/dop251/goja/unistring"
     6  	"math"
     7  	"reflect"
     8  	"sort"
     9  )
    10  
    11  type templatePropFactory func(*Runtime) Value
    12  
    13  type objectTemplate struct {
    14  	propNames []unistring.String
    15  	props     map[unistring.String]templatePropFactory
    16  
    17  	symProps     map[*Symbol]templatePropFactory
    18  	symPropNames []*Symbol
    19  
    20  	protoFactory func(*Runtime) *Object
    21  }
    22  
    23  type templatedObject struct {
    24  	baseObject
    25  	tmpl *objectTemplate
    26  
    27  	protoMaterialised bool
    28  }
    29  
    30  type templatedFuncObject struct {
    31  	templatedObject
    32  
    33  	f         func(FunctionCall) Value
    34  	construct func(args []Value, newTarget *Object) *Object
    35  }
    36  
    37  // This type exists because Array.prototype is supposed to be an array itself and I could not find
    38  // a different way of implementing it without either introducing another layer of interfaces or hoisting
    39  // the templates to baseObject both of which would have had a negative effect on the performance.
    40  // The implementation is as simple as possible and is not optimised in any way, but I very much doubt anybody
    41  // uses Array.prototype as an actual array.
    42  type templatedArrayObject struct {
    43  	templatedObject
    44  }
    45  
    46  func newObjectTemplate() *objectTemplate {
    47  	return &objectTemplate{
    48  		props: make(map[unistring.String]templatePropFactory),
    49  	}
    50  }
    51  
    52  func (t *objectTemplate) putStr(name unistring.String, f templatePropFactory) {
    53  	t.props[name] = f
    54  	t.propNames = append(t.propNames, name)
    55  }
    56  
    57  func (t *objectTemplate) putSym(s *Symbol, f templatePropFactory) {
    58  	if t.symProps == nil {
    59  		t.symProps = make(map[*Symbol]templatePropFactory)
    60  	}
    61  	t.symProps[s] = f
    62  	t.symPropNames = append(t.symPropNames, s)
    63  }
    64  
    65  func (r *Runtime) newTemplatedObject(tmpl *objectTemplate, obj *Object) *templatedObject {
    66  	if obj == nil {
    67  		obj = &Object{runtime: r}
    68  	}
    69  	o := &templatedObject{
    70  		baseObject: baseObject{
    71  			class:      classObject,
    72  			val:        obj,
    73  			extensible: true,
    74  		},
    75  		tmpl: tmpl,
    76  	}
    77  	obj.self = o
    78  	o.init()
    79  	return o
    80  }
    81  
    82  func (o *templatedObject) materialiseProto() {
    83  	if !o.protoMaterialised {
    84  		if o.tmpl.protoFactory != nil {
    85  			o.prototype = o.tmpl.protoFactory(o.val.runtime)
    86  		}
    87  		o.protoMaterialised = true
    88  	}
    89  }
    90  
    91  func (o *templatedObject) getStr(name unistring.String, receiver Value) Value {
    92  	ownProp := o.getOwnPropStr(name)
    93  	if ownProp == nil {
    94  		o.materialiseProto()
    95  	}
    96  	return o.getStrWithOwnProp(ownProp, name, receiver)
    97  }
    98  
    99  func (o *templatedObject) getSym(s *Symbol, receiver Value) Value {
   100  	ownProp := o.getOwnPropSym(s)
   101  	if ownProp == nil {
   102  		o.materialiseProto()
   103  	}
   104  	return o.getWithOwnProp(ownProp, s, receiver)
   105  }
   106  
   107  func (o *templatedObject) getOwnPropStr(p unistring.String) Value {
   108  	if v, exists := o.values[p]; exists {
   109  		return v
   110  	}
   111  	if f := o.tmpl.props[p]; f != nil {
   112  		v := f(o.val.runtime)
   113  		o.values[p] = v
   114  		return v
   115  	}
   116  	return nil
   117  }
   118  
   119  func (o *templatedObject) materialiseSymbols() {
   120  	if o.symValues == nil {
   121  		o.symValues = newOrderedMap(nil)
   122  		for _, p := range o.tmpl.symPropNames {
   123  			o.symValues.set(p, o.tmpl.symProps[p](o.val.runtime))
   124  		}
   125  	}
   126  }
   127  
   128  func (o *templatedObject) getOwnPropSym(s *Symbol) Value {
   129  	if o.symValues == nil && o.tmpl.symProps[s] == nil {
   130  		return nil
   131  	}
   132  	o.materialiseSymbols()
   133  	return o.baseObject.getOwnPropSym(s)
   134  }
   135  
   136  func (o *templatedObject) materialisePropNames() {
   137  	if o.propNames == nil {
   138  		o.propNames = append(([]unistring.String)(nil), o.tmpl.propNames...)
   139  	}
   140  }
   141  
   142  func (o *templatedObject) setOwnStr(p unistring.String, v Value, throw bool) bool {
   143  	existing := o.getOwnPropStr(p) // materialise property (in case it's an accessor)
   144  	if existing == nil {
   145  		o.materialiseProto()
   146  		o.materialisePropNames()
   147  	}
   148  	return o.baseObject.setOwnStr(p, v, throw)
   149  }
   150  
   151  func (o *templatedObject) setOwnSym(name *Symbol, val Value, throw bool) bool {
   152  	o.materialiseSymbols()
   153  	o.materialiseProto()
   154  	return o.baseObject.setOwnSym(name, val, throw)
   155  }
   156  
   157  func (o *templatedObject) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) {
   158  	ownProp := o.getOwnPropStr(name)
   159  	if ownProp == nil {
   160  		o.materialiseProto()
   161  	}
   162  	return o._setForeignStr(name, ownProp, val, receiver, throw)
   163  }
   164  
   165  func (o *templatedObject) proto() *Object {
   166  	o.materialiseProto()
   167  	return o.prototype
   168  }
   169  
   170  func (o *templatedObject) setProto(proto *Object, throw bool) bool {
   171  	o.protoMaterialised = true
   172  	ret := o.baseObject.setProto(proto, throw)
   173  	if ret {
   174  		o.protoMaterialised = true
   175  	}
   176  	return ret
   177  }
   178  
   179  func (o *templatedObject) setForeignIdx(name valueInt, val, receiver Value, throw bool) (bool, bool) {
   180  	return o.setForeignStr(name.string(), val, receiver, throw)
   181  }
   182  
   183  func (o *templatedObject) setForeignSym(name *Symbol, val, receiver Value, throw bool) (bool, bool) {
   184  	o.materialiseProto()
   185  	o.materialiseSymbols()
   186  	return o.baseObject.setForeignSym(name, val, receiver, throw)
   187  }
   188  
   189  func (o *templatedObject) hasPropertyStr(name unistring.String) bool {
   190  	if o.val.self.hasOwnPropertyStr(name) {
   191  		return true
   192  	}
   193  	o.materialiseProto()
   194  	if o.prototype != nil {
   195  		return o.prototype.self.hasPropertyStr(name)
   196  	}
   197  	return false
   198  }
   199  
   200  func (o *templatedObject) hasPropertySym(s *Symbol) bool {
   201  	if o.hasOwnPropertySym(s) {
   202  		return true
   203  	}
   204  	o.materialiseProto()
   205  	if o.prototype != nil {
   206  		return o.prototype.self.hasPropertySym(s)
   207  	}
   208  	return false
   209  }
   210  
   211  func (o *templatedObject) hasOwnPropertyStr(name unistring.String) bool {
   212  	if v, exists := o.values[name]; exists {
   213  		return v != nil
   214  	}
   215  
   216  	_, exists := o.tmpl.props[name]
   217  	return exists
   218  }
   219  
   220  func (o *templatedObject) hasOwnPropertySym(s *Symbol) bool {
   221  	if o.symValues != nil {
   222  		return o.symValues.has(s)
   223  	}
   224  	_, exists := o.tmpl.symProps[s]
   225  	return exists
   226  }
   227  
   228  func (o *templatedObject) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool {
   229  	existingVal := o.getOwnPropStr(name)
   230  	if v, ok := o._defineOwnProperty(name, existingVal, descr, throw); ok {
   231  		o.values[name] = v
   232  		if existingVal == nil {
   233  			o.materialisePropNames()
   234  			names := copyNamesIfNeeded(o.propNames, 1)
   235  			o.propNames = append(names, name)
   236  		}
   237  		return true
   238  	}
   239  	return false
   240  }
   241  
   242  func (o *templatedObject) defineOwnPropertySym(s *Symbol, descr PropertyDescriptor, throw bool) bool {
   243  	o.materialiseSymbols()
   244  	return o.baseObject.defineOwnPropertySym(s, descr, throw)
   245  }
   246  
   247  func (o *templatedObject) deleteStr(name unistring.String, throw bool) bool {
   248  	if val := o.getOwnPropStr(name); val != nil {
   249  		if !o.checkDelete(name, val, throw) {
   250  			return false
   251  		}
   252  		o.materialisePropNames()
   253  		o._delete(name)
   254  		if _, exists := o.tmpl.props[name]; exists {
   255  			o.values[name] = nil // white hole
   256  		}
   257  	}
   258  	return true
   259  }
   260  
   261  func (o *templatedObject) deleteSym(s *Symbol, throw bool) bool {
   262  	o.materialiseSymbols()
   263  	return o.baseObject.deleteSym(s, throw)
   264  }
   265  
   266  func (o *templatedObject) materialiseProps() {
   267  	for name, f := range o.tmpl.props {
   268  		if _, exists := o.values[name]; !exists {
   269  			o.values[name] = f(o.val.runtime)
   270  		}
   271  	}
   272  	o.materialisePropNames()
   273  }
   274  
   275  func (o *templatedObject) iterateStringKeys() iterNextFunc {
   276  	o.materialiseProps()
   277  	return o.baseObject.iterateStringKeys()
   278  }
   279  
   280  func (o *templatedObject) iterateSymbols() iterNextFunc {
   281  	o.materialiseSymbols()
   282  	return o.baseObject.iterateSymbols()
   283  }
   284  
   285  func (o *templatedObject) stringKeys(all bool, keys []Value) []Value {
   286  	if all {
   287  		o.materialisePropNames()
   288  	} else {
   289  		o.materialiseProps()
   290  	}
   291  	return o.baseObject.stringKeys(all, keys)
   292  }
   293  
   294  func (o *templatedObject) symbols(all bool, accum []Value) []Value {
   295  	o.materialiseSymbols()
   296  	return o.baseObject.symbols(all, accum)
   297  }
   298  
   299  func (o *templatedObject) keys(all bool, accum []Value) []Value {
   300  	return o.symbols(all, o.stringKeys(all, accum))
   301  }
   302  
   303  func (r *Runtime) newTemplatedFuncObject(tmpl *objectTemplate, obj *Object, f func(FunctionCall) Value, ctor func([]Value, *Object) *Object) *templatedFuncObject {
   304  	if obj == nil {
   305  		obj = &Object{runtime: r}
   306  	}
   307  	o := &templatedFuncObject{
   308  		templatedObject: templatedObject{
   309  			baseObject: baseObject{
   310  				class:      classFunction,
   311  				val:        obj,
   312  				extensible: true,
   313  			},
   314  			tmpl: tmpl,
   315  		},
   316  		f:         f,
   317  		construct: ctor,
   318  	}
   319  	obj.self = o
   320  	o.init()
   321  	return o
   322  }
   323  
   324  func (f *templatedFuncObject) source() String {
   325  	return newStringValue(fmt.Sprintf("function %s() { [native code] }", nilSafe(f.getStr("name", nil)).toString()))
   326  }
   327  
   328  func (f *templatedFuncObject) export(*objectExportCtx) interface{} {
   329  	return f.f
   330  }
   331  
   332  func (f *templatedFuncObject) assertCallable() (func(FunctionCall) Value, bool) {
   333  	if f.f != nil {
   334  		return f.f, true
   335  	}
   336  	return nil, false
   337  }
   338  
   339  func (f *templatedFuncObject) vmCall(vm *vm, n int) {
   340  	var nf nativeFuncObject
   341  	nf.f = f.f
   342  	nf.vmCall(vm, n)
   343  }
   344  
   345  func (f *templatedFuncObject) assertConstructor() func(args []Value, newTarget *Object) *Object {
   346  	return f.construct
   347  }
   348  
   349  func (f *templatedFuncObject) exportType() reflect.Type {
   350  	return reflectTypeFunc
   351  }
   352  
   353  func (f *templatedFuncObject) typeOf() String {
   354  	return stringFunction
   355  }
   356  
   357  func (f *templatedFuncObject) hasInstance(v Value) bool {
   358  	return hasInstance(f.val, v)
   359  }
   360  
   361  func (r *Runtime) newTemplatedArrayObject(tmpl *objectTemplate, obj *Object) *templatedArrayObject {
   362  	if obj == nil {
   363  		obj = &Object{runtime: r}
   364  	}
   365  	o := &templatedArrayObject{
   366  		templatedObject: templatedObject{
   367  			baseObject: baseObject{
   368  				class:      classArray,
   369  				val:        obj,
   370  				extensible: true,
   371  			},
   372  			tmpl: tmpl,
   373  		},
   374  	}
   375  	obj.self = o
   376  	o.init()
   377  	return o
   378  }
   379  
   380  func (a *templatedArrayObject) getLenProp() *valueProperty {
   381  	lenProp, _ := a.getOwnPropStr("length").(*valueProperty)
   382  	if lenProp == nil {
   383  		panic(a.val.runtime.NewTypeError("missing length property"))
   384  	}
   385  	return lenProp
   386  }
   387  
   388  func (a *templatedArrayObject) _setOwnIdx(idx uint32) {
   389  	lenProp := a.getLenProp()
   390  	l := uint32(lenProp.value.ToInteger())
   391  	if idx >= l {
   392  		lenProp.value = intToValue(int64(idx) + 1)
   393  	}
   394  }
   395  
   396  func (a *templatedArrayObject) setLength(l uint32, throw bool) bool {
   397  	lenProp := a.getLenProp()
   398  	oldLen := uint32(lenProp.value.ToInteger())
   399  	if l == oldLen {
   400  		return true
   401  	}
   402  	if !lenProp.writable {
   403  		a.val.runtime.typeErrorResult(throw, "length is not writable")
   404  		return false
   405  	}
   406  	ret := true
   407  	if l < oldLen {
   408  		a.materialisePropNames()
   409  		a.fixPropOrder()
   410  		i := sort.Search(a.idxPropCount, func(idx int) bool {
   411  			return strToArrayIdx(a.propNames[idx]) >= l
   412  		})
   413  		for j := a.idxPropCount - 1; j >= i; j-- {
   414  			if !a.deleteStr(a.propNames[j], false) {
   415  				l = strToArrayIdx(a.propNames[j]) + 1
   416  				ret = false
   417  				break
   418  			}
   419  		}
   420  	}
   421  	lenProp.value = intToValue(int64(l))
   422  	return ret
   423  }
   424  
   425  func (a *templatedArrayObject) setOwnStr(name unistring.String, value Value, throw bool) bool {
   426  	if name == "length" {
   427  		return a.setLength(a.val.runtime.toLengthUint32(value), throw)
   428  	}
   429  	if !a.templatedObject.setOwnStr(name, value, throw) {
   430  		return false
   431  	}
   432  	if idx := strToArrayIdx(name); idx != math.MaxUint32 {
   433  		a._setOwnIdx(idx)
   434  	}
   435  	return true
   436  }
   437  
   438  func (a *templatedArrayObject) setOwnIdx(p valueInt, v Value, throw bool) bool {
   439  	if !a.templatedObject.setOwnStr(p.string(), v, throw) {
   440  		return false
   441  	}
   442  	if idx := toIdx(p); idx != math.MaxUint32 {
   443  		a._setOwnIdx(idx)
   444  	}
   445  	return true
   446  }
   447  
   448  func (a *templatedArrayObject) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool {
   449  	if name == "length" {
   450  		return a.val.runtime.defineArrayLength(a.getLenProp(), descr, a.setLength, throw)
   451  	}
   452  	if !a.templatedObject.defineOwnPropertyStr(name, descr, throw) {
   453  		return false
   454  	}
   455  	if idx := strToArrayIdx(name); idx != math.MaxUint32 {
   456  		a._setOwnIdx(idx)
   457  	}
   458  	return true
   459  }
   460  
   461  func (a *templatedArrayObject) defineOwnPropertyIdx(p valueInt, desc PropertyDescriptor, throw bool) bool {
   462  	if !a.templatedObject.defineOwnPropertyStr(p.string(), desc, throw) {
   463  		return false
   464  	}
   465  	if idx := toIdx(p); idx != math.MaxUint32 {
   466  		a._setOwnIdx(idx)
   467  	}
   468  	return true
   469  }
   470  

View as plain text