Use single object spread call in loose mode (#11520)
This commit is contained in:
parent
b8a6145e50
commit
a080a1d345
@ -579,6 +579,16 @@ export default declare((api, opts) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In loose mode, we don't want to make multiple calls. We're assuming
|
||||||
|
// that the spread objects either don't use getters, or that the
|
||||||
|
// getters are pure and don't depend on the order of evaluation.
|
||||||
|
if (loose) {
|
||||||
|
if (hadProps) {
|
||||||
|
exp.arguments.push(obj);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
exp = t.callExpression(t.cloneNode(helper), [
|
exp = t.callExpression(t.cloneNode(helper), [
|
||||||
exp,
|
exp,
|
||||||
// If we have static props, we need to insert an empty object
|
// If we have static props, we need to insert an empty object
|
||||||
|
|||||||
@ -0,0 +1,9 @@
|
|||||||
|
var log = [];
|
||||||
|
|
||||||
|
var a = {
|
||||||
|
...{ get foo() { log.push(1); } },
|
||||||
|
get bar() { log.push(2); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Loose mode uses regular Get, not GetOwnProperty.
|
||||||
|
expect(log).toEqual([1, 2]);
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
var a;
|
||||||
|
var b;
|
||||||
|
var c;
|
||||||
|
var d;
|
||||||
|
var x;
|
||||||
|
var y;
|
||||||
|
|
||||||
|
({ x, ...y, a, ...b, c });
|
||||||
|
|
||||||
|
({ ...Object.prototype });
|
||||||
|
|
||||||
|
({ ...{ foo: 'bar' } });
|
||||||
|
|
||||||
|
({ ...{ foo: 'bar' }, ...{ bar: 'baz' } });
|
||||||
|
|
||||||
|
({ ...{ get foo () { return 'foo' } } });
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
var a;
|
||||||
|
var b;
|
||||||
|
var c;
|
||||||
|
var d;
|
||||||
|
var x;
|
||||||
|
var y;
|
||||||
|
Object.assign({
|
||||||
|
x
|
||||||
|
}, y, {
|
||||||
|
a
|
||||||
|
}, b, {
|
||||||
|
c
|
||||||
|
});
|
||||||
|
Object.assign({}, Object.prototype);
|
||||||
|
Object.assign({}, {
|
||||||
|
foo: 'bar'
|
||||||
|
});
|
||||||
|
Object.assign({}, {
|
||||||
|
foo: 'bar'
|
||||||
|
}, {
|
||||||
|
bar: 'baz'
|
||||||
|
});
|
||||||
|
Object.assign({}, {
|
||||||
|
get foo() {
|
||||||
|
return 'foo';
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
const oldGOPDs = Object.getOwnPropertyDescriptors;
|
||||||
|
Object.getOwnPropertyDescriptors = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
({ ...{ a: 1 }, b: 1, ...{} });
|
||||||
|
} finally {
|
||||||
|
Object.getOwnPropertyDescriptors = oldGOPDs;
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
"use strict";
|
||||||
|
const NOSET = `NOSET${__filename}`;
|
||||||
|
const NOWRITE = `NOWRITE${__filename}`;
|
||||||
|
|
||||||
|
Object.defineProperty(Object.prototype, NOSET, {
|
||||||
|
get(value) {
|
||||||
|
// noop
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(Object.prototype, NOWRITE, {
|
||||||
|
writable: false,
|
||||||
|
value: 'abc',
|
||||||
|
});
|
||||||
|
|
||||||
|
const obj = { [NOSET]: 123 };
|
||||||
|
// this won't work as expected if transformed as Object.assign (or equivalent)
|
||||||
|
// because those trigger object setters (spread don't)
|
||||||
|
expect(() => {
|
||||||
|
const objSpread = { ...obj };
|
||||||
|
}).toThrow();
|
||||||
|
|
||||||
|
const obj2 = { [NOWRITE]: 456 };
|
||||||
|
// this throws `TypeError: Cannot assign to read only property 'NOWRITE'`
|
||||||
|
// if transformed as Object.assign (or equivalent) because those use *assignment* for creating properties
|
||||||
|
// (spread defines them)
|
||||||
|
expect(() => {
|
||||||
|
const obj2Spread = { ...obj2 };
|
||||||
|
}).toThrow();
|
||||||
|
|
||||||
|
const KEY = Symbol('key');
|
||||||
|
const obj3Spread = { ...{ get foo () { return 'bar' } }, [KEY]: 'symbol' };
|
||||||
|
expect(Object.getOwnPropertyDescriptor(obj3Spread, 'foo').value).toBe('bar');
|
||||||
|
expect(Object.getOwnPropertyDescriptor(obj3Spread, KEY).value).toBe('symbol');
|
||||||
|
|
||||||
|
const obj4Spread = { ...Object.prototype };
|
||||||
|
expect(Object.getOwnPropertyDescriptor(obj4Spread, 'hasOwnProperty')).toBeUndefined();
|
||||||
|
|
||||||
|
expect(() => ({ ...null, ...undefined })).not.toThrow();
|
||||||
|
|
||||||
|
const o = Object.create(null);
|
||||||
|
o.a = 'foo';
|
||||||
|
o.__proto__ = [];
|
||||||
|
const o2 = { ...o };
|
||||||
|
// Loose will do o2.__proto__ = []
|
||||||
|
expect(Array.isArray(Object.getPrototypeOf(o2))).toBe(true);
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
var k = { a: 1, b: 2 };
|
||||||
|
var o = { a: 3, ...k, b: k.a++ };
|
||||||
|
// Loose will evaluate the static `b: k.a++` before spreading `...k`.
|
||||||
|
// It should be {a: 1, b: 1}
|
||||||
|
expect(o).toEqual({a: 2, b: 1});
|
||||||
|
|
||||||
|
var k = { a: 1, get b() { l = { z: 9 }; return 2; } };
|
||||||
|
var l = { c: 3 };
|
||||||
|
var o = { ...k, ...l };
|
||||||
|
// Loose will evaluate the `l` before spreading `...k`.
|
||||||
|
// It should be {a: 1, b: 2, z: 9}
|
||||||
|
expect(o).toEqual({ a: 1, b: 2, c: 3 });
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
var k = { a: 1, b: 2 };
|
||||||
|
var o = { a: 3, ...k, b: k.a++ };
|
||||||
|
|
||||||
|
var pureA = {};
|
||||||
|
var pureB = {};
|
||||||
|
var pureC = {};
|
||||||
|
var pureD = {};
|
||||||
|
var pureE = {};
|
||||||
|
|
||||||
|
function impureFunc() {
|
||||||
|
console.log('hello')
|
||||||
|
}
|
||||||
|
|
||||||
|
var output = { ...pureA, get foo() {}, get bar() {}, ...pureB, ...pureC, ...impureFunc(), ...pureD, pureD }
|
||||||
|
|
||||||
|
var simpleOutput = { ...pureA, test: '1', ...pureB, }
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
var k = {
|
||||||
|
a: 1,
|
||||||
|
b: 2
|
||||||
|
};
|
||||||
|
var o = Object.assign({
|
||||||
|
a: 3
|
||||||
|
}, k, {
|
||||||
|
b: k.a++
|
||||||
|
});
|
||||||
|
var pureA = {};
|
||||||
|
var pureB = {};
|
||||||
|
var pureC = {};
|
||||||
|
var pureD = {};
|
||||||
|
var pureE = {};
|
||||||
|
|
||||||
|
function impureFunc() {
|
||||||
|
console.log('hello');
|
||||||
|
}
|
||||||
|
|
||||||
|
var output = Object.assign({}, pureA, {
|
||||||
|
get foo() {},
|
||||||
|
|
||||||
|
get bar() {}
|
||||||
|
|
||||||
|
}, pureB, pureC, impureFunc(), pureD, {
|
||||||
|
pureD
|
||||||
|
});
|
||||||
|
var simpleOutput = Object.assign({}, pureA, {
|
||||||
|
test: '1'
|
||||||
|
}, pureB);
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
expect([...'']).toHaveLength(0);
|
||||||
|
expect([...'abc']).toHaveLength(3);
|
||||||
|
expect([...'def']).toMatchObject(['d','e','f']);
|
||||||
@ -0,0 +1 @@
|
|||||||
|
var z = { ...x };
|
||||||
@ -0,0 +1 @@
|
|||||||
|
var z = Object.assign({}, x);
|
||||||
@ -5,5 +5,3 @@ var z;
|
|||||||
z = { x, ...y };
|
z = { x, ...y };
|
||||||
|
|
||||||
z = { x, w: { ...y } };
|
z = { x, w: { ...y } };
|
||||||
|
|
||||||
const { q, ...rest } = z;
|
|
||||||
@ -1,5 +1,3 @@
|
|||||||
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
||||||
|
|
||||||
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
||||||
|
|
||||||
var x;
|
var x;
|
||||||
@ -12,5 +10,3 @@ z = {
|
|||||||
x,
|
x,
|
||||||
w: _extends({}, y)
|
w: _extends({}, y)
|
||||||
};
|
};
|
||||||
|
|
||||||
const rest = _objectWithoutPropertiesLoose(z, ["q"]);
|
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
var log = [];
|
||||||
|
|
||||||
|
var a = {
|
||||||
|
...{ get foo() { log.push(1); } },
|
||||||
|
get bar() { log.push(2); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Loose mode uses regular Get, not GetOwnProperty.
|
||||||
|
expect(log).toEqual([1, 2]);
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
var a;
|
||||||
|
var b;
|
||||||
|
var c;
|
||||||
|
var d;
|
||||||
|
var x;
|
||||||
|
var y;
|
||||||
|
|
||||||
|
({ x, ...y, a, ...b, c });
|
||||||
|
|
||||||
|
({ ...Object.prototype });
|
||||||
|
|
||||||
|
({ ...{ foo: 'bar' } });
|
||||||
|
|
||||||
|
({ ...{ foo: 'bar' }, ...{ bar: 'baz' } });
|
||||||
|
|
||||||
|
({ ...{ get foo () { return 'foo' } } });
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
||||||
|
|
||||||
|
var a;
|
||||||
|
var b;
|
||||||
|
var c;
|
||||||
|
var d;
|
||||||
|
var x;
|
||||||
|
var y;
|
||||||
|
|
||||||
|
_extends({
|
||||||
|
x
|
||||||
|
}, y, {
|
||||||
|
a
|
||||||
|
}, b, {
|
||||||
|
c
|
||||||
|
});
|
||||||
|
|
||||||
|
_extends({}, Object.prototype);
|
||||||
|
|
||||||
|
_extends({}, {
|
||||||
|
foo: 'bar'
|
||||||
|
});
|
||||||
|
|
||||||
|
_extends({}, {
|
||||||
|
foo: 'bar'
|
||||||
|
}, {
|
||||||
|
bar: 'baz'
|
||||||
|
});
|
||||||
|
|
||||||
|
_extends({}, {
|
||||||
|
get foo() {
|
||||||
|
return 'foo';
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
const oldGOPDs = Object.getOwnPropertyDescriptors;
|
||||||
|
Object.getOwnPropertyDescriptors = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
({ ...{ a: 1 }, b: 1, ...{} });
|
||||||
|
} finally {
|
||||||
|
Object.getOwnPropertyDescriptors = oldGOPDs;
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
"use strict";
|
||||||
|
const NOSET = `NOSET${__filename}`;
|
||||||
|
const NOWRITE = `NOWRITE${__filename}`;
|
||||||
|
|
||||||
|
Object.defineProperty(Object.prototype, NOSET, {
|
||||||
|
get(value) {
|
||||||
|
// noop
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(Object.prototype, NOWRITE, {
|
||||||
|
writable: false,
|
||||||
|
value: 'abc',
|
||||||
|
});
|
||||||
|
|
||||||
|
const obj = { [NOSET]: 123 };
|
||||||
|
// this won't work as expected if transformed as Object.assign (or equivalent)
|
||||||
|
// because those trigger object setters (spread don't)
|
||||||
|
expect(() => {
|
||||||
|
const objSpread = { ...obj };
|
||||||
|
}).toThrow();
|
||||||
|
|
||||||
|
const obj2 = { [NOWRITE]: 456 };
|
||||||
|
// this throws `TypeError: Cannot assign to read only property 'NOWRITE'`
|
||||||
|
// if transformed as Object.assign (or equivalent) because those use *assignment* for creating properties
|
||||||
|
// (spread defines them)
|
||||||
|
expect(() => {
|
||||||
|
const obj2Spread = { ...obj2 };
|
||||||
|
}).toThrow();
|
||||||
|
|
||||||
|
const KEY = Symbol('key');
|
||||||
|
const obj3Spread = { ...{ get foo () { return 'bar' } }, [KEY]: 'symbol' };
|
||||||
|
expect(Object.getOwnPropertyDescriptor(obj3Spread, 'foo').value).toBe('bar');
|
||||||
|
expect(Object.getOwnPropertyDescriptor(obj3Spread, KEY).value).toBe('symbol');
|
||||||
|
|
||||||
|
const obj4Spread = { ...Object.prototype };
|
||||||
|
expect(Object.getOwnPropertyDescriptor(obj4Spread, 'hasOwnProperty')).toBeUndefined();
|
||||||
|
|
||||||
|
expect(() => ({ ...null, ...undefined })).not.toThrow();
|
||||||
|
|
||||||
|
const o = Object.create(null);
|
||||||
|
o.a = 'foo';
|
||||||
|
o.__proto__ = [];
|
||||||
|
const o2 = { ...o };
|
||||||
|
// Loose will do o2.__proto__ = []
|
||||||
|
expect(Array.isArray(Object.getPrototypeOf(o2))).toBe(true);
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
["proposal-object-rest-spread", { "loose": true }]
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
var k = { a: 1, b: 2 };
|
||||||
|
var o = { a: 3, ...k, b: k.a++ };
|
||||||
|
// Loose will evaluate the static `b: k.a++` before spreading `...k`.
|
||||||
|
// It should be {a: 1, b: 1}
|
||||||
|
expect(o).toEqual({a: 2, b: 1});
|
||||||
|
|
||||||
|
var k = { a: 1, get b() { l = { z: 9 }; return 2; } };
|
||||||
|
var l = { c: 3 };
|
||||||
|
var o = { ...k, ...l };
|
||||||
|
// Loose will evaluate the `l` before spreading `...k`.
|
||||||
|
// It should be {a: 1, b: 2, z: 9}
|
||||||
|
expect(o).toEqual({ a: 1, b: 2, c: 3 });
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
var k = { a: 1, b: 2 };
|
||||||
|
var o = { a: 3, ...k, b: k.a++ };
|
||||||
|
|
||||||
|
var pureA = {};
|
||||||
|
var pureB = {};
|
||||||
|
var pureC = {};
|
||||||
|
var pureD = {};
|
||||||
|
var pureE = {};
|
||||||
|
|
||||||
|
function impureFunc() {
|
||||||
|
console.log('hello')
|
||||||
|
}
|
||||||
|
|
||||||
|
var output = { ...pureA, get foo() {}, get bar() {}, ...pureB, ...pureC, ...impureFunc(), ...pureD, pureD }
|
||||||
|
|
||||||
|
var simpleOutput = { ...pureA, test: '1', ...pureB, }
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
||||||
|
|
||||||
|
var k = {
|
||||||
|
a: 1,
|
||||||
|
b: 2
|
||||||
|
};
|
||||||
|
|
||||||
|
var o = _extends({
|
||||||
|
a: 3
|
||||||
|
}, k, {
|
||||||
|
b: k.a++
|
||||||
|
});
|
||||||
|
|
||||||
|
var pureA = {};
|
||||||
|
var pureB = {};
|
||||||
|
var pureC = {};
|
||||||
|
var pureD = {};
|
||||||
|
var pureE = {};
|
||||||
|
|
||||||
|
function impureFunc() {
|
||||||
|
console.log('hello');
|
||||||
|
}
|
||||||
|
|
||||||
|
var output = _extends({}, pureA, {
|
||||||
|
get foo() {},
|
||||||
|
|
||||||
|
get bar() {}
|
||||||
|
|
||||||
|
}, pureB, pureC, impureFunc(), pureD, {
|
||||||
|
pureD
|
||||||
|
});
|
||||||
|
|
||||||
|
var simpleOutput = _extends({}, pureA, {
|
||||||
|
test: '1'
|
||||||
|
}, pureB);
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
expect([...'']).toHaveLength(0);
|
||||||
|
expect([...'abc']).toHaveLength(3);
|
||||||
|
expect([...'def']).toMatchObject(['d','e','f']);
|
||||||
@ -0,0 +1 @@
|
|||||||
|
var z = { ...x };
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
||||||
|
|
||||||
|
var z = _extends({}, x);
|
||||||
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"plugins": [["proposal-object-rest-spread", { "loose": true }]]
|
|
||||||
}
|
|
||||||
@ -1,6 +1,9 @@
|
|||||||
const oldGOPDs = Object.getOwnPropertyDescriptors;
|
const oldGOPDs = Object.getOwnPropertyDescriptors;
|
||||||
Object.getOwnPropertyDescriptors = null;
|
Object.getOwnPropertyDescriptors = null;
|
||||||
|
|
||||||
({ ...{ a: 1 }, b: 1, ...{} });
|
try {
|
||||||
|
({ ...{ a: 1 }, b: 1, ...{} });
|
||||||
|
} finally {
|
||||||
|
Object.getOwnPropertyDescriptors = oldGOPDs;
|
||||||
|
}
|
||||||
|
|
||||||
Object.getOwnPropertyDescriptors = oldGOPDs;
|
|
||||||
|
|||||||
@ -1,20 +1,24 @@
|
|||||||
Object.defineProperty(Object.prototype, 'NOSET', {
|
"use strict";
|
||||||
|
const NOSET = `NOSET${__filename}`;
|
||||||
|
const NOWRITE = `NOWRITE${__filename}`;
|
||||||
|
|
||||||
|
Object.defineProperty(Object.prototype, NOSET, {
|
||||||
set(value) {
|
set(value) {
|
||||||
// noop
|
// noop
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.defineProperty(Object.prototype, 'NOWRITE', {
|
Object.defineProperty(Object.prototype, NOWRITE, {
|
||||||
writable: false,
|
writable: false,
|
||||||
value: 'abc',
|
value: 'abc',
|
||||||
});
|
});
|
||||||
|
|
||||||
const obj = { NOSET: 123 };
|
const obj = { [NOSET]: 123 };
|
||||||
// this wouldn't work as expected if transformed as Object.assign (or equivalent)
|
// this wouldn't work as expected if transformed as Object.assign (or equivalent)
|
||||||
// because those trigger object setters (spread don't)
|
// because those trigger object setters (spread don't)
|
||||||
const objSpread = { ...obj };
|
const objSpread = { ...obj };
|
||||||
|
|
||||||
const obj2 = { NOSET: 123, NOWRITE: 456 };
|
const obj2 = { NOSET: 123, [NOWRITE]: 456 };
|
||||||
// this line would throw `TypeError: Cannot assign to read only property 'NOWRITE'`
|
// this line would throw `TypeError: Cannot assign to read only property 'NOWRITE'`
|
||||||
// if transformed as Object.assign (or equivalent) because those use *assignment* for creating properties
|
// if transformed as Object.assign (or equivalent) because those use *assignment* for creating properties
|
||||||
// (spread defines them)
|
// (spread defines them)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user