46be10d188
Object.assign should not normalize JSGlobalProxy objects. Bug: chromium:1139769 Change-Id: Ie7e24f6498267966b7553b0c5994307f5b632b0d Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2485505 Reviewed-by: Igor Sheludko <ishell@chromium.org> Commit-Queue: Camillo Bruni <cbruni@chromium.org> Cr-Commit-Position: refs/heads/master@{#70713}
288 lines
8.9 KiB
JavaScript
288 lines
8.9 KiB
JavaScript
// Copyright 2014 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// Based on Mozilla Object.assign() tests
|
|
|
|
// Flags: --allow-natives-syntax
|
|
|
|
function checkDataProperty(object, propertyKey, value, writable, enumerable, configurable) {
|
|
var desc = Object.getOwnPropertyDescriptor(object, propertyKey);
|
|
assertFalse(desc === undefined);
|
|
assertTrue('value' in desc);
|
|
assertEquals(desc.value, value);
|
|
assertEquals(desc.writable, writable);
|
|
assertEquals(desc.enumerable, enumerable);
|
|
assertEquals(desc.configurable, configurable);
|
|
}
|
|
|
|
// 19.1.2.1 Object.assign ( target, ...sources )
|
|
assertEquals(Object.assign.length, 2);
|
|
|
|
// Basic functionality works with multiple sources
|
|
(function basicMultipleSources() {
|
|
var a = {};
|
|
var b = { bProp: 1 };
|
|
var c = { cProp: 2 };
|
|
Object.assign(a, b, c);
|
|
assertEquals(a, {
|
|
bProp: 1,
|
|
cProp: 2
|
|
});
|
|
})();
|
|
|
|
// Basic functionality works with symbols
|
|
(function basicSymbols() {
|
|
var a = {};
|
|
var b = { bProp: 1 };
|
|
var aSymbol = Symbol("aSymbol");
|
|
b[aSymbol] = 2;
|
|
Object.assign(a, b);
|
|
assertEquals(1, a.bProp);
|
|
assertEquals(2, a[aSymbol]);
|
|
})();
|
|
|
|
// Dies if target is null or undefined
|
|
assertThrows(function() { return Object.assign(null, null); }, TypeError);
|
|
assertThrows(function() { return Object.assign(null, {}); }, TypeError);
|
|
assertThrows(function() { return Object.assign(undefined); }, TypeError);
|
|
assertThrows(function() { return Object.assign(); }, TypeError);
|
|
|
|
// Calls ToObject for target
|
|
assertTrue(Object.assign(true, {}) instanceof Boolean);
|
|
assertTrue(Object.assign(1, {}) instanceof Number);
|
|
assertTrue(Object.assign("string", {}) instanceof String);
|
|
var o = {};
|
|
assertSame(Object.assign(o, {}), o);
|
|
|
|
// Only [[Enumerable]] properties are assigned to target
|
|
(function onlyEnumerablePropertiesAssigned() {
|
|
var source = Object.defineProperties({}, {
|
|
a: {value: 1, enumerable: true},
|
|
b: {value: 2, enumerable: false},
|
|
});
|
|
var target = Object.assign({}, source);
|
|
assertTrue("a" in target);
|
|
assertFalse("b" in target);
|
|
})();
|
|
|
|
// Properties are retrieved through Get()
|
|
// Properties are assigned through Put()
|
|
(function testPropertiesAssignedThroughPut() {
|
|
var setterCalled = false;
|
|
Object.assign({set a(v) { setterCalled = v }}, {a: true});
|
|
assertTrue(setterCalled);
|
|
})();
|
|
|
|
// Properties are retrieved through Get()
|
|
// Properties are assigned through Put(): Existing property attributes are not altered
|
|
(function propertiesAssignedExistingNotAltered() {
|
|
var source = {a: 1, b: 2, c: 3};
|
|
var target = {a: 0, b: 0, c: 0};
|
|
Object.defineProperty(target, "a", {enumerable: false});
|
|
Object.defineProperty(target, "b", {configurable: false});
|
|
Object.defineProperty(target, "c", {enumerable: false, configurable: false});
|
|
Object.assign(target, source);
|
|
checkDataProperty(target, "a", 1, true, false, true);
|
|
checkDataProperty(target, "b", 2, true, true, false);
|
|
checkDataProperty(target, "c", 3, true, false, false);
|
|
})();
|
|
|
|
// Properties are retrieved through Get()
|
|
// Properties are assigned through Put(): Throws TypeError if non-writable
|
|
(function propertiesAssignedTypeErrorNonWritable() {
|
|
var source = {a: 1};
|
|
var target = {a: 0};
|
|
Object.defineProperty(target, "a", {writable: false});
|
|
assertThrows(function() { return Object.assign(target, source); }, TypeError);
|
|
checkDataProperty(target, "a", 0, false, true, true);
|
|
})();
|
|
|
|
// Properties are retrieved through Get()
|
|
// Put() creates standard properties; Property attributes from source are
|
|
// ignored
|
|
(function createsStandardProperties() {
|
|
var source = {a: 1, b: 2, c: 3, get d() { return 4 }};
|
|
Object.defineProperty(source, "b", {writable: false});
|
|
Object.defineProperty(source, "c", {configurable: false});
|
|
var target = Object.assign({}, source);
|
|
checkDataProperty(target, "a", 1, true, true, true);
|
|
checkDataProperty(target, "b", 2, true, true, true);
|
|
checkDataProperty(target, "c", 3, true, true, true);
|
|
checkDataProperty(target, "d", 4, true, true, true);
|
|
})();
|
|
|
|
// Properties created during traversal are not copied
|
|
(function propertiesCreatedDuringTraversalNotCopied() {
|
|
var source = {get a() { this.b = 2 }};
|
|
var target = Object.assign({}, source);
|
|
assertTrue("a" in target);
|
|
assertFalse("b" in target);
|
|
})();
|
|
|
|
// String and Symbol valued properties are copied
|
|
(function testStringAndSymbolPropertiesCopied() {
|
|
var keyA = "str-prop";
|
|
var source = {"str-prop": 1};
|
|
var target = Object.assign({}, source);
|
|
checkDataProperty(target, keyA, 1, true, true, true);
|
|
})();
|
|
|
|
(function testExceptionsStopFirstException() {
|
|
var ErrorA = function ErrorA() {};
|
|
var ErrorB = function ErrorB() {};
|
|
var log = "";
|
|
var source = { b: 1, a: 1 };
|
|
var target = {
|
|
set a(v) { log += "a"; throw new ErrorA },
|
|
set b(v) { log += "b"; throw new ErrorB },
|
|
};
|
|
assertThrows(function() { return Object.assign(target, source); }, ErrorB);
|
|
assertEquals(log, "b");
|
|
})();
|
|
|
|
(function add_to_source() {
|
|
var target = {set k1(v) { source.k3 = 100; }};
|
|
var source = {k1:10};
|
|
Object.defineProperty(source, "k2",
|
|
{value: 20, enumerable: false, configurable: true});
|
|
Object.assign(target, source);
|
|
assertEquals(undefined, target.k2);
|
|
assertEquals(undefined, target.k3);
|
|
})();
|
|
|
|
(function reconfigure_enumerable_source() {
|
|
var target = {set k1(v) {
|
|
Object.defineProperty(source, "k2", {value: 20, enumerable: true});
|
|
}};
|
|
var source = {k1:10};
|
|
Object.defineProperty(source, "k2",
|
|
{value: 20, enumerable: false, configurable: true});
|
|
Object.assign(target, source);
|
|
assertEquals(20, target.k2);
|
|
})();
|
|
|
|
(function propagate_assign_failure() {
|
|
var target = {set k1(v) { throw "fail" }};
|
|
var source = {k1:10};
|
|
assertThrows(()=>Object.assign(target, source));
|
|
})();
|
|
|
|
(function propagate_read_failure() {
|
|
var target = {};
|
|
var source = {get k1() { throw "fail" }};
|
|
assertThrows(()=>Object.assign(target, source));
|
|
})();
|
|
|
|
(function strings_and_symbol_order1() {
|
|
// first order
|
|
var log = [];
|
|
|
|
var sym1 = Symbol("x"), sym2 = Symbol("y");
|
|
var source = {
|
|
get [sym1](){ log.push("get sym1"); },
|
|
get a() { log.push("get a"); },
|
|
get b() { log.push("get b"); },
|
|
get c() { log.push("get c"); },
|
|
get [sym2](){ log.push("get sym2"); },
|
|
};
|
|
|
|
Object.assign({}, source);
|
|
|
|
assertEquals(log, ["get a", "get b", "get c", "get sym1", "get sym2"]);
|
|
})();
|
|
|
|
(function strings_and_symbol_order2() {
|
|
// first order
|
|
var log = [];
|
|
|
|
var sym1 = Symbol("x"), sym2 = Symbol("y");
|
|
var source = {
|
|
get [sym1](){ log.push("get sym1"); },
|
|
get a() { log.push("get a"); },
|
|
get [sym2](){ log.push("get sym2"); },
|
|
get b() { log.push("get b"); },
|
|
get c() { log.push("get c"); },
|
|
};
|
|
|
|
Object.assign({}, source);
|
|
|
|
assertEquals(log, ["get a", "get b", "get c", "get sym1", "get sym2"]);
|
|
})();
|
|
|
|
|
|
(function strings_and_symbol_order3() {
|
|
// first order
|
|
var log = [];
|
|
|
|
var sym1 = Symbol("x"), sym2 = Symbol("y");
|
|
var source = {
|
|
get a() { log.push("get a"); },
|
|
get [sym1](){ log.push("get sym1"); },
|
|
get b() { log.push("get b"); },
|
|
get [sym2](){ log.push("get sym2"); },
|
|
get c() { log.push("get c"); },
|
|
};
|
|
|
|
Object.assign({}, source);
|
|
|
|
assertEquals(log, ["get a", "get b", "get c", "get sym1", "get sym2"]);
|
|
})();
|
|
|
|
(function proxy() {
|
|
const fast_source = { key1: "value1", key2: "value2"};
|
|
const slow_source = {__proto__:null};
|
|
for (let i = 0; i < 2000; i++) {
|
|
slow_source["key" + i] = i;
|
|
}
|
|
|
|
const empty_handler = {};
|
|
let target = {};
|
|
let proxy = new Proxy(target, empty_handler);
|
|
assertArrayEquals(Object.keys(target), []);
|
|
let result = Object.assign(proxy, fast_source);
|
|
%HeapObjectVerify(result);
|
|
assertArrayEquals(Object.keys(result), Object.keys(target));
|
|
assertArrayEquals(Object.keys(result), Object.keys(fast_source));
|
|
assertArrayEquals(Object.values(result), Object.values(fast_source));
|
|
|
|
target = {};
|
|
proxy = new Proxy(target, empty_handler);
|
|
assertArrayEquals(Object.keys(target), []);
|
|
result = Object.assign(proxy, slow_source);
|
|
%HeapObjectVerify(result);
|
|
assertEquals(Object.keys(result).length, Object.keys(target).length);
|
|
assertEquals(Object.keys(result).length, Object.keys(slow_source).length);
|
|
})();
|
|
|
|
(function global_object() {
|
|
let source = {
|
|
global1: "global1",
|
|
get global2() { return "global2" },
|
|
};
|
|
let result = Object.assign(globalThis, source);
|
|
%HeapObjectVerify(result);
|
|
assertTrue(result === globalThis);
|
|
assertTrue(result.global1 === source.global1);
|
|
assertTrue(result.global2 === source.global2);
|
|
|
|
let target = {};
|
|
result = Object.assign(target, globalThis);
|
|
%HeapObjectVerify(result);
|
|
assertTrue(result === target);
|
|
assertTrue(result.global1 === source.global1);
|
|
assertTrue(result.global2 === source.global2);
|
|
|
|
for (let i = 0; i < 2000; i++) {
|
|
source["property" + i] = i;
|
|
}
|
|
result = Object.assign(globalThis, source);
|
|
%HeapObjectVerify(result);
|
|
assertTrue(result === globalThis);
|
|
for (let i = 0; i < 2000; i++) {
|
|
const key = "property" + i;
|
|
assertEquals(result[key], i);
|
|
}
|
|
|
|
})();
|