Skip to content

Commit

Permalink
Implemented Map, Set, WeakMap, WeakSet (dop251#130)
Browse files Browse the repository at this point in the history
  • Loading branch information
dop251 authored Mar 3, 2020
1 parent 3672625 commit 0a7d410
Show file tree
Hide file tree
Showing 16 changed files with 1,630 additions and 33 deletions.
1 change: 1 addition & 0 deletions builtin_array.go
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,7 @@ func (r *Runtime) createArrayProto(val *Object) objectImpl {
valuesFunc := r.newNativeFunc(r.arrayproto_values, nil, "values", nil, 0)
o._putProp("values", valuesFunc, true, false, true)
o.put(symIterator, valueProp(valuesFunc, false, false, true), true)
r.global.arrayValues = valuesFunc

return o
}
Expand Down
14 changes: 2 additions & 12 deletions builtin_date.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func (r *Runtime) builtin_newDate(args []Value) *Object {
return r.newDateTime(args, time.Local)
}

func (r *Runtime) builtin_date(call FunctionCall) Value {
func (r *Runtime) builtin_date(FunctionCall) Value {
return asciiString(dateFormat(r.now()))
}

Expand All @@ -131,7 +131,7 @@ func (r *Runtime) date_UTC(call FunctionCall) Value {
return intToValue(timeToMsec(t))
}

func (r *Runtime) date_now(call FunctionCall) Value {
func (r *Runtime) date_now(FunctionCall) Value {
return intToValue(timeToMsec(r.now()))
}

Expand Down Expand Up @@ -910,16 +910,6 @@ func (r *Runtime) createDate(val *Object) objectImpl {
return o
}

func (r *Runtime) newLazyObject(create func(*Object) objectImpl) *Object {
val := &Object{runtime: r}
o := &lazyObject{
val: val,
create: create,
}
val.self = o
return val
}

func (r *Runtime) initDate() {
//r.global.DatePrototype = r.newObject()
//o := r.global.DatePrototype.self
Expand Down
267 changes: 267 additions & 0 deletions builtin_map.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
package goja

type mapObject struct {
baseObject
m *orderedMap
}

type mapIterObject struct {
baseObject
iter *orderedMapIter
kind iterationKind
}

func (o *mapIterObject) next() Value {
if o.iter == nil {
return o.val.runtime.createIterResultObject(_undefined, true)
}

entry := o.iter.next()
if entry == nil {
o.iter = nil
return o.val.runtime.createIterResultObject(_undefined, true)
}

var result Value
switch o.kind {
case iterationKindKey:
result = entry.key
case iterationKindValue:
result = entry.value
default:
result = o.val.runtime.newArrayValues([]Value{entry.key, entry.value})
}

return o.val.runtime.createIterResultObject(result, false)
}

func (mo *mapObject) init() {
mo.baseObject.init()
mo.m = newOrderedMap()
}

func (r *Runtime) mapProto_clear(call FunctionCall) Value {
thisObj := r.toObject(call.This)
mo, ok := thisObj.self.(*mapObject)
if !ok {
panic(r.NewTypeError("Method Map.prototype.clear called on incompatible receiver %s", thisObj.String()))
}

mo.m.clear()

return _undefined
}

func (r *Runtime) mapProto_delete(call FunctionCall) Value {
thisObj := r.toObject(call.This)
mo, ok := thisObj.self.(*mapObject)
if !ok {
panic(r.NewTypeError("Method Map.prototype.delete called on incompatible receiver %s", thisObj.String()))
}

return r.toBoolean(mo.m.remove(call.Argument(0)))
}

func (r *Runtime) mapProto_get(call FunctionCall) Value {
thisObj := r.toObject(call.This)
mo, ok := thisObj.self.(*mapObject)
if !ok {
panic(r.NewTypeError("Method Map.prototype.get called on incompatible receiver %s", thisObj.String()))
}

return nilSafe(mo.m.get(call.Argument(0)))
}

func (r *Runtime) mapProto_has(call FunctionCall) Value {
thisObj := r.toObject(call.This)
mo, ok := thisObj.self.(*mapObject)
if !ok {
panic(r.NewTypeError("Method Map.prototype.has called on incompatible receiver %s", thisObj.String()))
}
if mo.m.has(call.Argument(0)) {
return valueTrue
}
return valueFalse
}

func (r *Runtime) mapProto_set(call FunctionCall) Value {
thisObj := r.toObject(call.This)
mo, ok := thisObj.self.(*mapObject)
if !ok {
panic(r.NewTypeError("Method Map.prototype.set called on incompatible receiver %s", thisObj.String()))
}
mo.m.set(call.Argument(0), call.Argument(1))
return call.This
}

func (r *Runtime) mapProto_entries(call FunctionCall) Value {
return r.createMapIterator(call.This, iterationKindKeyValue)
}

func (r *Runtime) mapProto_forEach(call FunctionCall) Value {
thisObj := r.toObject(call.This)
mo, ok := thisObj.self.(*mapObject)
if !ok {
panic(r.NewTypeError("Method Map.prototype.forEach called on incompatible receiver %s", thisObj.String()))
}
callbackFn, ok := r.toObject(call.Argument(0)).self.assertCallable()
if !ok {
panic(r.NewTypeError("object is not a function %s"))
}
t := call.Argument(1)
iter := mo.m.newIter()
for {
entry := iter.next()
if entry == nil {
break
}
callbackFn(FunctionCall{This: t, Arguments: []Value{entry.value, entry.key, thisObj}})
}

return _undefined
}

func (r *Runtime) mapProto_keys(call FunctionCall) Value {
return r.createMapIterator(call.This, iterationKindKey)
}

func (r *Runtime) mapProto_values(call FunctionCall) Value {
return r.createMapIterator(call.This, iterationKindValue)
}

func (r *Runtime) mapProto_getSize(call FunctionCall) Value {
thisObj := r.toObject(call.This)
mo, ok := thisObj.self.(*mapObject)
if !ok {
panic(r.NewTypeError("Method get Map.prototype.size called on incompatible receiver %s", thisObj.String()))
}
return intToValue(int64(mo.m.size))
}

func (r *Runtime) builtin_newMap(args []Value) *Object {
o := &Object{runtime: r}

mo := &mapObject{}
mo.class = classMap
mo.val = o
mo.extensible = true
o.self = mo
mo.prototype = r.global.MapPrototype
mo.init()
if len(args) > 0 {
if arg := args[0]; arg != nil && arg != _undefined && arg != _null {
adder := mo.getStr("set")
iter := r.getIterator(arg.ToObject(r), nil)
i0 := intToValue(0)
i1 := intToValue(1)
if adder == r.global.mapAdder {
r.iterate(iter, func(item Value) {
itemObj := r.toObject(item)
k := itemObj.self.get(i0)
v := itemObj.self.get(i1)
mo.m.set(k, v)
})
} else {
adderFn := toMethod(adder)
if adderFn == nil {
panic(r.NewTypeError("Map.set in missing"))
}
r.iterate(iter, func(item Value) {
itemObj := r.toObject(item)
k := itemObj.self.get(i0)
v := itemObj.self.get(i1)
adderFn(FunctionCall{This: o, Arguments: []Value{k, v}})
})
}
}
}
return o
}

func (r *Runtime) createMapIterator(mapValue Value, kind iterationKind) Value {
obj := r.toObject(mapValue)
mapObj, ok := obj.self.(*mapObject)
if !ok {
panic(r.NewTypeError("Object is not a Map"))
}

o := &Object{runtime: r}

mi := &mapIterObject{
iter: mapObj.m.newIter(),
kind: kind,
}
mi.class = classMapIterator
mi.val = o
mi.extensible = true
o.self = mi
mi.prototype = r.global.MapIteratorPrototype
mi.init()

return o
}

func (r *Runtime) mapIterProto_next(call FunctionCall) Value {
thisObj := r.toObject(call.This)
if iter, ok := thisObj.self.(*mapIterObject); ok {
return iter.next()
}
panic(r.NewTypeError("Method Map Iterator.prototype.next called on incompatible receiver %s", thisObj.String()))
}

func (r *Runtime) createMapProto(val *Object) objectImpl {
o := newBaseObjectObj(val, r.global.ObjectPrototype, classObject)

o._putProp("constructor", r.global.Map, true, false, true)
o._putProp("clear", r.newNativeFunc(r.mapProto_clear, nil, "clear", nil, 0), true, false, true)
r.global.mapAdder = r.newNativeFunc(r.mapProto_set, nil, "set", nil, 2)
o._putProp("set", r.global.mapAdder, true, false, true)
o._putProp("delete", r.newNativeFunc(r.mapProto_delete, nil, "delete", nil, 1), true, false, true)
o._putProp("forEach", r.newNativeFunc(r.mapProto_forEach, nil, "forEach", nil, 1), true, false, true)
o._putProp("has", r.newNativeFunc(r.mapProto_has, nil, "has", nil, 1), true, false, true)
o._putProp("get", r.newNativeFunc(r.mapProto_get, nil, "get", nil, 1), true, false, true)
o.putStr("size", &valueProperty{
getterFunc: r.newNativeFunc(r.mapProto_getSize, nil, "get size", nil, 0),
accessor: true,
writable: true,
configurable: true,
}, true)
o._putProp("keys", r.newNativeFunc(r.mapProto_keys, nil, "keys", nil, 0), true, false, true)
o._putProp("values", r.newNativeFunc(r.mapProto_values, nil, "values", nil, 0), true, false, true)

entriesFunc := r.newNativeFunc(r.mapProto_entries, nil, "entries", nil, 0)
o._putProp("entries", entriesFunc, true, false, true)
o.put(symIterator, valueProp(entriesFunc, true, false, true), true)
o.put(symToStringTag, valueProp(asciiString(classMap), false, false, true), true)

return o
}

func (r *Runtime) createMap(val *Object) objectImpl {
o := r.newNativeFuncObj(val, r.constructorThrower("Map"), r.builtin_newMap, "Map", r.global.MapPrototype, 0)
o.putSym(symSpecies, &valueProperty{
getterFunc: r.newNativeFunc(r.returnThis, nil, "get [Symbol.species]", nil, 0),
accessor: true,
configurable: true,
}, true)

return o
}

func (r *Runtime) createMapIterProto(val *Object) objectImpl {
o := newBaseObjectObj(val, r.global.IteratorPrototype, classObject)

o._putProp("next", r.newNativeFunc(r.mapIterProto_next, nil, "next", nil, 0), true, false, true)
o.put(symToStringTag, valueProp(asciiString(classMapIterator), false, false, true), true)

return o
}

func (r *Runtime) initMap() {
r.global.MapIteratorPrototype = r.newLazyObject(r.createMapIterProto)

r.global.MapPrototype = r.newLazyObject(r.createMapProto)
r.global.Map = r.newLazyObject(r.createMap)

r.addToGlobal("Map", r.global.Map)
}
Loading

0 comments on commit 0a7d410

Please sign in to comment.