Skip to content

Commit

Permalink
Implemented 'copy-on-change' mechanism for inner compound values. Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
dop251 committed Jul 5, 2022
1 parent 4418d45 commit b1618db
Show file tree
Hide file tree
Showing 19 changed files with 749 additions and 168 deletions.
16 changes: 8 additions & 8 deletions array.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,19 +178,19 @@ func (a *arrayObject) getOwnPropIdx(idx valueInt) Value {
return a.baseObject.getOwnPropStr(idx.string())
}

func (a *arrayObject) sortLen() int64 {
return int64(len(a.values))
func (a *arrayObject) sortLen() int {
return len(a.values)
}

func (a *arrayObject) sortGet(i int64) Value {
func (a *arrayObject) sortGet(i int) Value {
v := a.values[i]
if p, ok := v.(*valueProperty); ok {
v = p.get(a.val)
}
return v
}

func (a *arrayObject) swap(i, j int64) {
func (a *arrayObject) swap(i int, j int) {
a.values[i], a.values[j] = a.values[j], a.values[i]
}

Expand Down Expand Up @@ -511,12 +511,12 @@ func (a *arrayObject) exportToArrayOrSlice(dst reflect.Value, typ reflect.Type,
r := a.val.runtime
if iter := a.getSym(SymIterator, nil); iter == r.global.arrayValues || iter == nil {
l := toIntStrict(int64(a.length))
if dst.Len() != l {
if typ.Kind() == reflect.Array {
if typ.Kind() == reflect.Array {
if dst.Len() != l {
return fmt.Errorf("cannot convert an Array into an array, lengths mismatch (have %d, need %d)", l, dst.Len())
} else {
dst.Set(reflect.MakeSlice(typ, l, l))
}
} else {
dst.Set(reflect.MakeSlice(typ, l, l))
}
ctx.putTyped(a.val, typ, dst.Interface())
for i := 0; i < l; i++ {
Expand Down
12 changes: 6 additions & 6 deletions array_sparse.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,9 +409,9 @@ func (a *sparseArrayObject) deleteIdx(idx valueInt, throw bool) bool {
return a.baseObject.deleteStr(idx.string(), throw)
}

func (a *sparseArrayObject) sortLen() int64 {
func (a *sparseArrayObject) sortLen() int {
if len(a.items) > 0 {
return int64(a.items[len(a.items)-1].idx) + 1
return toIntStrict(int64(a.items[len(a.items)-1].idx) + 1)
}

return 0
Expand Down Expand Up @@ -460,12 +460,12 @@ func (a *sparseArrayObject) exportToArrayOrSlice(dst reflect.Value, typ reflect.
r := a.val.runtime
if iter := a.getSym(SymIterator, nil); iter == r.global.arrayValues || iter == nil {
l := toIntStrict(int64(a.length))
if dst.Len() != l {
if typ.Kind() == reflect.Array {
if typ.Kind() == reflect.Array {
if dst.Len() != l {
return fmt.Errorf("cannot convert an Array into an array, lengths mismatch (have %d, need %d)", l, dst.Len())
} else {
dst.Set(reflect.MakeSlice(typ, l, l))
}
} else {
dst.Set(reflect.MakeSlice(typ, l, l))
}
ctx.putTyped(a.val, typ, dst.Interface())
for _, item := range a.items {
Expand Down
21 changes: 14 additions & 7 deletions builtin_array.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,9 +352,16 @@ func (r *Runtime) arrayproto_sort(call FunctionCall) Value {
}
}

var s sortable
if r.checkStdArrayObj(o) != nil {
s = o.self
} else if _, ok := o.self.(reflectValueWrapper); ok {
s = o.self
}

if s != nil {
ctx := arraySortCtx{
obj: o.self,
obj: s,
compare: compareFn,
}

Expand Down Expand Up @@ -1443,9 +1450,9 @@ func (r *Runtime) initArray() {
}

type sortable interface {
sortLen() int64
sortGet(int64) Value
swap(int64, int64)
sortLen() int
sortGet(int) Value
swap(int, int)
}

type arraySortCtx struct {
Expand Down Expand Up @@ -1500,13 +1507,13 @@ func (a *arraySortCtx) sortCompare(x, y Value) int {
// sort.Interface

func (a *arraySortCtx) Len() int {
return int(a.obj.sortLen())
return a.obj.sortLen()
}

func (a *arraySortCtx) Less(j, k int) bool {
return a.sortCompare(a.obj.sortGet(int64(j)), a.obj.sortGet(int64(k))) < 0
return a.sortCompare(a.obj.sortGet(j), a.obj.sortGet(k)) < 0
}

func (a *arraySortCtx) Swap(j, k int) {
a.obj.swap(int64(j), int64(k))
a.obj.swap(j, k)
}
8 changes: 4 additions & 4 deletions builtin_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@ func (so *setObject) export(ctx *objectExportCtx) interface{} {

func (so *setObject) exportToArrayOrSlice(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error {
l := so.m.size
if dst.Len() != l {
if typ.Kind() == reflect.Array {
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))
}
} else {
dst.Set(reflect.MakeSlice(typ, l, l))
}
ctx.putTyped(so.val, typ, dst.Interface())
iter := so.m.newIter()
Expand Down
6 changes: 3 additions & 3 deletions destruct.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,15 @@ func (d *destructKeyedSource) recordKey(key Value) {
d.usedKeys[key] = struct{}{}
}

func (d *destructKeyedSource) sortLen() int64 {
func (d *destructKeyedSource) sortLen() int {
return d.w().sortLen()
}

func (d *destructKeyedSource) sortGet(i int64) Value {
func (d *destructKeyedSource) sortGet(i int) Value {
return d.w().sortGet(i)
}

func (d *destructKeyedSource) swap(i int64, i2 int64) {
func (d *destructKeyedSource) swap(i int, i2 int) {
d.w().swap(i, i2)
}

Expand Down
16 changes: 8 additions & 8 deletions object.go
Original file line number Diff line number Diff line change
Expand Up @@ -923,15 +923,15 @@ func (o *baseObject) preventExtensions(bool) bool {
return true
}

func (o *baseObject) sortLen() int64 {
return toLength(o.val.self.getStr("length", nil))
func (o *baseObject) sortLen() int {
return toIntStrict(toLength(o.val.self.getStr("length", nil)))
}

func (o *baseObject) sortGet(i int64) Value {
func (o *baseObject) sortGet(i int) Value {
return o.val.self.getIdx(valueInt(i), nil)
}

func (o *baseObject) swap(i, j int64) {
func (o *baseObject) swap(i int, j int) {
ii := valueInt(i)
jj := valueInt(j)

Expand Down Expand Up @@ -1026,12 +1026,12 @@ func genericExportToArrayOrSlice(o *Object, dst reflect.Value, typ reflect.Type,
if ex != nil {
return ex
}
if dst.Len() != len(values) {
if typ.Kind() == reflect.Array {
if typ.Kind() == reflect.Array {
if dst.Len() != len(values) {
return fmt.Errorf("cannot convert an iterable into an array, lengths mismatch (have %d, need %d)", len(values), dst.Len())
} else {
dst.Set(reflect.MakeSlice(typ, len(values), len(values)))
}
} else {
dst.Set(reflect.MakeSlice(typ, len(values), len(values)))
}
ctx.putTyped(o, typ, dst.Interface())
for i, val := range values {
Expand Down
16 changes: 8 additions & 8 deletions object_dynamic.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,15 +132,15 @@ func (r *Runtime) NewDynamicArray(a DynamicArray) *Object {
return v
}

func (*dynamicObject) sortLen() int64 {
func (*dynamicObject) sortLen() int {
return 0
}

func (*dynamicObject) sortGet(i int64) Value {
func (*dynamicObject) sortGet(i int) Value {
return nil
}

func (*dynamicObject) swap(i int64, i2 int64) {
func (*dynamicObject) swap(i int, i2 int) {
}

func (*dynamicObject) className() string {
Expand Down Expand Up @@ -526,15 +526,15 @@ func (*baseDynamicObject) _putProp(name unistring.String, value Value, writable,
func (*baseDynamicObject) _putSym(s *Symbol, prop Value) {
}

func (a *dynamicArray) sortLen() int64 {
return int64(a.a.Len())
func (a *dynamicArray) sortLen() int {
return a.a.Len()
}

func (a *dynamicArray) sortGet(i int64) Value {
return a.a.Get(int(i))
func (a *dynamicArray) sortGet(i int) Value {
return a.a.Get(i)
}

func (a *dynamicArray) swap(i int64, j int64) {
func (a *dynamicArray) swap(i int, j int) {
x := a.sortGet(i)
y := a.sortGet(j)
a.a.Set(int(i), y)
Expand Down
120 changes: 99 additions & 21 deletions object_goarray_reflect.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,51 @@ type objectGoArrayReflect struct {
objectGoReflect
lengthProp valueProperty

valueCache valueArrayCache

putIdx func(idx int, v Value, throw bool) bool
}

type valueArrayCache []reflectValueWrapper

func (c *valueArrayCache) get(idx int) reflectValueWrapper {
if idx < len(*c) {
return (*c)[idx]
}
return nil
}

func (c *valueArrayCache) grow(newlen int) {
oldcap := cap(*c)
if oldcap < newlen {
a := make([]reflectValueWrapper, newlen, growCap(newlen, len(*c), oldcap))
copy(a, *c)
*c = a
} else {
*c = (*c)[:newlen]
}
}

func (c *valueArrayCache) put(idx int, w reflectValueWrapper) {
if len(*c) <= idx {
c.grow(idx + 1)
}
(*c)[idx] = w
}

func (c *valueArrayCache) shrink(newlen int) {
if len(*c) > newlen {
tail := (*c)[newlen:]
for i, item := range tail {
if item != nil {
copyReflectValueWrapper(item)
tail[i] = nil
}
}
*c = (*c)[:newlen]
}
}

func (o *objectGoArrayReflect) _init() {
o.objectGoReflect.init()
o.class = classArray
Expand Down Expand Up @@ -46,11 +88,18 @@ func (o *objectGoArrayReflect) _hasStr(name unistring.String) bool {
}

func (o *objectGoArrayReflect) _getIdx(idx int) Value {
if v := o.valueCache.get(idx); v != nil {
return v.esValue()
}

v := o.value.Index(idx)
if (v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface) && v.IsNil() {
return _null

res, w := o.elemToValue(v)
if w != nil {
o.valueCache.put(idx, w)
}
return o.val.runtime.toValue(v.Interface(), v)

return res
}

func (o *objectGoArrayReflect) getIdx(idx valueInt, receiver Value) Value {
Expand Down Expand Up @@ -101,11 +150,23 @@ func (o *objectGoArrayReflect) getOwnPropIdx(idx valueInt) Value {
}

func (o *objectGoArrayReflect) _putIdx(idx int, v Value, throw bool) bool {
err := o.val.runtime.toReflectValue(v, o.value.Index(idx), &objectExportCtx{})
cached := o.valueCache.get(idx)
if cached != nil {
copyReflectValueWrapper(cached)
}

rv := o.value.Index(idx)
err := o.val.runtime.toReflectValue(v, rv, &objectExportCtx{})
if err != nil {
if cached != nil {
cached.setReflectValue(rv)
}
o.val.runtime.typeErrorResult(throw, "Go type conversion error: %v", err)
return false
}
if cached != nil {
o.valueCache[idx] = nil
}
return true
}

Expand Down Expand Up @@ -211,6 +272,11 @@ func (o *objectGoArrayReflect) toPrimitive() Value {

func (o *objectGoArrayReflect) _deleteIdx(idx int) {
if idx < o.value.Len() {
if cv := o.valueCache.get(idx); cv != nil {
copyReflectValueWrapper(cv)
o.valueCache[idx] = nil
}

o.value.Index(idx).Set(reflect.Zero(o.value.Type().Elem()))
}
}
Expand Down Expand Up @@ -262,27 +328,39 @@ func (o *objectGoArrayReflect) iterateStringKeys() iterNextFunc {
}).next
}

func (o *objectGoArrayReflect) equal(other objectImpl) bool {
if other, ok := other.(*objectGoArrayReflect); ok {
return o.value.Interface() == other.value.Interface()
}
return false
func (o *objectGoArrayReflect) sortLen() int {
return o.value.Len()
}

func (o *objectGoArrayReflect) sortLen() int64 {
return int64(o.value.Len())
}

func (o *objectGoArrayReflect) sortGet(i int64) Value {
func (o *objectGoArrayReflect) sortGet(i int) Value {
return o.getIdx(valueInt(i), nil)
}

func (o *objectGoArrayReflect) swap(i, j int64) {
ii := toIntStrict(i)
jj := toIntStrict(j)
x := o._getIdx(ii)
y := o._getIdx(jj)
func (o *objectGoArrayReflect) swap(i int, j int) {
vi := o.value.Index(i)
vj := o.value.Index(j)
tmp := reflect.New(o.value.Type().Elem()).Elem()
tmp.Set(vi)
vi.Set(vj)
vj.Set(tmp)

o._putIdx(ii, y, false)
o._putIdx(jj, x, false)
cachedI := o.valueCache.get(i)
cachedJ := o.valueCache.get(j)
if cachedI != nil {
cachedI.setReflectValue(vj)
o.valueCache.put(j, cachedI)
} else {
if j < len(o.valueCache) {
o.valueCache[j] = nil
}
}

if cachedJ != nil {
cachedJ.setReflectValue(vi)
o.valueCache.put(i, cachedJ)
} else {
if i < len(o.valueCache) {
o.valueCache[i] = nil
}
}
}
Loading

0 comments on commit b1618db

Please sign in to comment.