github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/builtin_map.go (about)

     1  package goja
     2  
     3  import (
     4  	"reflect"
     5  )
     6  
     7  var mapExportType = reflect.TypeOf([][2]interface{}{})
     8  
     9  type mapObject struct {
    10  	baseObject
    11  	m *orderedMap
    12  }
    13  
    14  type mapIterObject struct {
    15  	baseObject
    16  	iter *orderedMapIter
    17  	kind iterationKind
    18  }
    19  
    20  func (o *mapIterObject) next() Value {
    21  	if o.iter == nil {
    22  		return o.val.runtime.createIterResultObject(_undefined, true)
    23  	}
    24  
    25  	entry := o.iter.next()
    26  	if entry == nil {
    27  		o.iter = nil
    28  		return o.val.runtime.createIterResultObject(_undefined, true)
    29  	}
    30  
    31  	var result Value
    32  	switch o.kind {
    33  	case iterationKindKey:
    34  		result = entry.key
    35  	case iterationKindValue:
    36  		result = entry.value
    37  	default:
    38  		result = o.val.runtime.newArrayValues([]Value{entry.key, entry.value})
    39  	}
    40  
    41  	return o.val.runtime.createIterResultObject(result, false)
    42  }
    43  
    44  func (mo *mapObject) init() {
    45  	mo.baseObject.init()
    46  	mo.m = newOrderedMap(mo.val.runtime.getHash())
    47  }
    48  
    49  func (mo *mapObject) exportType() reflect.Type {
    50  	return mapExportType
    51  }
    52  
    53  func (mo *mapObject) export(ctx *objectExportCtx) interface{} {
    54  	m := make([][2]interface{}, mo.m.size)
    55  	ctx.put(mo.val, m)
    56  
    57  	iter := mo.m.newIter()
    58  	for i := 0; i < len(m); i++ {
    59  		entry := iter.next()
    60  		if entry == nil {
    61  			break
    62  		}
    63  		m[i][0] = exportValue(entry.key, ctx)
    64  		m[i][1] = exportValue(entry.value, ctx)
    65  	}
    66  
    67  	return m
    68  }
    69  
    70  func (mo *mapObject) exportToMap(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error {
    71  	dst.Set(reflect.MakeMap(typ))
    72  	ctx.putTyped(mo.val, typ, dst.Interface())
    73  	keyTyp := typ.Key()
    74  	elemTyp := typ.Elem()
    75  	iter := mo.m.newIter()
    76  	r := mo.val.runtime
    77  	for {
    78  		entry := iter.next()
    79  		if entry == nil {
    80  			break
    81  		}
    82  		keyVal := reflect.New(keyTyp).Elem()
    83  		err := r.toReflectValue(entry.key, keyVal, ctx)
    84  		if err != nil {
    85  			return err
    86  		}
    87  		elemVal := reflect.New(elemTyp).Elem()
    88  		err = r.toReflectValue(entry.value, elemVal, ctx)
    89  		if err != nil {
    90  			return err
    91  		}
    92  		dst.SetMapIndex(keyVal, elemVal)
    93  	}
    94  	return nil
    95  }
    96  
    97  func (r *Runtime) mapProto_clear(call FunctionCall) Value {
    98  	thisObj := r.toObject(call.This)
    99  	mo, ok := thisObj.self.(*mapObject)
   100  	if !ok {
   101  		panic(r.NewTypeError("Method Map.prototype.clear called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
   102  	}
   103  
   104  	mo.m.clear()
   105  
   106  	return _undefined
   107  }
   108  
   109  func (r *Runtime) mapProto_delete(call FunctionCall) Value {
   110  	thisObj := r.toObject(call.This)
   111  	mo, ok := thisObj.self.(*mapObject)
   112  	if !ok {
   113  		panic(r.NewTypeError("Method Map.prototype.delete called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
   114  	}
   115  
   116  	return r.toBoolean(mo.m.remove(call.Argument(0)))
   117  }
   118  
   119  func (r *Runtime) mapProto_get(call FunctionCall) Value {
   120  	thisObj := r.toObject(call.This)
   121  	mo, ok := thisObj.self.(*mapObject)
   122  	if !ok {
   123  		panic(r.NewTypeError("Method Map.prototype.get called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
   124  	}
   125  
   126  	return nilSafe(mo.m.get(call.Argument(0)))
   127  }
   128  
   129  func (r *Runtime) mapProto_has(call FunctionCall) Value {
   130  	thisObj := r.toObject(call.This)
   131  	mo, ok := thisObj.self.(*mapObject)
   132  	if !ok {
   133  		panic(r.NewTypeError("Method Map.prototype.has called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
   134  	}
   135  	if mo.m.has(call.Argument(0)) {
   136  		return valueTrue
   137  	}
   138  	return valueFalse
   139  }
   140  
   141  func (r *Runtime) mapProto_set(call FunctionCall) Value {
   142  	thisObj := r.toObject(call.This)
   143  	mo, ok := thisObj.self.(*mapObject)
   144  	if !ok {
   145  		panic(r.NewTypeError("Method Map.prototype.set called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
   146  	}
   147  	mo.m.set(call.Argument(0), call.Argument(1))
   148  	return call.This
   149  }
   150  
   151  func (r *Runtime) mapProto_entries(call FunctionCall) Value {
   152  	return r.createMapIterator(call.This, iterationKindKeyValue)
   153  }
   154  
   155  func (r *Runtime) mapProto_forEach(call FunctionCall) Value {
   156  	thisObj := r.toObject(call.This)
   157  	mo, ok := thisObj.self.(*mapObject)
   158  	if !ok {
   159  		panic(r.NewTypeError("Method Map.prototype.forEach called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
   160  	}
   161  	callbackFn, ok := r.toObject(call.Argument(0)).self.assertCallable()
   162  	if !ok {
   163  		panic(r.NewTypeError("object is not a function %s"))
   164  	}
   165  	t := call.Argument(1)
   166  	iter := mo.m.newIter()
   167  	for {
   168  		entry := iter.next()
   169  		if entry == nil {
   170  			break
   171  		}
   172  		callbackFn(FunctionCall{This: t, Arguments: []Value{entry.value, entry.key, thisObj}})
   173  	}
   174  
   175  	return _undefined
   176  }
   177  
   178  func (r *Runtime) mapProto_keys(call FunctionCall) Value {
   179  	return r.createMapIterator(call.This, iterationKindKey)
   180  }
   181  
   182  func (r *Runtime) mapProto_values(call FunctionCall) Value {
   183  	return r.createMapIterator(call.This, iterationKindValue)
   184  }
   185  
   186  func (r *Runtime) mapProto_getSize(call FunctionCall) Value {
   187  	thisObj := r.toObject(call.This)
   188  	mo, ok := thisObj.self.(*mapObject)
   189  	if !ok {
   190  		panic(r.NewTypeError("Method get Map.prototype.size called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
   191  	}
   192  	return intToValue(int64(mo.m.size))
   193  }
   194  
   195  func (r *Runtime) builtin_newMap(args []Value, newTarget *Object) *Object {
   196  	if newTarget == nil {
   197  		panic(r.needNew("Map"))
   198  	}
   199  	proto := r.getPrototypeFromCtor(newTarget, r.global.Map, r.global.MapPrototype)
   200  	o := &Object{runtime: r}
   201  
   202  	mo := &mapObject{}
   203  	mo.class = classObject
   204  	mo.val = o
   205  	mo.extensible = true
   206  	o.self = mo
   207  	mo.prototype = proto
   208  	mo.init()
   209  	if len(args) > 0 {
   210  		if arg := args[0]; arg != nil && arg != _undefined && arg != _null {
   211  			adder := mo.getStr("set", nil)
   212  			adderFn := toMethod(adder)
   213  			if adderFn == nil {
   214  				panic(r.NewTypeError("Map.set in missing"))
   215  			}
   216  			iter := r.getIterator(arg, nil)
   217  			i0 := valueInt(0)
   218  			i1 := valueInt(1)
   219  			if adder == r.global.mapAdder {
   220  				iter.iterate(func(item Value) {
   221  					itemObj := r.toObject(item)
   222  					k := nilSafe(itemObj.self.getIdx(i0, nil))
   223  					v := nilSafe(itemObj.self.getIdx(i1, nil))
   224  					mo.m.set(k, v)
   225  				})
   226  			} else {
   227  				iter.iterate(func(item Value) {
   228  					itemObj := r.toObject(item)
   229  					k := itemObj.self.getIdx(i0, nil)
   230  					v := itemObj.self.getIdx(i1, nil)
   231  					adderFn(FunctionCall{This: o, Arguments: []Value{k, v}})
   232  				})
   233  			}
   234  		}
   235  	}
   236  	return o
   237  }
   238  
   239  func (r *Runtime) createMapIterator(mapValue Value, kind iterationKind) Value {
   240  	obj := r.toObject(mapValue)
   241  	mapObj, ok := obj.self.(*mapObject)
   242  	if !ok {
   243  		panic(r.NewTypeError("Object is not a Map"))
   244  	}
   245  
   246  	o := &Object{runtime: r}
   247  
   248  	mi := &mapIterObject{
   249  		iter: mapObj.m.newIter(),
   250  		kind: kind,
   251  	}
   252  	mi.class = classObject
   253  	mi.val = o
   254  	mi.extensible = true
   255  	o.self = mi
   256  	mi.prototype = r.getMapIteratorPrototype()
   257  	mi.init()
   258  
   259  	return o
   260  }
   261  
   262  func (r *Runtime) mapIterProto_next(call FunctionCall) Value {
   263  	thisObj := r.toObject(call.This)
   264  	if iter, ok := thisObj.self.(*mapIterObject); ok {
   265  		return iter.next()
   266  	}
   267  	panic(r.NewTypeError("Method Map Iterator.prototype.next called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
   268  }
   269  
   270  func (r *Runtime) createMapProto(val *Object) objectImpl {
   271  	o := newBaseObjectObj(val, r.global.ObjectPrototype, classObject)
   272  
   273  	o._putProp("constructor", r.global.Map, true, false, true)
   274  	o._putProp("clear", r.newNativeFunc(r.mapProto_clear, nil, "clear", nil, 0), true, false, true)
   275  	r.global.mapAdder = r.newNativeFunc(r.mapProto_set, nil, "set", nil, 2)
   276  	o._putProp("set", r.global.mapAdder, true, false, true)
   277  	o._putProp("delete", r.newNativeFunc(r.mapProto_delete, nil, "delete", nil, 1), true, false, true)
   278  	o._putProp("forEach", r.newNativeFunc(r.mapProto_forEach, nil, "forEach", nil, 1), true, false, true)
   279  	o._putProp("has", r.newNativeFunc(r.mapProto_has, nil, "has", nil, 1), true, false, true)
   280  	o._putProp("get", r.newNativeFunc(r.mapProto_get, nil, "get", nil, 1), true, false, true)
   281  	o.setOwnStr("size", &valueProperty{
   282  		getterFunc:   r.newNativeFunc(r.mapProto_getSize, nil, "get size", nil, 0),
   283  		accessor:     true,
   284  		writable:     true,
   285  		configurable: true,
   286  	}, true)
   287  	o._putProp("keys", r.newNativeFunc(r.mapProto_keys, nil, "keys", nil, 0), true, false, true)
   288  	o._putProp("values", r.newNativeFunc(r.mapProto_values, nil, "values", nil, 0), true, false, true)
   289  
   290  	entriesFunc := r.newNativeFunc(r.mapProto_entries, nil, "entries", nil, 0)
   291  	o._putProp("entries", entriesFunc, true, false, true)
   292  	o._putSym(SymIterator, valueProp(entriesFunc, true, false, true))
   293  	o._putSym(SymToStringTag, valueProp(asciiString(classMap), false, false, true))
   294  
   295  	return o
   296  }
   297  
   298  func (r *Runtime) createMap(val *Object) objectImpl {
   299  	o := r.newNativeConstructOnly(val, r.builtin_newMap, r.global.MapPrototype, "Map", 0)
   300  	r.putSpeciesReturnThis(o)
   301  
   302  	return o
   303  }
   304  
   305  func (r *Runtime) createMapIterProto(val *Object) objectImpl {
   306  	o := newBaseObjectObj(val, r.getIteratorPrototype(), classObject)
   307  
   308  	o._putProp("next", r.newNativeFunc(r.mapIterProto_next, nil, "next", nil, 0), true, false, true)
   309  	o._putSym(SymToStringTag, valueProp(asciiString(classMapIterator), false, false, true))
   310  
   311  	return o
   312  }
   313  
   314  func (r *Runtime) getMapIteratorPrototype() *Object {
   315  	var o *Object
   316  	if o = r.global.MapIteratorPrototype; o == nil {
   317  		o = &Object{runtime: r}
   318  		r.global.MapIteratorPrototype = o
   319  		o.self = r.createMapIterProto(o)
   320  	}
   321  	return o
   322  }
   323  
   324  func (r *Runtime) initMap() {
   325  	r.global.MapIteratorPrototype = r.newLazyObject(r.createMapIterProto)
   326  
   327  	r.global.MapPrototype = r.newLazyObject(r.createMapProto)
   328  	r.global.Map = r.newLazyObject(r.createMap)
   329  
   330  	r.addToGlobal("Map", r.global.Map)
   331  }