Skip to content

Commit

Permalink
Fixed defineProperty("length") for arrays. Improved detection of non-…
Browse files Browse the repository at this point in the history
…standard array configurations. Upgraded tc39 tests.
  • Loading branch information
dop251 committed Apr 3, 2022
1 parent 451b4e4 commit 90825c0
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 35 deletions.
2 changes: 1 addition & 1 deletion .tc39_test262_checkout.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/sh
# this is just the commit it was last tested with
sha=e87b0048c402479df1d9cb391fb86620cf3200fd
sha=926b0960d737b9f1dfd0ec0c1dfd95d836016d33

mkdir -p testdata/test262
cd testdata/test262
Expand Down
12 changes: 6 additions & 6 deletions array.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,6 @@ func (a *arrayObject) setLengthInt(l uint32, throw bool) bool {
}

func (a *arrayObject) setLength(v uint32, throw bool) bool {
if v == a.length {
return true
}
if !a.lengthProp.writable {
a.val.runtime.typeErrorResult(throw, "length is not writable")
return false
Expand Down Expand Up @@ -201,7 +198,7 @@ func (a *arrayObject) getStr(name unistring.String, receiver Value) Value {
return a.getStrWithOwnProp(a.getOwnPropStr(name), name, receiver)
}

func (a *arrayObject) getLengthProp() Value {
func (a *arrayObject) getLengthProp() *valueProperty {
a.lengthProp.value = intToValue(int64(a.length))
return &a.lengthProp
}
Expand Down Expand Up @@ -382,7 +379,10 @@ func (r *Runtime) defineArrayLength(prop *valueProperty, descr PropertyDescripto
}

if descr.Value != nil {
ret = setter(newLen, false)
oldLen := uint32(prop.value.ToInteger())
if oldLen != newLen {
ret = setter(newLen, false)
}
} else {
ret = true
}
Expand Down Expand Up @@ -437,7 +437,7 @@ func (a *arrayObject) defineOwnPropertyStr(name unistring.String, descr Property
return a._defineIdxProperty(idx, descr, throw)
}
if name == "length" {
return a.val.runtime.defineArrayLength(&a.lengthProp, descr, a.setLength, throw)
return a.val.runtime.defineArrayLength(a.getLengthProp(), descr, a.setLength, throw)
}
return a.baseObject.defineOwnPropertyStr(name, descr, throw)
}
Expand Down
7 changes: 2 additions & 5 deletions array_sparse.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,6 @@ func (a *sparseArrayObject) setLengthInt(l uint32, throw bool) bool {
}

func (a *sparseArrayObject) setLength(v uint32, throw bool) bool {
if v == a.length {
return true
}
if !a.lengthProp.writable {
a.val.runtime.typeErrorResult(throw, "length is not writable")
return false
Expand Down Expand Up @@ -120,7 +117,7 @@ func (a *sparseArrayObject) getIdx(idx valueInt, receiver Value) Value {
return prop
}

func (a *sparseArrayObject) getLengthProp() Value {
func (a *sparseArrayObject) getLengthProp() *valueProperty {
a.lengthProp.value = intToValue(int64(a.length))
return &a.lengthProp
}
Expand Down Expand Up @@ -369,7 +366,7 @@ func (a *sparseArrayObject) defineOwnPropertyStr(name unistring.String, descr Pr
return a._defineIdxProperty(idx, descr, throw)
}
if name == "length" {
return a.val.runtime.defineArrayLength(&a.lengthProp, descr, a.setLength, throw)
return a.val.runtime.defineArrayLength(a.getLengthProp(), descr, a.setLength, throw)
}
return a.baseObject.defineOwnPropertyStr(name, descr, throw)
}
Expand Down
42 changes: 33 additions & 9 deletions builtin_array.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,8 @@ func (r *Runtime) arrayproto_pop(call FunctionCall) Value {
obj := call.This.ToObject(r)
if a, ok := obj.self.(*arrayObject); ok {
l := a.length
var val Value
if l > 0 {
var val Value
l--
if l < uint32(len(a.values)) {
val = a.values[l]
Expand All @@ -161,10 +161,15 @@ func (r *Runtime) arrayproto_pop(call FunctionCall) Value {
//a._setLengthInt(l, false)
a.values[l] = nil
a.values = a.values[:l]
} else {
val = _undefined
}
if a.lengthProp.writable {
a.length = l
return val
} else {
a.setLength(0, true) // will throw
}
return _undefined
return val
} else {
return r.arrayproto_pop_generic(obj)
}
Expand Down Expand Up @@ -313,7 +318,7 @@ func (r *Runtime) arrayproto_slice(call FunctionCall) Value {

a := arraySpeciesCreate(o, count)
if src := r.checkStdArrayObj(o); src != nil {
if dst, ok := a.self.(*arrayObject); ok {
if dst := r.checkStdArrayObjWithProto(a); dst != nil {
values := make([]Value, count)
copy(values, src.values[start:])
setArrayValues(dst, values)
Expand Down Expand Up @@ -396,7 +401,7 @@ func (r *Runtime) arrayproto_splice(call FunctionCall) Value {
itemCount := max(int64(len(call.Arguments)-2), 0)
newLength := length - actualDeleteCount + itemCount
if src := r.checkStdArrayObj(o); src != nil {
if dst, ok := a.self.(*arrayObject); ok {
if dst := r.checkStdArrayObjWithProto(a); dst != nil {
values := make([]Value, actualDeleteCount)
copy(values, src.values[actualStart:])
setArrayValues(dst, values)
Expand Down Expand Up @@ -484,7 +489,7 @@ func (r *Runtime) arrayproto_unshift(call FunctionCall) Value {
argCount := int64(len(call.Arguments))
newLen := intToValue(length + argCount)
newSize := length + argCount
if arr := r.checkStdArrayObj(o); arr != nil && newSize < math.MaxUint32 {
if arr := r.checkStdArrayObjWithProto(o); arr != nil && newSize < math.MaxUint32 {
if int64(cap(arr.values)) >= newSize {
arr.values = arr.values[:newSize]
copy(arr.values[argCount:], arr.values[:length])
Expand Down Expand Up @@ -917,8 +922,11 @@ func (r *Runtime) arrayproto_reverse(call FunctionCall) Value {

func (r *Runtime) arrayproto_shift(call FunctionCall) Value {
o := call.This.ToObject(r)
if a := r.checkStdArrayObj(o); a != nil {
if a := r.checkStdArrayObjWithProto(o); a != nil {
if len(a.values) == 0 {
if !a.lengthProp.writable {
a.setLength(0, true) // will throw
}
return _undefined
}
first := a.values[0]
Expand Down Expand Up @@ -1138,6 +1146,20 @@ func (r *Runtime) checkStdArrayObj(obj *Object) *arrayObject {
return nil
}

func (r *Runtime) checkStdArrayObjWithProto(obj *Object) *arrayObject {
if arr := r.checkStdArrayObj(obj); arr != nil {
if p1, ok := arr.prototype.self.(*arrayObject); ok && p1.propValueCount == 0 {
if p2, ok := p1.prototype.self.(*baseObject); ok && p2.prototype == nil {
p2.ensurePropOrder()
if p2.idxPropCount == 0 {
return arr
}
}
}
}
return nil
}

func (r *Runtime) checkStdArray(v Value) *arrayObject {
if obj, ok := v.(*Object); ok {
return r.checkStdArrayObj(obj)
Expand Down Expand Up @@ -1195,7 +1217,7 @@ func (r *Runtime) array_from(call FunctionCall) Value {
}
iter := r.getIterator(items, usingIterator)
if mapFn == nil {
if a := r.checkStdArrayObj(arr); a != nil {
if a := r.checkStdArrayObjWithProto(arr); a != nil {
var values []Value
iter.iterate(func(val Value) {
values = append(values, val)
Expand All @@ -1222,7 +1244,7 @@ func (r *Runtime) array_from(call FunctionCall) Value {
arr = r.newArrayValues(nil)
}
if mapFn == nil {
if a := r.checkStdArrayObj(arr); a != nil {
if a := r.checkStdArrayObjWithProto(arr); a != nil {
values := make([]Value, l)
for k := int64(0); k < l; k++ {
values[k] = nilSafe(arrayLike.self.getIdx(valueInt(k), nil))
Expand Down Expand Up @@ -1344,6 +1366,8 @@ func (r *Runtime) createArrayProto(val *Object) objectImpl {
bl.setOwnStr("includes", valueTrue, true)
bl.setOwnStr("keys", valueTrue, true)
bl.setOwnStr("values", valueTrue, true)
bl.setOwnStr("groupBy", valueTrue, true)
bl.setOwnStr("groupByToMap", valueTrue, true)
o._putSym(SymUnscopables, valueProp(bl.val, false, false, true))

return o
Expand Down
19 changes: 10 additions & 9 deletions object.go
Original file line number Diff line number Diff line change
Expand Up @@ -590,9 +590,7 @@ func (o *baseObject) setForeignStr(name unistring.String, val, receiver Value, t

func (o *baseObject) setForeignIdx(name valueInt, val, receiver Value, throw bool) (bool, bool) {
if idx := toIdx(name); idx != math.MaxUint32 {
if o.lastSortedPropLen != len(o.propNames) {
o.fixPropOrder()
}
o.ensurePropOrder()
if o.idxPropCount == 0 {
return o._setForeignIdx(name, name, nil, receiver, throw)
}
Expand Down Expand Up @@ -1238,9 +1236,7 @@ func copyNamesIfNeeded(names []unistring.String, extraCap int) []unistring.Strin
}

func (o *baseObject) iterateStringKeys() iterNextFunc {
if len(o.propNames) > o.lastSortedPropLen {
o.fixPropOrder()
}
o.ensurePropOrder()
propNames := prepareNamesForCopy(o.propNames)
o.propNames = propNames
return (&objectPropIter{
Expand Down Expand Up @@ -1301,6 +1297,13 @@ func (o *baseObject) equal(objectImpl) bool {
return false
}

// hopefully this gets inlined
func (o *baseObject) ensurePropOrder() {
if o.lastSortedPropLen < len(o.propNames) {
o.fixPropOrder()
}
}

// Reorder property names so that any integer properties are shifted to the beginning of the list
// in ascending order. This is to conform to https://262.ecma-international.org/#sec-ordinaryownpropertykeys.
// Personally I think this requirement is strange. I can sort of understand where they are coming from,
Expand Down Expand Up @@ -1336,9 +1339,7 @@ func (o *baseObject) fixPropOrder() {
}

func (o *baseObject) stringKeys(all bool, keys []Value) []Value {
if len(o.propNames) > o.lastSortedPropLen {
o.fixPropOrder()
}
o.ensurePropOrder()
if all {
for _, k := range o.propNames {
keys = append(keys, stringValueFromRaw(k))
Expand Down
18 changes: 13 additions & 5 deletions vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -3088,12 +3088,20 @@ func (vm *vm) checkBindVarsGlobal(names []unistring.String) {
sn := vm.r.global.stash.names
if bo, ok := o.(*baseObject); ok {
// shortcut
for _, name := range names {
if !bo.hasOwnPropertyStr(name) && !bo.extensible {
panic(vm.r.NewTypeError("Cannot define global variable '%s', global object is not extensible", name))
if bo.extensible {
for _, name := range names {
if _, exists := sn[name]; exists {
panic(vm.alreadyDeclared(name))
}
}
if _, exists := sn[name]; exists {
panic(vm.alreadyDeclared(name))
} else {
for _, name := range names {
if !bo.hasOwnPropertyStr(name) {
panic(vm.r.NewTypeError("Cannot define global variable '%s', global object is not extensible", name))
}
if _, exists := sn[name]; exists {
panic(vm.alreadyDeclared(name))
}
}
}
} else {
Expand Down

0 comments on commit 90825c0

Please sign in to comment.