Ver Fonte

Fixed bugs in Array.prototype.sort(), Object.assign() and Date.prototype.toJSON()

Dmitry Panov há 5 anos atrás
pai
commit
3ec8e20a89
8 ficheiros alterados com 128 adições e 8 exclusões
  1. 9 4
      builtin_array.go
  2. 46 0
      builtin_arrray_test.go
  3. 0 2
      builtin_date.go
  4. 4 1
      builtin_object.go
  5. 1 1
      builtin_typedarrays.go
  6. 43 0
      date_test.go
  7. 16 0
      object_test.go
  8. 9 0
      regexp_test.go

+ 9 - 4
builtin_array.go

@@ -173,7 +173,7 @@ func (r *Runtime) arrayproto_pop(call FunctionCall) Value {
 func (r *Runtime) arrayproto_join(call FunctionCall) Value {
 	o := call.This.ToObject(r)
 	l := int(toLength(o.self.getStr("length", nil)))
-	var sep valueString = asciiString("")
+	var sep valueString
 	if s := call.Argument(0); s != _undefined {
 		sep = s.toString()
 	} else {
@@ -334,9 +334,14 @@ func (r *Runtime) arrayproto_sort(call FunctionCall) Value {
 	o := call.This.ToObject(r)
 
 	var compareFn func(FunctionCall) Value
-
-	if arg, ok := call.Argument(0).(*Object); ok {
-		compareFn, _ = arg.self.assertCallable()
+	arg := call.Argument(0)
+	if arg != _undefined {
+		if arg, ok := call.Argument(0).(*Object); ok {
+			compareFn, _ = arg.self.assertCallable()
+		}
+		if compareFn == nil {
+			panic(r.NewTypeError("The comparison function must be either a function or undefined"))
+		}
 	}
 
 	ctx := arraySortCtx{

+ 46 - 0
builtin_arrray_test.go

@@ -219,3 +219,49 @@ func TestUnscopables(t *testing.T) {
 	`
 	testScript1(SCRIPT, valueTrue, t)
 }
+
+func TestArraySort(t *testing.T) {
+	const SCRIPT = `
+	assert.throws(TypeError, function() {
+		[1,2].sort(null);
+	}, "null compare function");
+	assert.throws(TypeError, function() {
+		[1,2].sort({});
+	}, "non-callable compare function");
+	`
+	testScript1(TESTLIB+SCRIPT, _undefined, t)
+}
+
+func TestArrayConcat(t *testing.T) {
+	const SCRIPT = `
+	var concat = Array.prototype.concat;
+	var array = [1, 2];
+	var sparseArray = [1, , 2];
+	var nonSpreadableArray = [1, 2];
+	nonSpreadableArray[Symbol.isConcatSpreadable] = false;
+	var arrayLike = { 0: 1, 1: 2, length: 2 };
+	var spreadableArrayLike = { 0: 1, 1: 2, length: 2 };
+	spreadableArrayLike[Symbol.isConcatSpreadable] = true;
+	assert(looksNative(concat));
+	assert(deepEqual(array.concat(), [1, 2]), '#1');
+	assert(deepEqual(sparseArray.concat(), [1, , 2]), '#2');
+	assert(deepEqual(nonSpreadableArray.concat(), [[1, 2]]), '#3');
+	assert(deepEqual(concat.call(arrayLike), [{ 0: 1, 1: 2, length: 2 }]), '#4');
+	assert(deepEqual(concat.call(spreadableArrayLike), [1, 2]), '#5');
+	assert(deepEqual([].concat(array), [1, 2]), '#6');
+	assert(deepEqual([].concat(sparseArray), [1, , 2]), '#7');
+	assert(deepEqual([].concat(nonSpreadableArray), [[1, 2]]), '#8');
+	assert(deepEqual([].concat(arrayLike), [{ 0: 1, 1: 2, length: 2 }]), '#9');
+	assert(deepEqual([].concat(spreadableArrayLike), [1, 2]), '#10');
+	assert(deepEqual(array.concat(sparseArray, nonSpreadableArray, arrayLike, spreadableArrayLike), [
+	1, 2, 1, , 2, [1, 2], { 0: 1, 1: 2, length: 2 }, 1, 2,
+	]), '#11');
+	array = [];
+	array.constructor = {};
+	array.constructor[Symbol.species] = function () {
+		return { foo: 1 };
+	}
+	assert.sameValue(array.concat().foo, 1, '@@species');
+	`
+	testScript1(TESTLIBX+SCRIPT, _undefined, t)
+}

+ 0 - 2
builtin_date.go

@@ -147,8 +147,6 @@ func (r *Runtime) dateproto_toJSON(call FunctionCall) Value {
 		if math.IsNaN(f) || math.IsInf(f, 0) {
 			return _null
 		}
-	} else if _, ok := tv.(valueInt); !ok {
-		return _null
 	}
 
 	if toISO, ok := obj.self.getStr("toISOString", nil).(*Object); ok {

+ 4 - 1
builtin_object.go

@@ -453,12 +453,15 @@ func (r *Runtime) object_assign(call FunctionCall) Value {
 		for _, arg := range call.Arguments[1:] {
 			if arg != _undefined && arg != _null {
 				source := arg.ToObject(r)
-				for _, key := range source.self.ownPropertyKeys(false, nil) {
+				for _, key := range source.self.ownPropertyKeys(true, nil) {
 					p := source.getOwnProp(key)
 					if p == nil {
 						continue
 					}
 					if v, ok := p.(*valueProperty); ok {
+						if !v.enumerable {
+							continue
+						}
 						p = v.get(source)
 					}
 					to.setOwn(key, p, true)

+ 1 - 1
builtin_typedarrays.go

@@ -643,7 +643,7 @@ func (r *Runtime) typedArrayProto_join(call FunctionCall) Value {
 	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
 		ta.viewedArrayBuf.ensureNotDetached()
 		s := call.Argument(0)
-		sep := stringEmpty
+		var sep valueString
 		if s != _undefined {
 			sep = s.toString()
 		} else {

+ 43 - 0
date_test.go

@@ -91,7 +91,43 @@ function compareArray(a, b) {
   }
   return true;
 }
+`
+
+const TESTLIBX = TESTLIB +
+	`function looksNative(fn) {
+		return /native code/.test(Function.prototype.toString.call(fn));
+	}
 
+	function deepEqual(a, b) {
+		if (typeof a === "object") {
+			if (typeof b === "object") {
+				if (a === b) {
+					return true;
+				}
+				if (Reflect.getPrototypeOf(a) !== Reflect.getPrototypeOf(b)) {
+					return false;
+				}
+				var keysA = Object.keys(a);
+				var keysB = Object.keys(b);
+				if (keysA.length !== keysB.length) {
+					return false;
+				}
+				if (!compareArray(keysA.sort(), keysB.sort())) {
+					return false;
+				}
+				for (var i = 0; i < keysA.length; i++) {
+					var key = keysA[i];
+					if (!deepEqual(a[key], b[key])) {
+						return false;
+					}
+				}
+				return true;
+			} else {
+				return false;
+			}
+		}
+		return assert._isSameValue(a, b);
+	}
 `
 
 func TestDateUTC(t *testing.T) {
@@ -396,3 +432,10 @@ func TestDateExport(t *testing.T) {
 		t.Fatalf("Invalid export type: %T", exp)
 	}
 }
+
+func TestDateToJSON(t *testing.T) {
+	const SCRIPT = `
+	Date.prototype.toJSON.call({ toISOString: function () { return 1; } })
+	`
+	testScript1(SCRIPT, intToValue(1), t)
+}

+ 16 - 0
object_test.go

@@ -109,6 +109,22 @@ func TestDefinePropertiesSymbol(t *testing.T) {
 	testScript1(SCRIPT, valueTrue, t)
 }
 
+func TestObjectAssign(t *testing.T) {
+	const SCRIPT = `
+	assert.sameValue(Object.assign({ b: 1 }, { get a() {
+          Object.defineProperty(this, "b", {
+            value: 3,
+            enumerable: false
+          });
+        }, b: 2 }).b, 1, "#1");
+
+	assert.sameValue(Object.assign({ b: 1 }, { get a() {
+          delete this.b;
+        }, b: 2 }).b, 1, "#2");
+	`
+	testScript1(TESTLIB+SCRIPT, _undefined, t)
+}
+
 func BenchmarkPut(b *testing.B) {
 	v := &Object{}
 

+ 9 - 0
regexp_test.go

@@ -350,6 +350,15 @@ func TestRegexpInit(t *testing.T) {
 	testScript1(SCRIPT, intToValue(0), t)
 }
 
+func TestRegexpToString(t *testing.T) {
+	const SCRIPT = `
+	RegExp.prototype.toString.call({
+	source: 'foo',
+    flags: 'bar'});
+	`
+	testScript1(SCRIPT, asciiString("/foo/bar"), t)
+}
+
 func BenchmarkRegexpSplitWithBackRef(b *testing.B) {
 	const SCRIPT = `
 	"aaaaaaaaaaaaaaaaaaaaaaaaa++bbbbbbbbbbbbbbbbbbbbbb+-ccccccccccccccccccccccc".split(/([+-])\1/)