347 lines
8.9 KiB
Go
347 lines
8.9 KiB
Go
package goja
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
)
|
|
|
|
var setExportType = reflectTypeArray
|
|
|
|
type setObject struct {
|
|
baseObject
|
|
m *orderedMap
|
|
}
|
|
|
|
type setIterObject struct {
|
|
baseObject
|
|
iter *orderedMapIter
|
|
kind iterationKind
|
|
}
|
|
|
|
func (o *setIterObject) 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 iterationKindValue:
|
|
result = entry.key
|
|
default:
|
|
result = o.val.runtime.newArrayValues([]Value{entry.key, entry.key})
|
|
}
|
|
|
|
return o.val.runtime.createIterResultObject(result, false)
|
|
}
|
|
|
|
func (so *setObject) init() {
|
|
so.baseObject.init()
|
|
so.m = newOrderedMap(so.val.runtime.getHash())
|
|
}
|
|
|
|
func (so *setObject) exportType() reflect.Type {
|
|
return setExportType
|
|
}
|
|
|
|
func (so *setObject) export(ctx *objectExportCtx) interface{} {
|
|
a := make([]interface{}, so.m.size)
|
|
ctx.put(so.val, a)
|
|
iter := so.m.newIter()
|
|
for i := 0; i < len(a); i++ {
|
|
entry := iter.next()
|
|
if entry == nil {
|
|
break
|
|
}
|
|
a[i] = exportValue(entry.key, ctx)
|
|
}
|
|
return a
|
|
}
|
|
|
|
func (so *setObject) exportToArrayOrSlice(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error {
|
|
l := so.m.size
|
|
if typ.Kind() == reflect.Array {
|
|
if dst.Len() != l {
|
|
return fmt.Errorf("cannot convert a Set into an array, lengths mismatch: have %d, need %d)", l, dst.Len())
|
|
}
|
|
} else {
|
|
dst.Set(reflect.MakeSlice(typ, l, l))
|
|
}
|
|
ctx.putTyped(so.val, typ, dst.Interface())
|
|
iter := so.m.newIter()
|
|
r := so.val.runtime
|
|
for i := 0; i < l; i++ {
|
|
entry := iter.next()
|
|
if entry == nil {
|
|
break
|
|
}
|
|
err := r.toReflectValue(entry.key, dst.Index(i), ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (so *setObject) exportToMap(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error {
|
|
dst.Set(reflect.MakeMap(typ))
|
|
keyTyp := typ.Key()
|
|
elemTyp := typ.Elem()
|
|
iter := so.m.newIter()
|
|
r := so.val.runtime
|
|
for {
|
|
entry := iter.next()
|
|
if entry == nil {
|
|
break
|
|
}
|
|
keyVal := reflect.New(keyTyp).Elem()
|
|
err := r.toReflectValue(entry.key, keyVal, ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
dst.SetMapIndex(keyVal, reflect.Zero(elemTyp))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *Runtime) setProto_add(call FunctionCall) Value {
|
|
thisObj := r.toObject(call.This)
|
|
so, ok := thisObj.self.(*setObject)
|
|
if !ok {
|
|
panic(r.NewTypeError("Method Set.prototype.add called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
|
|
}
|
|
|
|
so.m.set(call.Argument(0), nil)
|
|
return call.This
|
|
}
|
|
|
|
func (r *Runtime) setProto_clear(call FunctionCall) Value {
|
|
thisObj := r.toObject(call.This)
|
|
so, ok := thisObj.self.(*setObject)
|
|
if !ok {
|
|
panic(r.NewTypeError("Method Set.prototype.clear called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
|
|
}
|
|
|
|
so.m.clear()
|
|
return _undefined
|
|
}
|
|
|
|
func (r *Runtime) setProto_delete(call FunctionCall) Value {
|
|
thisObj := r.toObject(call.This)
|
|
so, ok := thisObj.self.(*setObject)
|
|
if !ok {
|
|
panic(r.NewTypeError("Method Set.prototype.delete called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
|
|
}
|
|
|
|
return r.toBoolean(so.m.remove(call.Argument(0)))
|
|
}
|
|
|
|
func (r *Runtime) setProto_entries(call FunctionCall) Value {
|
|
return r.createSetIterator(call.This, iterationKindKeyValue)
|
|
}
|
|
|
|
func (r *Runtime) setProto_forEach(call FunctionCall) Value {
|
|
thisObj := r.toObject(call.This)
|
|
so, ok := thisObj.self.(*setObject)
|
|
if !ok {
|
|
panic(r.NewTypeError("Method Set.prototype.forEach called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
|
|
}
|
|
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 := so.m.newIter()
|
|
for {
|
|
entry := iter.next()
|
|
if entry == nil {
|
|
break
|
|
}
|
|
callbackFn(FunctionCall{This: t, Arguments: []Value{entry.key, entry.key, thisObj}})
|
|
}
|
|
|
|
return _undefined
|
|
}
|
|
|
|
func (r *Runtime) setProto_has(call FunctionCall) Value {
|
|
thisObj := r.toObject(call.This)
|
|
so, ok := thisObj.self.(*setObject)
|
|
if !ok {
|
|
panic(r.NewTypeError("Method Set.prototype.has called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
|
|
}
|
|
|
|
return r.toBoolean(so.m.has(call.Argument(0)))
|
|
}
|
|
|
|
func (r *Runtime) setProto_getSize(call FunctionCall) Value {
|
|
thisObj := r.toObject(call.This)
|
|
so, ok := thisObj.self.(*setObject)
|
|
if !ok {
|
|
panic(r.NewTypeError("Method get Set.prototype.size called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
|
|
}
|
|
|
|
return intToValue(int64(so.m.size))
|
|
}
|
|
|
|
func (r *Runtime) setProto_values(call FunctionCall) Value {
|
|
return r.createSetIterator(call.This, iterationKindValue)
|
|
}
|
|
|
|
func (r *Runtime) builtin_newSet(args []Value, newTarget *Object) *Object {
|
|
if newTarget == nil {
|
|
panic(r.needNew("Set"))
|
|
}
|
|
proto := r.getPrototypeFromCtor(newTarget, r.global.Set, r.global.SetPrototype)
|
|
o := &Object{runtime: r}
|
|
|
|
so := &setObject{}
|
|
so.class = classObject
|
|
so.val = o
|
|
so.extensible = true
|
|
o.self = so
|
|
so.prototype = proto
|
|
so.init()
|
|
if len(args) > 0 {
|
|
if arg := args[0]; arg != nil && arg != _undefined && arg != _null {
|
|
adder := so.getStr("add", nil)
|
|
stdArr := r.checkStdArrayIter(arg)
|
|
if adder == r.global.setAdder {
|
|
if stdArr != nil {
|
|
for _, v := range stdArr.values {
|
|
so.m.set(v, nil)
|
|
}
|
|
} else {
|
|
r.getIterator(arg, nil).iterate(func(item Value) {
|
|
so.m.set(item, nil)
|
|
})
|
|
}
|
|
} else {
|
|
adderFn := toMethod(adder)
|
|
if adderFn == nil {
|
|
panic(r.NewTypeError("Set.add in missing"))
|
|
}
|
|
if stdArr != nil {
|
|
for _, item := range stdArr.values {
|
|
adderFn(FunctionCall{This: o, Arguments: []Value{item}})
|
|
}
|
|
} else {
|
|
r.getIterator(arg, nil).iterate(func(item Value) {
|
|
adderFn(FunctionCall{This: o, Arguments: []Value{item}})
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return o
|
|
}
|
|
|
|
func (r *Runtime) createSetIterator(setValue Value, kind iterationKind) Value {
|
|
obj := r.toObject(setValue)
|
|
setObj, ok := obj.self.(*setObject)
|
|
if !ok {
|
|
panic(r.NewTypeError("Object is not a Set"))
|
|
}
|
|
|
|
o := &Object{runtime: r}
|
|
|
|
si := &setIterObject{
|
|
iter: setObj.m.newIter(),
|
|
kind: kind,
|
|
}
|
|
si.class = classObject
|
|
si.val = o
|
|
si.extensible = true
|
|
o.self = si
|
|
si.prototype = r.getSetIteratorPrototype()
|
|
si.init()
|
|
|
|
return o
|
|
}
|
|
|
|
func (r *Runtime) setIterProto_next(call FunctionCall) Value {
|
|
thisObj := r.toObject(call.This)
|
|
if iter, ok := thisObj.self.(*setIterObject); ok {
|
|
return iter.next()
|
|
}
|
|
panic(r.NewTypeError("Method Set Iterator.prototype.next called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
|
|
}
|
|
|
|
func (r *Runtime) createSetProto(val *Object) objectImpl {
|
|
o := newBaseObjectObj(val, r.global.ObjectPrototype, classObject)
|
|
|
|
o._putProp("constructor", r.getSet(), true, false, true)
|
|
r.global.setAdder = r.newNativeFunc(r.setProto_add, "add", 1)
|
|
o._putProp("add", r.global.setAdder, true, false, true)
|
|
|
|
o._putProp("clear", r.newNativeFunc(r.setProto_clear, "clear", 0), true, false, true)
|
|
o._putProp("delete", r.newNativeFunc(r.setProto_delete, "delete", 1), true, false, true)
|
|
o._putProp("forEach", r.newNativeFunc(r.setProto_forEach, "forEach", 1), true, false, true)
|
|
o._putProp("has", r.newNativeFunc(r.setProto_has, "has", 1), true, false, true)
|
|
o.setOwnStr("size", &valueProperty{
|
|
getterFunc: r.newNativeFunc(r.setProto_getSize, "get size", 0),
|
|
accessor: true,
|
|
writable: true,
|
|
configurable: true,
|
|
}, true)
|
|
|
|
valuesFunc := r.newNativeFunc(r.setProto_values, "values", 0)
|
|
o._putProp("values", valuesFunc, true, false, true)
|
|
o._putProp("keys", valuesFunc, true, false, true)
|
|
o._putProp("entries", r.newNativeFunc(r.setProto_entries, "entries", 0), true, false, true)
|
|
o._putSym(SymIterator, valueProp(valuesFunc, true, false, true))
|
|
o._putSym(SymToStringTag, valueProp(asciiString(classSet), false, false, true))
|
|
|
|
return o
|
|
}
|
|
|
|
func (r *Runtime) createSet(val *Object) objectImpl {
|
|
o := r.newNativeConstructOnly(val, r.builtin_newSet, r.getSetPrototype(), "Set", 0)
|
|
r.putSpeciesReturnThis(o)
|
|
|
|
return o
|
|
}
|
|
|
|
func (r *Runtime) createSetIterProto(val *Object) objectImpl {
|
|
o := newBaseObjectObj(val, r.getIteratorPrototype(), classObject)
|
|
|
|
o._putProp("next", r.newNativeFunc(r.setIterProto_next, "next", 0), true, false, true)
|
|
o._putSym(SymToStringTag, valueProp(asciiString(classSetIterator), false, false, true))
|
|
|
|
return o
|
|
}
|
|
|
|
func (r *Runtime) getSetIteratorPrototype() *Object {
|
|
var o *Object
|
|
if o = r.global.SetIteratorPrototype; o == nil {
|
|
o = &Object{runtime: r}
|
|
r.global.SetIteratorPrototype = o
|
|
o.self = r.createSetIterProto(o)
|
|
}
|
|
return o
|
|
}
|
|
|
|
func (r *Runtime) getSetPrototype() *Object {
|
|
ret := r.global.SetPrototype
|
|
if ret == nil {
|
|
ret = &Object{runtime: r}
|
|
r.global.SetPrototype = ret
|
|
ret.self = r.createSetProto(ret)
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (r *Runtime) getSet() *Object {
|
|
ret := r.global.Set
|
|
if ret == nil {
|
|
ret = &Object{runtime: r}
|
|
r.global.Set = ret
|
|
ret.self = r.createSet(ret)
|
|
}
|
|
return ret
|
|
}
|