Fix evaluation order with object spread, 2 (#11471)
This commit is contained in:
parent
83d365acb6
commit
c1d65d8842
@ -281,6 +281,7 @@ export default declare((api, opts) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// adapted from transform-destructuring/src/index.js#pushObjectRest
|
// adapted from transform-destructuring/src/index.js#pushObjectRest
|
||||||
// const { a, ...b } = c;
|
// const { a, ...b } = c;
|
||||||
VariableDeclarator(path, file) {
|
VariableDeclarator(path, file) {
|
||||||
@ -385,6 +386,7 @@ export default declare((api, opts) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// taken from transform-destructuring/src/index.js#visitor
|
// taken from transform-destructuring/src/index.js#visitor
|
||||||
// export var { a, ...b } = c;
|
// export var { a, ...b } = c;
|
||||||
ExportNamedDeclaration(path) {
|
ExportNamedDeclaration(path) {
|
||||||
@ -410,11 +412,13 @@ export default declare((api, opts) => {
|
|||||||
path.replaceWith(declaration.node);
|
path.replaceWith(declaration.node);
|
||||||
path.insertAfter(t.exportNamedDeclaration(null, specifiers));
|
path.insertAfter(t.exportNamedDeclaration(null, specifiers));
|
||||||
},
|
},
|
||||||
|
|
||||||
// try {} catch ({a, ...b}) {}
|
// try {} catch ({a, ...b}) {}
|
||||||
CatchClause(path) {
|
CatchClause(path) {
|
||||||
const paramPath = path.get("param");
|
const paramPath = path.get("param");
|
||||||
replaceRestElement(paramPath.parentPath, paramPath);
|
replaceRestElement(paramPath.parentPath, paramPath);
|
||||||
},
|
},
|
||||||
|
|
||||||
// ({a, ...b} = c);
|
// ({a, ...b} = c);
|
||||||
AssignmentExpression(path, file) {
|
AssignmentExpression(path, file) {
|
||||||
const leftPath = path.get("left");
|
const leftPath = path.get("left");
|
||||||
@ -457,6 +461,7 @@ export default declare((api, opts) => {
|
|||||||
path.replaceWithMultiple(nodes);
|
path.replaceWithMultiple(nodes);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// taken from transform-destructuring/src/index.js#visitor
|
// taken from transform-destructuring/src/index.js#visitor
|
||||||
ForXStatement(path) {
|
ForXStatement(path) {
|
||||||
const { node, scope } = path;
|
const { node, scope } = path;
|
||||||
@ -506,6 +511,7 @@ export default declare((api, opts) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// [{a, ...b}] = c;
|
// [{a, ...b}] = c;
|
||||||
ArrayPattern(path) {
|
ArrayPattern(path) {
|
||||||
const objectPatterns = [];
|
const objectPatterns = [];
|
||||||
@ -537,34 +543,11 @@ export default declare((api, opts) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// var a = { ...b, ...c }
|
// var a = { ...b, ...c }
|
||||||
ObjectExpression(path, file) {
|
ObjectExpression(path, file) {
|
||||||
if (!hasSpread(path.node)) return;
|
if (!hasSpread(path.node)) return;
|
||||||
|
|
||||||
const { scope } = path;
|
|
||||||
|
|
||||||
// a non-SpreadElement and SpreadElement striped array
|
|
||||||
const args = [];
|
|
||||||
let props = [];
|
|
||||||
|
|
||||||
function push() {
|
|
||||||
args.push(t.objectExpression(props));
|
|
||||||
props = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const prop of (path.node.properties: Array)) {
|
|
||||||
if (t.isSpreadElement(prop)) {
|
|
||||||
push();
|
|
||||||
args.push(prop.argument);
|
|
||||||
} else {
|
|
||||||
props.push(prop);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.length) {
|
|
||||||
push();
|
|
||||||
}
|
|
||||||
|
|
||||||
let helper;
|
let helper;
|
||||||
if (loose) {
|
if (loose) {
|
||||||
helper = getExtendsHelper(file);
|
helper = getExtendsHelper(file);
|
||||||
@ -582,53 +565,40 @@ export default declare((api, opts) => {
|
|||||||
helper = file.addHelper("objectSpread");
|
helper = file.addHelper("objectSpread");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// We cannot call _objectSpread with more than two elements directly, since any element could cause side effects. For
|
|
||||||
// example:
|
let exp = null;
|
||||||
// var k = { a: 1, b: 2 };
|
let props = [];
|
||||||
// var o = { a: 3, ...k, b: k.a++ };
|
|
||||||
// // expected: { a: 1, b: 1 }
|
function make() {
|
||||||
// If we translate the above to `_objectSpread({ a: 3 }, k, { b: k.a++ })`, the `k.a++` will evaluate before
|
const hadProps = props.length > 0;
|
||||||
// `k` is spread and we end up with `{ a: 2, b: 1 }`.
|
const obj = t.objectExpression(props);
|
||||||
// adapted from https://github.com/microsoft/TypeScript/blob/eb105efdcd6db8a73f5b983bf329cb7a5eee55e1/src/compiler/transformers/es2018.ts#L272
|
props = [];
|
||||||
const chunks = [];
|
|
||||||
let currentChunk = [];
|
if (!exp) {
|
||||||
for (let i = 0; i < args.length; i++) {
|
exp = t.callExpression(helper, [obj]);
|
||||||
currentChunk.push(args[i]);
|
return;
|
||||||
const isCurrentChunkEmptyObject =
|
|
||||||
currentChunk.length === 1 &&
|
|
||||||
t.isObjectExpression(args[i]) &&
|
|
||||||
args[i].properties.length === 0;
|
|
||||||
const isNextArgEffectful =
|
|
||||||
i < args.length - 1 && !scope.isPure(args[i + 1]);
|
|
||||||
// prevent current chunk from pollution unless current chunk is an empty object
|
|
||||||
if (!isCurrentChunkEmptyObject && isNextArgEffectful) {
|
|
||||||
chunks.push(currentChunk);
|
|
||||||
currentChunk = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exp = t.callExpression(t.cloneNode(helper), [
|
||||||
|
exp,
|
||||||
|
// If we have static props, we need to insert an empty object
|
||||||
|
// becuase the odd arguments are copied with [[Get]], not
|
||||||
|
// [[GetOwnProperty]]
|
||||||
|
...(hadProps ? [t.objectExpression([]), obj] : []),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentChunk.length) {
|
for (const prop of (path.node.properties: Array)) {
|
||||||
chunks.push(currentChunk);
|
if (t.isSpreadElement(prop)) {
|
||||||
currentChunk = [];
|
make();
|
||||||
}
|
exp.arguments.push(prop.argument);
|
||||||
|
|
||||||
let exp = t.callExpression(helper, chunks[0]);
|
|
||||||
let nthArg = chunks[0].length;
|
|
||||||
for (let i = 1; i < chunks.length; i++) {
|
|
||||||
// reference: packages/babel-helpers/src/helpers.js#objectSpread2
|
|
||||||
if (nthArg % 2) {
|
|
||||||
exp = t.callExpression(helper, [exp, ...chunks[i]]);
|
|
||||||
} else {
|
} else {
|
||||||
exp = t.callExpression(helper, [
|
props.push(prop);
|
||||||
exp,
|
|
||||||
t.objectExpression([]),
|
|
||||||
...chunks[i],
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nthArg += chunks[i].length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (props.length) make();
|
||||||
|
|
||||||
path.replaceWith(exp);
|
path.replaceWith(exp);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -11,11 +11,11 @@ var d;
|
|||||||
var x;
|
var x;
|
||||||
var y;
|
var y;
|
||||||
|
|
||||||
_objectSpread({
|
_objectSpread(_objectSpread(_objectSpread({
|
||||||
x
|
x
|
||||||
}, y, {
|
}, y), {}, {
|
||||||
a
|
a
|
||||||
}, b, {
|
}, b), {}, {
|
||||||
c
|
c
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -25,9 +25,9 @@ _objectSpread({}, {
|
|||||||
foo: 'bar'
|
foo: 'bar'
|
||||||
});
|
});
|
||||||
|
|
||||||
_objectSpread({}, {
|
_objectSpread(_objectSpread({}, {
|
||||||
foo: 'bar'
|
foo: 'bar'
|
||||||
}, {}, {
|
}), {
|
||||||
bar: 'baz'
|
bar: 'baz'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,8 @@
|
|||||||
var k = { a: 1, b: 2 };
|
var k = { a: 1, b: 2 };
|
||||||
var o = { a: 3, ...k, b: k.a++ };
|
var o = { a: 3, ...k, b: k.a++ };
|
||||||
|
|
||||||
expect(o).toEqual({a: 1, b: 1});
|
expect(o).toEqual({a: 1, b: 1});
|
||||||
|
|
||||||
|
var k = { a: 1, get b() { l = { z: 9 }; return 2; } };
|
||||||
|
var l = { c: 3 };
|
||||||
|
var o = { ...k, ...l };
|
||||||
|
expect(o).toEqual({ a: 1, b: 2, z: 9 });
|
||||||
|
|||||||
@ -25,15 +25,15 @@ function impureFunc() {
|
|||||||
console.log('hello');
|
console.log('hello');
|
||||||
}
|
}
|
||||||
|
|
||||||
var output = _objectSpread(_objectSpread(_objectSpread({}, pureA), {}, {
|
var output = _objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread({}, pureA), {}, {
|
||||||
get foo() {},
|
get foo() {},
|
||||||
|
|
||||||
get bar() {}
|
get bar() {}
|
||||||
|
|
||||||
}, pureB, {}, pureC, {}), impureFunc(), {}, pureD, {
|
}, pureB), pureC), impureFunc()), pureD), {}, {
|
||||||
pureD
|
pureD
|
||||||
});
|
});
|
||||||
|
|
||||||
var simpleOutput = _objectSpread({}, pureA, {
|
var simpleOutput = _objectSpread(_objectSpread({}, pureA), {}, {
|
||||||
test: '1'
|
test: '1'
|
||||||
}, pureB);
|
}, pureB);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user