Browse Source

Fixed defineProperty("length") for arrays. Improved detection of non-standard array configurations. Upgraded tc39 tests.

Dmitry Panov 3 years ago
parent
commit
90825c0207
6 changed files with 65 additions and 35 deletions
  1. 1 1
      .tc39_test262_checkout.sh
  2. 6 6
      array.go
  3. 2 5
      array_sparse.go
  4. 33 9
      builtin_array.go
  5. 10 9
      object.go
  6. 13 5
      vm.go

+ 1 - 1
.tc39_test262_checkout.sh

@@ -1,6 +1,6 @@
 #!/bin/sh
 #!/bin/sh
 # this is just the commit it was last tested with
 # this is just the commit it was last tested with
-sha=e87b0048c402479df1d9cb391fb86620cf3200fd
+sha=926b0960d737b9f1dfd0ec0c1dfd95d836016d33
 
 
 mkdir -p testdata/test262
 mkdir -p testdata/test262
 cd testdata/test262
 cd testdata/test262

+ 6 - 6
array.go

@@ -127,9 +127,6 @@ func (a *arrayObject) setLengthInt(l uint32, throw bool) bool {
 }
 }
 
 
 func (a *arrayObject) setLength(v uint32, throw bool) bool {
 func (a *arrayObject) setLength(v uint32, throw bool) bool {
-	if v == a.length {
-		return true
-	}
 	if !a.lengthProp.writable {
 	if !a.lengthProp.writable {
 		a.val.runtime.typeErrorResult(throw, "length is not writable")
 		a.val.runtime.typeErrorResult(throw, "length is not writable")
 		return false
 		return false
@@ -201,7 +198,7 @@ func (a *arrayObject) getStr(name unistring.String, receiver Value) Value {
 	return a.getStrWithOwnProp(a.getOwnPropStr(name), name, receiver)
 	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))
 	a.lengthProp.value = intToValue(int64(a.length))
 	return &a.lengthProp
 	return &a.lengthProp
 }
 }
@@ -382,7 +379,10 @@ func (r *Runtime) defineArrayLength(prop *valueProperty, descr PropertyDescripto
 	}
 	}
 
 
 	if descr.Value != nil {
 	if descr.Value != nil {
-		ret = setter(newLen, false)
+		oldLen := uint32(prop.value.ToInteger())
+		if oldLen != newLen {
+			ret = setter(newLen, false)
+		}
 	} else {
 	} else {
 		ret = true
 		ret = true
 	}
 	}
@@ -437,7 +437,7 @@ func (a *arrayObject) defineOwnPropertyStr(name unistring.String, descr Property
 		return a._defineIdxProperty(idx, descr, throw)
 		return a._defineIdxProperty(idx, descr, throw)
 	}
 	}
 	if name == "length" {
 	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)
 	return a.baseObject.defineOwnPropertyStr(name, descr, throw)
 }
 }

+ 2 - 5
array_sparse.go

@@ -78,9 +78,6 @@ func (a *sparseArrayObject) setLengthInt(l uint32, throw bool) bool {
 }
 }
 
 
 func (a *sparseArrayObject) setLength(v uint32, throw bool) bool {
 func (a *sparseArrayObject) setLength(v uint32, throw bool) bool {
-	if v == a.length {
-		return true
-	}
 	if !a.lengthProp.writable {
 	if !a.lengthProp.writable {
 		a.val.runtime.typeErrorResult(throw, "length is not writable")
 		a.val.runtime.typeErrorResult(throw, "length is not writable")
 		return false
 		return false
@@ -120,7 +117,7 @@ func (a *sparseArrayObject) getIdx(idx valueInt, receiver Value) Value {
 	return prop
 	return prop
 }
 }
 
 
-func (a *sparseArrayObject) getLengthProp() Value {
+func (a *sparseArrayObject) getLengthProp() *valueProperty {
 	a.lengthProp.value = intToValue(int64(a.length))
 	a.lengthProp.value = intToValue(int64(a.length))
 	return &a.lengthProp
 	return &a.lengthProp
 }
 }
@@ -369,7 +366,7 @@ func (a *sparseArrayObject) defineOwnPropertyStr(name unistring.String, descr Pr
 		return a._defineIdxProperty(idx, descr, throw)
 		return a._defineIdxProperty(idx, descr, throw)
 	}
 	}
 	if name == "length" {
 	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)
 	return a.baseObject.defineOwnPropertyStr(name, descr, throw)
 }
 }

+ 33 - 9
builtin_array.go

@@ -144,8 +144,8 @@ func (r *Runtime) arrayproto_pop(call FunctionCall) Value {
 	obj := call.This.ToObject(r)
 	obj := call.This.ToObject(r)
 	if a, ok := obj.self.(*arrayObject); ok {
 	if a, ok := obj.self.(*arrayObject); ok {
 		l := a.length
 		l := a.length
+		var val Value
 		if l > 0 {
 		if l > 0 {
-			var val Value
 			l--
 			l--
 			if l < uint32(len(a.values)) {
 			if l < uint32(len(a.values)) {
 				val = a.values[l]
 				val = a.values[l]
@@ -161,10 +161,15 @@ func (r *Runtime) arrayproto_pop(call FunctionCall) Value {
 			//a._setLengthInt(l, false)
 			//a._setLengthInt(l, false)
 			a.values[l] = nil
 			a.values[l] = nil
 			a.values = a.values[:l]
 			a.values = a.values[:l]
+		} else {
+			val = _undefined
+		}
+		if a.lengthProp.writable {
 			a.length = l
 			a.length = l
-			return val
+		} else {
+			a.setLength(0, true) // will throw
 		}
 		}
-		return _undefined
+		return val
 	} else {
 	} else {
 		return r.arrayproto_pop_generic(obj)
 		return r.arrayproto_pop_generic(obj)
 	}
 	}
@@ -313,7 +318,7 @@ func (r *Runtime) arrayproto_slice(call FunctionCall) Value {
 
 
 	a := arraySpeciesCreate(o, count)
 	a := arraySpeciesCreate(o, count)
 	if src := r.checkStdArrayObj(o); src != nil {
 	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)
 			values := make([]Value, count)
 			copy(values, src.values[start:])
 			copy(values, src.values[start:])
 			setArrayValues(dst, values)
 			setArrayValues(dst, values)
@@ -396,7 +401,7 @@ func (r *Runtime) arrayproto_splice(call FunctionCall) Value {
 	itemCount := max(int64(len(call.Arguments)-2), 0)
 	itemCount := max(int64(len(call.Arguments)-2), 0)
 	newLength := length - actualDeleteCount + itemCount
 	newLength := length - actualDeleteCount + itemCount
 	if src := r.checkStdArrayObj(o); src != nil {
 	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)
 			values := make([]Value, actualDeleteCount)
 			copy(values, src.values[actualStart:])
 			copy(values, src.values[actualStart:])
 			setArrayValues(dst, values)
 			setArrayValues(dst, values)
@@ -484,7 +489,7 @@ func (r *Runtime) arrayproto_unshift(call FunctionCall) Value {
 	argCount := int64(len(call.Arguments))
 	argCount := int64(len(call.Arguments))
 	newLen := intToValue(length + argCount)
 	newLen := intToValue(length + argCount)
 	newSize := 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 {
 		if int64(cap(arr.values)) >= newSize {
 			arr.values = arr.values[:newSize]
 			arr.values = arr.values[:newSize]
 			copy(arr.values[argCount:], arr.values[:length])
 			copy(arr.values[argCount:], arr.values[:length])
@@ -917,8 +922,11 @@ func (r *Runtime) arrayproto_reverse(call FunctionCall) Value {
 
 
 func (r *Runtime) arrayproto_shift(call FunctionCall) Value {
 func (r *Runtime) arrayproto_shift(call FunctionCall) Value {
 	o := call.This.ToObject(r)
 	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 len(a.values) == 0 {
+			if !a.lengthProp.writable {
+				a.setLength(0, true) // will throw
+			}
 			return _undefined
 			return _undefined
 		}
 		}
 		first := a.values[0]
 		first := a.values[0]
@@ -1138,6 +1146,20 @@ func (r *Runtime) checkStdArrayObj(obj *Object) *arrayObject {
 	return nil
 	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 {
 func (r *Runtime) checkStdArray(v Value) *arrayObject {
 	if obj, ok := v.(*Object); ok {
 	if obj, ok := v.(*Object); ok {
 		return r.checkStdArrayObj(obj)
 		return r.checkStdArrayObj(obj)
@@ -1195,7 +1217,7 @@ func (r *Runtime) array_from(call FunctionCall) Value {
 		}
 		}
 		iter := r.getIterator(items, usingIterator)
 		iter := r.getIterator(items, usingIterator)
 		if mapFn == nil {
 		if mapFn == nil {
-			if a := r.checkStdArrayObj(arr); a != nil {
+			if a := r.checkStdArrayObjWithProto(arr); a != nil {
 				var values []Value
 				var values []Value
 				iter.iterate(func(val Value) {
 				iter.iterate(func(val Value) {
 					values = append(values, val)
 					values = append(values, val)
@@ -1222,7 +1244,7 @@ func (r *Runtime) array_from(call FunctionCall) Value {
 			arr = r.newArrayValues(nil)
 			arr = r.newArrayValues(nil)
 		}
 		}
 		if mapFn == nil {
 		if mapFn == nil {
-			if a := r.checkStdArrayObj(arr); a != nil {
+			if a := r.checkStdArrayObjWithProto(arr); a != nil {
 				values := make([]Value, l)
 				values := make([]Value, l)
 				for k := int64(0); k < l; k++ {
 				for k := int64(0); k < l; k++ {
 					values[k] = nilSafe(arrayLike.self.getIdx(valueInt(k), nil))
 					values[k] = nilSafe(arrayLike.self.getIdx(valueInt(k), nil))
@@ -1344,6 +1366,8 @@ func (r *Runtime) createArrayProto(val *Object) objectImpl {
 	bl.setOwnStr("includes", valueTrue, true)
 	bl.setOwnStr("includes", valueTrue, true)
 	bl.setOwnStr("keys", valueTrue, true)
 	bl.setOwnStr("keys", valueTrue, true)
 	bl.setOwnStr("values", 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))
 	o._putSym(SymUnscopables, valueProp(bl.val, false, false, true))
 
 
 	return o
 	return o

+ 10 - 9
object.go

@@ -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) {
 func (o *baseObject) setForeignIdx(name valueInt, val, receiver Value, throw bool) (bool, bool) {
 	if idx := toIdx(name); idx != math.MaxUint32 {
 	if idx := toIdx(name); idx != math.MaxUint32 {
-		if o.lastSortedPropLen != len(o.propNames) {
-			o.fixPropOrder()
-		}
+		o.ensurePropOrder()
 		if o.idxPropCount == 0 {
 		if o.idxPropCount == 0 {
 			return o._setForeignIdx(name, name, nil, receiver, throw)
 			return o._setForeignIdx(name, name, nil, receiver, throw)
 		}
 		}
@@ -1238,9 +1236,7 @@ func copyNamesIfNeeded(names []unistring.String, extraCap int) []unistring.Strin
 }
 }
 
 
 func (o *baseObject) iterateStringKeys() iterNextFunc {
 func (o *baseObject) iterateStringKeys() iterNextFunc {
-	if len(o.propNames) > o.lastSortedPropLen {
-		o.fixPropOrder()
-	}
+	o.ensurePropOrder()
 	propNames := prepareNamesForCopy(o.propNames)
 	propNames := prepareNamesForCopy(o.propNames)
 	o.propNames = propNames
 	o.propNames = propNames
 	return (&objectPropIter{
 	return (&objectPropIter{
@@ -1301,6 +1297,13 @@ func (o *baseObject) equal(objectImpl) bool {
 	return false
 	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
 // 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.
 // 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,
 // Personally I think this requirement is strange. I can sort of understand where they are coming from,
@@ -1336,9 +1339,7 @@ func (o *baseObject) fixPropOrder() {
 }
 }
 
 
 func (o *baseObject) stringKeys(all bool, keys []Value) []Value {
 func (o *baseObject) stringKeys(all bool, keys []Value) []Value {
-	if len(o.propNames) > o.lastSortedPropLen {
-		o.fixPropOrder()
-	}
+	o.ensurePropOrder()
 	if all {
 	if all {
 		for _, k := range o.propNames {
 		for _, k := range o.propNames {
 			keys = append(keys, stringValueFromRaw(k))
 			keys = append(keys, stringValueFromRaw(k))

+ 13 - 5
vm.go

@@ -3088,12 +3088,20 @@ func (vm *vm) checkBindVarsGlobal(names []unistring.String) {
 	sn := vm.r.global.stash.names
 	sn := vm.r.global.stash.names
 	if bo, ok := o.(*baseObject); ok {
 	if bo, ok := o.(*baseObject); ok {
 		// shortcut
 		// 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 {
 	} else {