Skip to content

Commit

Permalink
Implement setSpreadProperties
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolo-ribaudo committed Jan 12, 2021
1 parent 7c00ea2 commit bdb60c6
Show file tree
Hide file tree
Showing 18 changed files with 267 additions and 6 deletions.
1 change: 1 addition & 0 deletions packages/babel-core/src/config/validation/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ export const assumptionsNames = new Set<string>([
"setClassMethods",
"setComputedProperties",
"setPublicClassFields",
"setSpreadProperties",
"skipForOfIteratorClosing",
]);

Expand Down
10 changes: 4 additions & 6 deletions packages/babel-plugin-proposal-object-rest-spread/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,15 @@ const ZERO_REFS = (() => {
export default declare((api, opts) => {
api.assertVersion(7);

const { useBuiltIns = false, loose = false } = opts;

if (typeof loose !== "boolean") {
throw new Error(".loose must be a boolean, or undefined");
}
const { useBuiltIns = false } = opts;

const ignoreFunctionLength =
api.assumption("ignoreFunctionLength") ?? opts.loose;
const objectRestNoSymbols =
api.assumption("objectRestNoSymbols") ?? opts.loose;
const pureGetters = api.assumption("pureGetters") ?? opts.loose;
const setSpreadProperties =
api.assumption("setSpreadProperties") ?? opts.loose;

function getExtendsHelper(file) {
return useBuiltIns
Expand Down Expand Up @@ -561,7 +559,7 @@ export default declare((api, opts) => {
if (!hasSpread(path.node)) return;

let helper;
if (loose) {
if (setSpreadProperties) {
helper = getExtendsHelper(file);
} else {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
var x;
var y;
var z;

z = { x, ...y };

z = { x, w: { ...y } };
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
var x;
var y;
var z;
z = Object.assign({
x
}, y);
z = {
x,
w: Object.assign({}, y)
};
Original file line number Diff line number Diff line change
@@ -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]);
Original file line number Diff line number Diff line change
@@ -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' } } });
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
var a;
var b;
var c;
var d;
var x;
var y;
Object.assign(Object.assign(Object.assign({
x
}, y), {}, {
a
}, b), {}, {
c
});
Object.assign({}, Object.prototype);
Object.assign({}, {
foo: 'bar'
});
Object.assign(Object.assign({}, {
foo: 'bar'
}), {
bar: 'baz'
});
Object.assign({}, {
get foo() {
return 'foo';
}

});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const oldGOPDs = Object.getOwnPropertyDescriptors;
Object.getOwnPropertyDescriptors = null;

try {
({ ...{ a: 1 }, b: 1, ...{} });
} finally {
Object.getOwnPropertyDescriptors = oldGOPDs;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"use strict";
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);
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"plugins": [
["external-helpers", { "helperVersion": "7.100.0"}],
["proposal-object-rest-spread", { "useBuiltIns": true }]
],
"assumptions": {
"setSpreadProperties": true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
var x;
var y;
var z;

z = { x, ...y };

z = { x, w: { ...y } };
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
var x;
var y;
var z;
z = babelHelpers.extends({
x
}, y);
z = {
x,
w: babelHelpers.extends({}, y)
};
Original file line number Diff line number Diff line change
@@ -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]);
Original file line number Diff line number Diff line change
@@ -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' } } });
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
var a;
var b;
var c;
var d;
var x;
var y;
babelHelpers.extends(babelHelpers.extends(babelHelpers.extends({
x
}, y), {}, {
a
}, b), {}, {
c
});
babelHelpers.extends({}, Object.prototype);
babelHelpers.extends({}, {
foo: 'bar'
});
babelHelpers.extends(babelHelpers.extends({}, {
foo: 'bar'
}), {
bar: 'baz'
});
babelHelpers.extends({}, {
get foo() {
return 'foo';
}

});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const oldGOPDs = Object.getOwnPropertyDescriptors;
Object.getOwnPropertyDescriptors = null;

try {
({ ...{ a: 1 }, b: 1, ...{} });
} finally {
Object.getOwnPropertyDescriptors = oldGOPDs;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"use strict";
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);
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"plugins": [
["external-helpers", { "helperVersion": "7.100.0"}],
"proposal-object-rest-spread"
],
"assumptions": {
"setSpreadProperties": true
}
}

0 comments on commit bdb60c6

Please sign in to comment.