tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

assign.js (10476B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 function checkDataProperty(object, propertyKey, value, writable, enumerable, configurable) {
      6    var desc = Object.getOwnPropertyDescriptor(object, propertyKey);
      7    assertEq(desc === undefined, false);
      8    assertEq('value' in desc, true);
      9    assertEq(desc.value, value);
     10    assertEq(desc.writable, writable);
     11    assertEq(desc.enumerable, enumerable);
     12    assertEq(desc.configurable, configurable);
     13 }
     14 
     15 /* 19.1.2.1  Object.assign ( target, ...sources ) */
     16 assertEq(Object.assign.length, 2);
     17 
     18 // Basic functionality works with multiple sources
     19 function basicMultipleSources() {
     20    var a = {};
     21    var b = { bProp : 7 };
     22    var c = { cProp : 8 };
     23    Object.assign(a, b, c);
     24    assertEq(a.bProp, 7);
     25    assertEq(a.cProp, 8);
     26 }
     27 basicMultipleSources();
     28 
     29 // Basic functionality works with symbols (Bug 1052358)
     30 function basicSymbols() {
     31    var a = {};
     32    var b = { bProp : 7 };
     33    var aSymbol = Symbol("aSymbol");
     34    b[aSymbol] = 22;
     35    Object.assign(a, b);
     36    assertEq(a.bProp, 7);
     37    assertEq(a[aSymbol], 22);
     38 }
     39 basicSymbols();
     40 
     41 // Calls ToObject() for target, skips null/undefined sources, uses
     42 // ToObject(source) otherwise.
     43 function testToObject() {
     44    assertThrowsInstanceOf(() => Object.assign(null, null), TypeError);
     45    assertThrowsInstanceOf(() => Object.assign(), TypeError);
     46    assertThrowsInstanceOf(() => Object.assign(null, {}), TypeError);
     47    assertEq(Object.assign({}, null) instanceof Object, true);
     48    assertEq(Object.assign({}, undefined) instanceof Object, true);
     49 
     50    // Technically an embedding could have this as extension acting differently
     51    // from ours, so a feature-test is inadequate.  We can move this subtest
     52    // into extensions/ if that ever matters.
     53    if (typeof createIsHTMLDDA === "function") {
     54        var falsyObject = createIsHTMLDDA();
     55        falsyObject.foo = 7;
     56 
     57        var obj = Object.assign({}, falsyObject);
     58        assertEq(obj instanceof Object, true);
     59        assertEq(obj.foo, 7);
     60    }
     61 
     62    assertEq(Object.assign(true, {}) instanceof Boolean, true);
     63    assertEq(Object.assign(1, {}) instanceof Number, true);
     64    assertEq(Object.assign("string", {}) instanceof String, true);
     65    var o = {};
     66    assertEq(Object.assign(o, {}), o);
     67 }
     68 testToObject();
     69 
     70 // Invokes [[OwnPropertyKeys]] on ToObject(source)
     71 function testOwnPropertyKeys() {
     72    assertThrowsInstanceOf(() => Object.assign(null, new Proxy({}, {
     73        getOwnPropertyNames: () => { throw new Error("not called"); }
     74    })), TypeError);
     75 
     76    var ownKeysCalled = false;
     77    Object.assign({}, new Proxy({}, {
     78        ownKeys: function() {
     79            ownKeysCalled = true;
     80            return [];
     81        }
     82    }));
     83    assertEq(ownKeysCalled, true);
     84 };
     85 testOwnPropertyKeys();
     86 
     87 // Ensure correct property traversal
     88 function correctPropertyTraversal() {
     89    var log = "";
     90    var source = new Proxy({a: 1, b: 2}, {
     91        ownKeys: () => ["b", "c", "a"],
     92        getOwnPropertyDescriptor: function(t, pk) {
     93            log += "#" + pk;
     94            return Object.getOwnPropertyDescriptor(t, pk);
     95        },
     96        get: function(t, pk, r) {
     97            log += "-" + pk;
     98            return t[pk];
     99        },
    100    });
    101    Object.assign({}, source);
    102    assertEq(log, "#b-b#c#a-a");
    103 }
    104 correctPropertyTraversal();
    105 
    106 // Only [[Enumerable]] properties are assigned to target
    107 function onlyEnumerablePropertiesAssigned() {
    108    var source = Object.defineProperties({}, {
    109        a: {value: 1, enumerable: true},
    110        b: {value: 2, enumerable: false},
    111    });
    112    var target = Object.assign({}, source);
    113    assertEq("a" in target, true);
    114    assertEq("b" in target, false);
    115 }
    116 onlyEnumerablePropertiesAssigned();
    117 
    118 
    119 // Enumerability is decided on-time, not before main loop (1)
    120 function testEnumerabilityDeterminedInLoop1()
    121 {
    122    var getterCalled = false;
    123    var sourceTarget = {
    124        get a() { getterCalled = true },
    125        get b() { Object.defineProperty(sourceTarget, "a", {enumerable: false}) },
    126    };
    127    var source = new Proxy(sourceTarget, { ownKeys: () => ["b", "a"] });
    128    Object.assign({}, source);
    129    assertEq(getterCalled, false);
    130 }
    131 testEnumerabilityDeterminedInLoop1();
    132 
    133 // Enumerability is decided on-time, not before main loop (2)
    134 function testEnumerabilityDeterminedInLoop2()
    135 {
    136    var getterCalled = false;
    137    var sourceTarget = {
    138        get a() { getterCalled = true },
    139        get b() { Object.defineProperty(sourceTarget, "a", {enumerable: true}) },
    140    };
    141    var source = new Proxy(sourceTarget, {
    142        ownKeys: () => ["b", "a"]
    143    });
    144    Object.defineProperty(sourceTarget, "a", {enumerable: false});
    145    Object.assign({}, source);
    146    assertEq(getterCalled, true);
    147 }
    148 testEnumerabilityDeterminedInLoop2();
    149 
    150 // Properties are retrieved through Get() and assigned onto
    151 // the target as data properties, not in any sense cloned over as descriptors
    152 function testPropertiesRetrievedThroughGet() {
    153    var getterCalled = false;
    154    Object.assign({}, {get a() { getterCalled = true }});
    155    assertEq(getterCalled, true);
    156 }
    157 testPropertiesRetrievedThroughGet();
    158 
    159 // Properties are retrieved through Get()
    160 // Properties are assigned through Put()
    161 function testPropertiesAssignedThroughPut() {
    162    var setterCalled = false;
    163    Object.assign({set a(v) { setterCalled = v }}, {a: true});
    164    assertEq(setterCalled, true);
    165 }
    166 testPropertiesAssignedThroughPut();
    167 
    168 // Properties are retrieved through Get()
    169 // Properties are assigned through Put(): Existing property attributes are not altered
    170 function propertiesAssignedExistingNotAltered() {
    171    var source = {a: 1, b: 2, c: 3};
    172    var target = {a: 0, b: 0, c: 0};
    173    Object.defineProperty(target, "a", {enumerable: false});
    174    Object.defineProperty(target, "b", {configurable: false});
    175    Object.defineProperty(target, "c", {enumerable: false, configurable: false});
    176    Object.assign(target, source);
    177    checkDataProperty(target, "a", 1, true, false, true);
    178    checkDataProperty(target, "b", 2, true, true, false);
    179    checkDataProperty(target, "c", 3, true, false, false);
    180 }
    181 propertiesAssignedExistingNotAltered();
    182 
    183 // Properties are retrieved through Get()
    184 // Properties are assigned through Put(): Throws TypeError if non-writable
    185 function propertiesAssignedTypeErrorNonWritable() {
    186    var source = {a: 1};
    187    var target = {a: 0};
    188    Object.defineProperty(target, "a", {writable: false});
    189    assertThrowsInstanceOf(() => Object.assign(target, source), TypeError);
    190    checkDataProperty(target, "a", 0, false, true, true);
    191 }
    192 propertiesAssignedTypeErrorNonWritable();
    193 
    194 // Properties are retrieved through Get()
    195 // Put() creates standard properties; Property attributes from source are ignored
    196 function createsStandardProperties() {
    197    var source = {a: 1, b: 2, c: 3, get d() { return 4 }};
    198    Object.defineProperty(source, "b", {writable: false});
    199    Object.defineProperty(source, "c", {configurable: false});
    200    var target = Object.assign({}, source);
    201    checkDataProperty(target, "a", 1, true, true, true);
    202    checkDataProperty(target, "b", 2, true, true, true);
    203    checkDataProperty(target, "c", 3, true, true, true);
    204    checkDataProperty(target, "d", 4, true, true, true);
    205 }
    206 createsStandardProperties();
    207 
    208 // Properties created during traversal are not copied
    209 function propertiesCreatedDuringTraversalNotCopied() {
    210    var source = {get a() { this.b = 2 }};
    211    var target = Object.assign({}, source);
    212    assertEq("a" in target, true);
    213    assertEq("b" in target, false);
    214 }
    215 propertiesCreatedDuringTraversalNotCopied();
    216 
    217 // Properties deleted during traversal are not copied
    218 function testDeletePropertiesNotCopied() {
    219    var source = new Proxy({
    220        get a() { delete this.b },
    221        b: 2,
    222    }, {
    223        getOwnPropertyNames: () => ["a", "b"]
    224    });
    225    var target = Object.assign({}, source);
    226    assertEq("a" in target, true);
    227    assertEq("b" in target, false);
    228 }
    229 testDeletePropertiesNotCopied();
    230 
    231 function testDeletionExposingShadowedProperty()
    232 {
    233    var srcProto = { b: 42 };
    234    var src =
    235        Object.create(srcProto,
    236                        { a: { enumerable: true, get: function() { delete this.b; } },
    237                          b: { value: 2, configurable: true, enumerable: true } });
    238    var source = new Proxy(src, { getOwnPropertyNames: () => ["a", "b"] });
    239    var target = Object.assign({}, source);
    240    assertEq("a" in target, true);
    241    assertEq("b" in target, false);
    242 }
    243 testDeletionExposingShadowedProperty();
    244 
    245 // Properties first deleted and then recreated during traversal are copied (1)
    246 function testDeletedAndRecreatedPropertiesCopied1() {
    247    var source = new Proxy({
    248        get a() { delete this.c },
    249        get b() { this.c = 4 },
    250        c: 3,
    251    }, {
    252        getOwnPropertyNames: () => ["a", "b", "c"]
    253    });
    254    var target = Object.assign({}, source);
    255    assertEq("a" in target, true);
    256    assertEq("b" in target, true);
    257    assertEq("c" in target, true);
    258    checkDataProperty(target, "c", 4, true, true, true);
    259 }
    260 testDeletedAndRecreatedPropertiesCopied1();
    261 
    262 // Properties first deleted and then recreated during traversal are copied (2)
    263 function testDeletedAndRecreatedPropertiesCopied2() {
    264    var source = new Proxy({
    265        get a() { delete this.c },
    266        get b() { this.c = 4 },
    267        c: 3,
    268    }, {
    269        ownKeys: () => ["a", "c", "b"]
    270    });
    271    var target = Object.assign({}, source);
    272    assertEq("a" in target, true);
    273    assertEq("b" in target, true);
    274    assertEq("c" in target, false);
    275 }
    276 testDeletedAndRecreatedPropertiesCopied2();
    277 
    278 // String and Symbol valued properties are copied
    279 function testStringAndSymbolPropertiesCopied() {
    280    var keyA = "str-prop";
    281    var source = {"str-prop": 1};
    282    var target = Object.assign({}, source);
    283    checkDataProperty(target, keyA, 1, true, true, true);
    284 }
    285 testStringAndSymbolPropertiesCopied();
    286 
    287 // Intermediate exceptions stop traversal and throw exception
    288 function testExceptionsDoNotStopFirstReported1() {
    289    var TestError = function TestError() {};
    290    var source = new Proxy({}, {
    291        getOwnPropertyDescriptor: function(t, pk) {
    292            assertEq(pk, "b");
    293            throw new TestError();
    294        },
    295        ownKeys: () => ["b", "a"]
    296    });
    297    assertThrowsInstanceOf(() => Object.assign({}, source), TestError);
    298 }
    299 testExceptionsDoNotStopFirstReported1();
    300 
    301 
    302 if (typeof reportCompare == "function")
    303    reportCompare(true, true);